From 6d2d07da24c43e996f4168a14116e29c30d506d5 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 25 Apr 2025 17:56:36 +0300 Subject: [PATCH 01/27] UI: Add init unit convertor and service --- ui-ngx/src/app/app.component.ts | 8 +- .../app/core/services/unit/converter-unit.ts | 444 ++++++++++++++++++ .../app/core/services/unit/definitions/all.ts | 39 ++ .../services/unit/definitions/temperature.ts | 77 +++ .../core/services/unit/definitions/time.ts | 76 +++ .../app/core/services/unit/unit.service.ts | 89 ++++ .../value-card-basic-config.component.html | 2 +- .../lib/cards/value-card-widget.component.ts | 17 +- .../home/pages/profile/profile.component.html | 10 + .../home/pages/profile/profile.component.ts | 20 +- ...convert-unit-settings-panel.component.html | 71 +++ ...convert-unit-settings-panel.component.scss | 49 ++ .../convert-unit-settings-panel.component.ts | 138 ++++++ .../components/unit-input.component.html | 21 +- .../shared/components/unit-input.component.ts | 198 +++++--- ui-ngx/src/app/shared/models/unit.models.ts | 159 ++++++- ui-ngx/src/app/shared/models/user.model.ts | 2 + ui-ngx/src/app/shared/shared.module.ts | 2 + .../assets/locale/locale.constant-en_US.json | 7 + 19 files changed, 1330 insertions(+), 99 deletions(-) create mode 100644 ui-ngx/src/app/core/services/unit/converter-unit.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/all.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/temperature.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/time.ts create mode 100644 ui-ngx/src/app/core/services/unit/unit.service.ts create mode 100644 ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html create mode 100644 ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss create mode 100644 ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 5e579a759a..6e04735706 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -33,6 +33,7 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; +import { UnitService } from '@core/services/unit/unit.service'; @Component({ selector: 'tb-root', @@ -46,7 +47,8 @@ export class AppComponent implements OnInit { private translate: TranslateService, private matIconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer, - private authService: AuthService) { + private authService: AuthService, + private unitService: UnitService) { console.log(`ThingsBoard Version: ${env.tbVersion}`); @@ -94,12 +96,14 @@ export class AppComponent implements OnInit { this.store.select(selectUserReady).pipe( filter((data) => data.isUserLoaded), tap((data) => { - let userLang = getCurrentAuthState(this.store).userDetails?.additionalInfo?.lang ?? null; + const userDetails = getCurrentAuthState(this.store).userDetails; + let userLang = userDetails?.additionalInfo?.lang ?? null; if (!userLang && !data.isAuthenticated) { const settings = this.storageService.getItem(SETTINGS_KEY); userLang = settings?.userLang ?? null; } this.notifyUserLang(userLang); + this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem) }), skip(1), ).subscribe((data) => { diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts new file mode 100644 index 0000000000..aa04521b55 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/converter-unit.ts @@ -0,0 +1,444 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbUnitConvertor, Unit, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures } from '@core/services/unit/definitions/all'; +import { TranslateService } from '@ngx-translate/core'; +import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; + +export interface Conversion< + TMeasures extends string, + TSystems extends string, + TUnits extends string, +> { + abbr: TUnits; + measure: TMeasures; + system: TSystems; + unit: Unit; +} + +// export interface BestResult { +// val: number; +// unit: TUnits; +// name: string; +// tags: string[]; +// } + +type Entries = [S, T[keyof T]]; + +export type UnitCache = Map< + string, + { + system: TSystems; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } +>; + +export class Converter< + TMeasures extends AllMeasures, + TSystems extends UnitSystem, + TUnits extends string, +> { + private measureData: Record>; + private unitCache: Map< + string, + { + system: TSystems; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } + >; + + constructor( + measures: Record>, + unitCache: UnitCache + ) { + this.measureData = measures; + this.unitCache = unitCache; + } + + convertor(from: TUnits | (string & {}), to: TUnits | (string & {})): TbUnitConvertor{ + const origin = this.getUnit(from); + if (origin === null) { + throw Error(`Unsupported unit ${from}`); + } + const destination = this.getUnit(to); + if (destination === null) { + throw Error(`Unsupported unit ${from}`); + } + if (origin.abbr === destination.abbr) { + return (value: number) => value; + } + if (destination.measure !== origin.measure) { + throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); + } + return (value: number): number => { + let result = value * origin.unit.to_anchor; + if (origin.unit.anchor_shift) { + result -= origin.unit.anchor_shift; + } + + if (origin.system !== destination.system) { + const measure = this.measureData[origin.measure]; + const anchors = measure.anchors; + if (!anchors) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + + const anchor = anchors[origin.system]; + if (!anchor) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + + const transform = anchor[destination.system]?.transform; + const ratio = anchor[destination.system]?.ratio; + + if (typeof transform === 'function') { + result = transform(result); + } else if (typeof ratio === 'number') { + result *= ratio; + } else { + throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); + } + } + + if (destination.unit.anchor_shift) { + result += destination.unit.anchor_shift; + } + return result / destination.unit.to_anchor; + }; + } + + convert(value: number, from: TUnits | (string & {}), to: TUnits | (string & {})): number { + const origin = this.getUnit(from); + if (origin === null) { + throw Error(`Unsupported unit ${from}`); + } + const destination = this.getUnit(to); + if (destination === null) { + throw Error(`Unsupported unit ${from}`); + } + if (origin.abbr === destination.abbr) { + return value; + } + if (destination.measure !== origin.measure) { + throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); + } + let result = value * origin.unit.to_anchor; + if (origin.unit.anchor_shift) { + result -= origin.unit.anchor_shift; + } + if (origin.system !== destination.system) { + const measure = this.measureData[origin.measure]; + const anchors = measure.anchors; + if (!anchors) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + const anchor = anchors[origin.system]; + if (!anchor) { + throw Error(`Unable to convert units. Anchors are missing for "${origin.measure}" and "${destination.measure}" measures.`); + } + const transform = anchor[destination.system]?.transform; + const ratio = anchor[destination.system]?.ratio; + if (typeof transform === 'function') { + result = transform(result); + } else if (typeof ratio === 'number') { + result *= ratio; + } else { + throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); + } + } + + if (destination.unit.anchor_shift) { + result += destination.unit.anchor_shift; + } + return result / destination.unit.to_anchor; + } + + // toBest(options?: { + // exclude?: (TUnits | (string & {}))[]; + // cutOffNumber?: number; + // system?: TSystems | (string & {}); + // }): BestResult | null { + // if (this.origin == null) + // throw new OperationOrderError('.toBest must be called after .from'); + // + // const isNegative = this.val < 0; + // + // let exclude: (TUnits | (string & {}))[] = []; + // let cutOffNumber = isNegative ? -1 : 1; + // let system: TSystems | (string & {}) = this.origin.system; + // + // if (typeof options === 'object') { + // exclude = options.exclude ?? []; + // cutOffNumber = options.cutOffNumber ?? cutOffNumber; + // system = options.system ?? this.origin.system; + // } + // + // let best: BestResult | null = null; + // /** + // Looks through every possibility for the 'best' available unit. + // i.e. Where the value has the fewest numbers before the decimal point, + // but is still higher than 1. + // */ + // for (const possibility of this.possibilities()) { + // const unit = this.describe(possibility); + // const isIncluded = exclude.indexOf(possibility) === -1; + // + // if (isIncluded && unit.system === system) { + // const result = this.to(possibility); + // if (isNegative ? result > cutOffNumber : result < cutOffNumber) { + // continue; + // } + // if ( + // best === null || + // (isNegative + // ? result <= cutOffNumber && result > best.val + // : result >= cutOffNumber && result < best.val) + // ) { + // best = { + // val: result, + // unit: possibility, + // name: unit.name, + // tags: unit.tags + // }; + // } + // } + // } + // + // if (best == null) { + // return { + // val: this.val, + // unit: this.origin.abbr, + // name: this.origin.unit.name, + // tags: this.origin.unit.tags + // }; + // } + // + // return best; + // } + + getUnit(abbr: TUnits | (string & {})): Conversion | null { + return this.unitCache.get(abbr) ?? null; + } + + describe(abbr: TUnits | (string & {})): UnitDescription { + const result = this.getUnit(abbr); + + if (result != null) { + return this.describeUnit(result); + } + return null; + } + + private describeUnit(unit: Conversion): UnitDescription { + return { + abbr: unit.abbr, + measure: unit.measure, + system: unit.system, + name: unit.unit.name, + tags: unit.unit.tags + }; + } + + list(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescription[] | never { + const list = []; + + if (isDefinedAndNotNull(measureName)) { + if (!this.isMeasure(measureName)) { + console.log(`Measure "${measureName}" not found.`); + return list; + } + const measure = this.measureData[measureName]; + if (isDefinedAndNotNull(unitSystem)) { + let currentUnitSystem = unitSystem; + let units = measure.systems[currentUnitSystem]; + if (isUndefinedOrNull(units)) { + if (currentUnitSystem === UnitSystem.IMPERIAL) { + currentUnitSystem = UnitSystem.METRIC; + units = measure.systems[currentUnitSystem]; + } + if (!units) { + console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); + return list; + } + } + for (const [abbr, unit] of Object.entries( + units + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: measureName as TMeasures, + system: currentUnitSystem as TSystems, + unit: unit as Unit, + }) + ); + } + } else { + for (const [systemName, units] of Object.entries( + (measure as TbMeasure).systems + )) { + for (const [abbr, unit] of Object.entries( + units as Partial> + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: measureName as TMeasures, + system: systemName as TSystems, + unit: unit as Unit, + }) + ); + } + } + } + } else { + for (const [name, measure] of Object.entries(this.measureData)) { + if (isDefinedAndNotNull(unitSystem)) { + let currentUnitSystem = unitSystem; + let units = (measure as TbMeasure).systems[currentUnitSystem]; + if (isUndefinedOrNull(units)) { + if (currentUnitSystem === UnitSystem.IMPERIAL) { + currentUnitSystem = UnitSystem.METRIC; + units = (measure as TbMeasure).systems[currentUnitSystem]; + } + if (!units) { + console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); + continue; + } + } + for (const [abbr, unit] of Object.entries( + units as Partial> + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: name as TMeasures, + system: currentUnitSystem as TSystems, + unit: unit as Unit, + }) + ); + } + } else { + for (const [systemName, units] of Object.entries( + (measure as TbMeasure).systems + )) { + for (const [abbr, unit] of Object.entries( + units as Partial> + )) { + list.push( + this.describeUnit({ + abbr: abbr as TUnits, + measure: name as TMeasures, + system: systemName as TSystems, + unit: unit as Unit, + }) + ); + } + } + } + } + } + + return list; + } + + private isMeasure(measureName: string): measureName is TMeasures { + return measureName in this.measureData; + } + + // possibilities(forMeasure?: TMeasures | (string & {})): TUnits[] { + // let possibilities: TUnits[] = []; + // let list_measures: TMeasures[] = []; + // + // if (typeof forMeasure == 'string' && this.isMeasure(forMeasure)) { + // list_measures.push(forMeasure); + // } else if (this.origin != null) { + // list_measures.push(this.origin.measure); + // } else { + // list_measures = Object.keys(this.measureData) as TMeasures[]; + // } + // + // for (const measure of list_measures) { + // const systems = this.measureData[measure].systems; + // + // for (const system of Object.values(systems)) { + // possibilities = [ + // ...possibilities, + // ...(Object.keys(system as Record) as TUnits[]), + // ]; + // } + // } + // + // return possibilities; + // } + + // measures(): TMeasures[] { + // return Object.keys(this.measureData) as TMeasures[]; + // } +} + +export function buildUnitCache< + TMeasures extends string, + TSystems extends UnitSystem, + TUnits extends string, +>(measures: Record>, + translate: TranslateService +) { + const unitCache: UnitCache = new Map(); + for (const [measureName, measure] of Object.entries(measures) as Entries< + typeof measures, + TMeasures + >[]) { + for (const [systemName, system] of Object.entries( + measure.systems + ) as Entries>, TSystems>[]) { + for (const [testAbbr, unit] of Object.entries(system) as Entries< + typeof system, + TUnits + >[]) { + unit.name = translate.instant(unit.name); + unitCache.set(testAbbr, { + measure: measureName, + system: systemName, + abbr: testAbbr, + unit, + }); + } + } + } + return unitCache; +} + +export function configureMeasurements< + TMeasures extends AllMeasures, + TSystems extends UnitSystem, + TUnits extends string, +>( + measures: Record>, + translate: TranslateService +): Converter { + if (typeof measures !== 'object') { + throw new TypeError('The measures argument needs to be an object'); + } + + const unitCache = buildUnitCache(measures, translate); + return new Converter(measures, unitCache); +} diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts new file mode 100644 index 0000000000..7ae88112d3 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import temperature, { + TemperatureUnits, +} from './temperature'; +import time, { TimeUnits } from './time'; +import { TbMeasure, UnitSystem } from '@shared/models/unit.models'; + +export type AllMeasuresUnits = + | TemperatureUnits + | TimeUnits; + +export type AllMeasures = + | 'temperature' + | 'time'; + +const allMeasures: Record< + AllMeasures, + TbMeasure +> = { + temperature, + time, +}; + +export default allMeasures; diff --git a/ui-ngx/src/app/core/services/unit/definitions/temperature.ts b/ui-ngx/src/app/core/services/unit/definitions/temperature.ts new file mode 100644 index 0000000000..23b5831dcb --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/temperature.ts @@ -0,0 +1,77 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, Unit, UnitSystem } from '@shared/models/unit.models'; + +export type TemperatureMetricUnits = '°C' | 'K'; +export type TemperatureImperialUnits = '°F' | '°R'; + +export type TemperatureUnits = + | TemperatureMetricUnits + | TemperatureImperialUnits; + +const METRIC: Record = { + '°C': { + name: 'unit.celsius', + tags: ['temperature','heat','cold','warmth','degrees','celsius','shipment condition','°C'], + to_anchor: 1, + }, + K: { + name: 'unit.kelvin', + tags: ['temperature','heat','cold','warmth','degrees','kelvin','K','color quality','white balance','color temperature'], + to_anchor: 1, + anchor_shift: 273.15, + }, +}; + +const IMPERIAL: Record = { + '°F': { + name: 'unit.fahrenheit', + tags: ['temperature','heat','cold','warmth','degrees','fahrenheit','°F'], + to_anchor: 1, + }, + '°R': { + name: 'unit.rankine', + tags: ['temperature','heat','cold','warmth','Rankine','°R'], + to_anchor: 1, + anchor_shift: 459.67, + }, +}; + +const measure: TbMeasure = { + systems: { + METRIC, + IMPERIAL, + }, + anchors: { + METRIC: { + IMPERIAL: { + transform: function (C: number): number { + return C / (5 / 9) + 32; + }, + }, + }, + IMPERIAL: { + METRIC: { + transform: function (F: number): number { + return (F - 32) * (5 / 9); + }, + }, + }, + }, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/time.ts b/ui-ngx/src/app/core/services/unit/definitions/time.ts new file mode 100644 index 0000000000..aead2db33c --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/time.ts @@ -0,0 +1,76 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, Unit, UnitSystem } from '@shared/models/unit.models'; + +export type TimeUnits = TimeSIUnits; + +export type TimeSIUnits = + | 's' + | 'min' + | 'h' + | 'd' + | 'wk' + | 'mo' + | 'yr'; + +const daysInYear = 365.25; + +const METRIC: Record = { + s: { + name: 'unit.second', + tags: ["time","duration","interval","angle","second","arcsecond","sec"], + to_anchor: 1, + }, + min: { + name: 'unit.minute', + tags: ["time","duration","interval","angle","minute","arcminute","min"], + to_anchor: 60, + }, + h: { + name: 'unit.hour', + tags: ["time","duration","interval","h"], + to_anchor: 60 * 60, + }, + d: { + name: 'unit.day', + tags: ["time","duration","interval","d"], + to_anchor: 60 * 60 * 24, + }, + wk: { + name: 'unit.week', + tags: ["time","duration","interval","wk"], + to_anchor: 60 * 60 * 24 * 7, + }, + mo: { + name: 'unit.month', + tags: ["time","duration","interval","mo"], + to_anchor: (60 * 60 * 24 * daysInYear) / 12, + }, + yr: { + name: 'unit.year', + tags: ["time","duration","interval","yr"], + to_anchor: 60 * 60 * 24 * daysInYear, + }, +}; + +const measure: TbMeasure = { + systems: { + METRIC, + }, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/unit.service.ts b/ui-ngx/src/app/core/services/unit/unit.service.ts new file mode 100644 index 0000000000..4013e71ced --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/unit.service.ts @@ -0,0 +1,89 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Injectable } from '@angular/core'; +import moment from 'moment-timezone'; +import { TbUnitConvertor, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { isNotEmptyStr } from '@core/utils'; +import { configureMeasurements, Converter } from '@core/services/unit/converter-unit'; +import allMeasures, { AllMeasures, AllMeasuresUnits } from '@core/services/unit/definitions/all'; +import { TranslateService } from '@ngx-translate/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Injectable({ + providedIn: 'root' +}) +export class UnitService { + + private currentUnitSystem: UnitSystem = UnitSystem.METRIC; + private converter: Converter; + + constructor(private store: Store, + private translate: TranslateService) { + this.translate.onLangChange.pipe( + takeUntilDestroyed() + ).subscribe(() => { + this.converter = configureMeasurements(allMeasures, this.translate); + console.warn(this.converter?.list()); + console.warn(this.converter?.list('temperature')); + console.warn(this.converter?.list('temperature', UnitSystem.METRIC)); + console.warn(this.converter?.list(null, UnitSystem.IMPERIAL)); + }); + } + + getUnitSystem(): UnitSystem { + return this.currentUnitSystem; + } + + setUnitSystem(unitSystem: UnitSystem) { + if (isNotEmptyStr(unitSystem)) { + this.currentUnitSystem = unitSystem; + } else { + this.currentUnitSystem = this.getUnitSystemByTimezone(); + } + console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); + } + + getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescription[] { + return this.converter?.list(measure, unitSystem) ?? []; + } + + getUnitDescription(abbr: AllMeasuresUnits | string): UnitDescription { + return this.converter.describe(abbr); + } + + geUnitConvertor(from: string, to: string): TbUnitConvertor { + return this.converter.convertor(from, to); + } + + convertValue(value: number, from: string, to: string): number { + return this.converter.convert(value, from, to); + } + + private getUnitSystemByTimezone(): UnitSystem { + const timeZone = moment.tz.guess(true); + const imperialCountries = ['US', 'LR', 'MM']; + + if (moment.tz.zonesForCountry('GB').includes(timeZone)) { + return UnitSystem.HYBRID; + } + return imperialCountries.some(country => + moment.tz.zonesForCountry(country).includes(timeZone) + ) ? UnitSystem.IMPERIAL : UnitSystem.METRIC; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html index b8a67c744d..644a09fa5b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/value-card-basic-config.component.html @@ -84,7 +84,7 @@
widgets.value-card.value
- +
widget-config.decimals-suffix
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts index 68dc8fb232..7404f95cdf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts @@ -27,7 +27,7 @@ import { ViewChild } from '@angular/core'; import { WidgetContext } from '@home/models/widget-component.models'; -import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { isDefinedAndNotNull } from '@core/utils'; import { backgroundStyle, ColorProcessor, @@ -46,6 +46,7 @@ import { WidgetComponent } from '@home/components/widget/widget.component'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { FormatValueProcessor } from '@shared/models/unit.models'; const squareLayoutSize = 160; const horizontalLayoutHeight = 80; @@ -100,8 +101,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro private panelResize$: ResizeObserver; private horizontal = false; - private decimals = 0; - private units = ''; + private formatValue: FormatValueProcessor; constructor(private imagePipe: ImagePipe, private sanitizer: DomSanitizer, @@ -116,15 +116,16 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro this.ctx.$scope.valueCardWidget = this; this.settings = {...valueCardDefaultSettings(this.horizontal), ...this.ctx.settings}; - this.decimals = this.ctx.decimals; - this.units = this.ctx.units; + let decimals = this.ctx.decimals; + let units = this.ctx.units; const dataKey = getDataKey(this.ctx.datasources); if (isDefinedAndNotNull(dataKey?.decimals)) { - this.decimals = dataKey.decimals; + decimals = dataKey.decimals; } if (dataKey?.units) { - this.units = dataKey.units; + units = dataKey.units; } + this.formatValue = FormatValueProcessor.fromSettings(this.ctx.$injector, {units: units, dec: decimals}); this.layout = this.settings.layout; @@ -187,7 +188,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro if (tsValue && isDefinedAndNotNull(tsValue[1]) && tsValue[0] !== 0) { ts = tsValue[0]; value = tsValue[1]; - this.valueText = formatValue(value, this.decimals, this.units, false); + this.valueText = this.formatValue.format(value); // formatValue(value, this.decimals, this.units, false); } else { this.valueText = 'N/A'; } diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html index 839af59a5c..44eea5865e 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.html +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.html @@ -66,6 +66,16 @@
+ + unit.unit-system + + {{ 'unit.unit-system-type.AUTO' | translate }} + @for(unit of UnitSystems; track unit) { + {{ 'unit.unit-system-type.' + unit | translate }} + } + +
, @@ -50,7 +53,8 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir private userService: UserService, private authService: AuthService, private translate: TranslateService, - public fb: UntypedFormBuilder) { + private unitService: UnitService, + private fb: UntypedFormBuilder) { super(store); this.authUser = getCurrentAuthUser(this.store); } @@ -67,6 +71,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir lastName: [''], phone: [''], language: [''], + unitSystem: [''], homeDashboardId: [null], homeDashboardHideToolbar: [true] }); @@ -80,6 +85,11 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir this.user.additionalInfo.lang = this.profile.get('language').value; this.user.additionalInfo.homeDashboardId = this.profile.get('homeDashboardId').value; this.user.additionalInfo.homeDashboardHideToolbar = this.profile.get('homeDashboardHideToolbar').value; + if (isNotEmptyStr(this.profile.get('unitSystem').value)) { + this.user.additionalInfo.unitSystem = this.profile.get('unitSystem').value; + } else { + delete this.user.additionalInfo.unitSystem; + } this.userService.saveUser(this.user).subscribe( (user) => { this.userLoaded(user); @@ -96,6 +106,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir lastName: user.lastName, } })); this.store.dispatch(new ActionSettingsChangeLanguage({ userLang: user.additionalInfo.lang })); + this.unitService.setUnitSystem(this.user.additionalInfo.unitSystem); this.authService.refreshJwtToken(false); } ); @@ -107,6 +118,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir let lang; let homeDashboardId; let homeDashboardHideToolbar = true; + let unitSystem: UnitSystem = null; if (user.additionalInfo) { if (user.additionalInfo.lang) { lang = user.additionalInfo.lang; @@ -115,11 +127,15 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir if (isDefinedAndNotNull(user.additionalInfo.homeDashboardHideToolbar)) { homeDashboardHideToolbar = user.additionalInfo.homeDashboardHideToolbar; } + if (isNotEmptyStr(user.additionalInfo.unitSystem)) { + unitSystem = user.additionalInfo.unitSystem; + } } if (!lang) { lang = this.translate.currentLang; } this.profile.get('language').setValue(lang); + this.profile.get('unitSystem').setValue(unitSystem); this.profile.get('homeDashboardId').setValue(homeDashboardId); this.profile.get('homeDashboardHideToolbar').setValue(homeDashboardHideToolbar); } diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html new file mode 100644 index 0000000000..8ca777ffcf --- /dev/null +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -0,0 +1,71 @@ + +
+
Unit convertion settings
+
+
+
From
+ +
+
+ + Convert units + +
+ @if(convertUnitForm.get('convertUnit').value) { +
+
Metrical
+ + +
+
+
Imperial
+ + +
+
+
Hybrid
+ + +
+ } +
+
+ + +
+
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss new file mode 100644 index 0000000000..1613e4135e --- /dev/null +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss @@ -0,0 +1,49 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../scss/constants'; + +.tb-convert-settings-panel { + width: 320px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-convert-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-convert-settings-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + margin: -10px; + padding: 10px; + } + .tb-convert-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts new file mode 100644 index 0000000000..370cc30985 --- /dev/null +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -0,0 +1,138 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { TbUnit, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { FormBuilder, Validators } from '@angular/forms'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { UnitService } from '@core/services/unit/unit.service'; +import { AllMeasures } from '@core/services/unit/definitions/all'; +import { debounceTime } from 'rxjs/operators'; + +@Component({ + selector: 'tb-covert-unit-settings-panel', + templateUrl: './convert-unit-settings-panel.component.html', + styleUrls: ['./convert-unit-settings-panel.component.scss'], + providers: [], + encapsulation: ViewEncapsulation.None +}) +export class ConvertUnitSettingsPanelComponent implements OnInit { + + @Input() + unit: TbUnit; + + @Input() + required: boolean; + + @Output() + unitSettingsApplied = new EventEmitter(); + + UnitSystem = UnitSystem; + + measure: AllMeasures; + + convertUnitForm = this.fb.group({ + from: [''], + convertUnit: [true], + METRIC: [''], + IMPERIAL: [''], + HYBRID: [''] + }) + + constructor( + private popover: TbPopoverComponent, + private fb: FormBuilder, + private unitService: UnitService + ) { + this.convertUnitForm.get('from').valueChanges.pipe( + debounceTime(200), + takeUntilDestroyed() + ).subscribe(unit => { + const unitDescription = this.unitService.getUnitDescription(unit); + if (unitDescription) { + this.convertUnitForm.get('convertUnit').enable({emitEvent: true}); + this.measure = unitDescription.measure; + if (unitDescription.system === UnitSystem.IMPERIAL) { + this.convertUnitForm.get('IMPERIAL').setValue(unit, {emitEvent: false}); + this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + } else { + this.convertUnitForm.get('METRIC').setValue(unit, {emitEvent: false}); + this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + } + } else { + this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); + } + }) + + this.convertUnitForm.get('convertUnit').valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => { + if (value) { + this.convertUnitForm.get('METRIC').enable({emitEvent: false}); + this.convertUnitForm.get('IMPERIAL').enable({emitEvent: false}); + this.convertUnitForm.get('HYBRID').enable({emitEvent: false}); + } else { + this.convertUnitForm.get('METRIC').disable({emitEvent: false}); + this.convertUnitForm.get('IMPERIAL').disable({emitEvent: false}); + this.convertUnitForm.get('HYBRID').disable({emitEvent: false}); + } + setTimeout(() => { + this.popover.updatePosition(); + }, 0); + }); + } + + ngOnInit() { + let unitDescription: UnitDescription; + if (this.required) { + this.convertUnitForm.get('from').setValidators(Validators.required); + this.convertUnitForm.get('from').updateValueAndValidity({emitEvent: false}); + } + if (typeof this.unit === 'string') { + this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.convertUnitForm.get('from').setValue(this.unit, {emitEvent: true}); + unitDescription = this.unitService.getUnitDescription(this.unit); + } else if (this.unit === null) { + this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.convertUnitForm.get('from').setValue(null, {emitEvent: true}); + } else { + this.convertUnitForm.patchValue(this.unit, {emitEvent: false}); + unitDescription = this.unitService.getUnitDescription(this.unit.from); + } + + if (unitDescription?.measure) { + this.measure = unitDescription.measure; + } else { + this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); + } + } + + cancel() { + this.popover.hide(); + } + + applyUnitSettings() { + if (this.convertUnitForm.value.convertUnit) { + const formValue = this.convertUnitForm.value; + delete formValue.convertUnit; + this.unitSettingsApplied.emit(formValue as TbUnit); + } else { + this.unitSettingsApplied.emit(this.convertUnitForm.value.from); + } + } +} diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index 00286fd422..ab5932dcb9 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -15,11 +15,18 @@ limitations under the License. --> - + + + [matAutocomplete]="unitsAutocomplete" + [matAutocompleteDisabled]="allowConverted"> warning + + mdi:swap-vertical-circle-outline + @for (group of filteredUnits | async; track group[0]) { - @if ((fetchUnits$ | async).length > 1) { - - @for(unit of group[1]; track unit.abbr) { - - - - - } - - } @else { + @for(unit of group[1]; track unit.abbr) { } - } - } + + } diff --git a/ui-ngx/src/app/shared/components/unit-input.component.scss b/ui-ngx/src/app/shared/components/unit-input.component.scss index e5a4122ef8..7ca8ea86a2 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.scss +++ b/ui-ngx/src/app/shared/components/unit-input.component.scss @@ -15,6 +15,7 @@ */ .tb-autocomplete.tb-unit-input-autocomplete { .mat-mdc-optgroup-label { + min-height: 36px; .mdc-list-item__primary-text { font-size: 14px; font-weight: 500; diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 312040da2a..9cd044d3dd 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -37,7 +37,7 @@ import { AllMeasures } from '@core/services/unit/definitions/all'; import { UnitService } from '@core/services/unit/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component'; -import { isNotEmptyStr } from '@core/utils'; +import { isNotEmptyStr, isObject } from '@core/utils'; @Component({ selector: 'tb-unit-input', @@ -59,7 +59,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang unitsFormControl: FormControl; - @Input() + @Input({transform: booleanAttribute}) disabled: boolean; @Input({transform: booleanAttribute}) @@ -81,13 +81,13 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang searchText = ''; - isGroupOption = false; + isUnitMapping = false; private dirty = false; private modelValue: TbUnit | null; - fetchUnits$: Observable]>> = null; + private fetchUnits$: Observable]>> = null; private propagateChange = (_val: any) => {}; @@ -109,9 +109,6 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang }), mergeMap(symbol => this.fetchUnits(symbol)) ); - if (!!this.measure || !!this.tagFilter) { - this.isGroupOption = true; - } } ngOnChanges(changes: SimpleChanges) { @@ -121,11 +118,6 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang if (propName === 'measure' || propName === 'unitSystem') { this.fetchUnits$ = null; this.dirty = true; - if (!!this.measure || !!this.tagFilter) { - this.isGroupOption = true; - } else { - this.isGroupOption = false; - } } } } @@ -136,8 +128,10 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang this.modelValue = symbol; if (typeof symbol === 'string') { this.unitsFormControl.patchValue(this.unitService.getUnitDescription(symbol) ?? symbol, {emitEvent: false}); + this.isUnitMapping = false; } else { this.unitsFormControl.patchValue(symbol, {emitEvent: false}); + this.isUnitMapping = symbol !== null; } this.dirty = true; } @@ -172,18 +166,25 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - clear() { + clear($event: Event) { + $event.stopPropagation(); this.unitsFormControl.patchValue(null, {emitEvent: true}); - setTimeout(() => { - this.unitInput.nativeElement.blur(); - this.unitInput.nativeElement.focus(); - }, 0); + if (!this.allowConverted) { + setTimeout(() => { + this.unitInput.nativeElement.blur(); + this.unitInput.nativeElement.focus(); + }, 0); + } } openConvertSettingsPopup($event: Event) { + if (!this.allowConverted) { + return; + } if ($event) { $event.stopPropagation(); } + this.unitInput.nativeElement.blur(); const trigger = this.elementRef.nativeElement; if (this.popoverService.hasPopover(trigger)) { this.popoverService.hidePopover(trigger); @@ -196,7 +197,8 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang preferredPlacement: ['left', 'bottom', 'top'], context: { unit: this.getTbUnit(this.unitsFormControl.value), - required: this.required + required: this.required, + disabled: this.disabled, }, isModal: true }); @@ -212,6 +214,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang const res = this.getTbUnit(value); if (this.modelValue !== res) { this.modelValue = res; + this.isUnitMapping = (res !== null && isObject(res)); this.propagateChange(this.modelValue); } } diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 60b9e4f753..8a62b2d9d7 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -15,9 +15,6 @@ /// import { AllMeasures } from '@core/services/unit/definitions/all'; -import { Injector } from '@angular/core'; -import { isDefinedAndNotNull, isNotEmptyStr, isNumeric } from '@core/utils'; -import { UnitService } from '@core/services/unit/unit.service'; export enum UnitsType { capacity = 'capacity' @@ -74,104 +71,3 @@ export const searchUnits = (_units: Array, searchText: string): u.name.toUpperCase().includes(searchText) || searchUnitTags(u, searchText) ); - -export interface FormatValueSettingProcessor { - dec?: number; - units?: TbUnit; - showZeroDecimals?: boolean; -} - -export abstract class FormatValueProcessor { - - static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor { - if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) { - return new ConvertUnitProcessor($injector, settings) - } else { - return new SimpleUnitProcessor($injector, settings); - } - } - - protected constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { - } - - abstract format(value: any): string; -} - -export class SimpleUnitProcessor extends FormatValueProcessor { - - private readonly isDefinedUnit: boolean; - private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; - - constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { - super($injector, settings); - this.isDefinedUnit = isNotEmptyStr(settings.units); - this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; - } - - format(value: any): string { - if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) { - let formatted = value; - if (this.isDefinedDec) { - formatted = Number(formatted).toFixed(this.settings.dec); - } - if (!this.showZeroDecimals) { - formatted = Number(formatted) - } - formatted = formatted.toString(); - if (this.isDefinedUnit) { - formatted += ` ${this.settings.units}`; - } - return formatted; - } - return value ?? ''; - } -} - -export class ConvertUnitProcessor extends FormatValueProcessor { - - private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; - private readonly unitConvertor: TbUnitConvertor; - private readonly unitAbbr: string; - - constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { - super($injector, settings); - const unitService = this.$injector.get(UnitService); - const userUnitSystem = unitService.getUnitSystem(); - const unit = settings.units as TbUnitMapping; - const fromUnit = unit.from; - this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit; - try { - this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr); - } catch (e) {/**/} - - this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; - } - - format(value: any): string { - if (isDefinedAndNotNull(value) && isNumeric(value)) { - let formatted: number | string = Number(value); - if (this.unitConvertor) { - formatted = this.unitConvertor(value); - } - if (this.isDefinedDec) { - formatted = Number(formatted).toFixed(this.settings.dec); - } - if (!this.showZeroDecimals) { - formatted = Number(formatted) - } - formatted = formatted.toString(); - if (this.unitAbbr) { - formatted += ` ${this.unitAbbr}`; - } - return formatted; - } - return value ?? ''; - } -} diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index ed2e769c1e..89c2824545 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -14,7 +14,15 @@ /// limitations under the License. /// -import { isDefinedAndNotNull, isNumber, isNumeric, isUndefinedOrNull, mergeDeep, parseFunction } from '@core/utils'; +import { + isDefinedAndNotNull, + isNotEmptyStr, + isNumber, + isNumeric, + isUndefinedOrNull, + mergeDeep, + parseFunction +} from '@core/utils'; import { DataEntry, DataKey, @@ -45,6 +53,8 @@ import { WidgetSubscriptionCallbacks, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { UnitService } from '@core/services/unit/unit.service'; +import { TbUnit, TbUnitConvertor, TbUnitMapping } from '@shared/models/unit.models'; export type ComponentStyle = {[klass: string]: any}; @@ -852,6 +862,107 @@ export class AutoDateFormatProcessor extends DateFormatProcessor { } } +export interface FormatValueSettingProcessor { + dec?: number; + units?: TbUnit; + showZeroDecimals?: boolean; +} + +export abstract class FormatValueProcessor { + + static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor { + if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) { + return new ConvertUnitProcessor($injector, settings) + } else { + return new SimpleUnitProcessor($injector, settings); + } + } + + protected constructor(protected $injector: Injector, + protected settings: FormatValueSettingProcessor) { + } + + abstract format(value: any): string; +} + +export class SimpleUnitProcessor extends FormatValueProcessor { + + private readonly isDefinedUnit: boolean; + private readonly isDefinedDec: boolean; + private readonly showZeroDecimals: boolean; + + constructor(protected $injector: Injector, + protected settings: FormatValueSettingProcessor) { + super($injector, settings); + this.isDefinedUnit = isNotEmptyStr(settings.units); + this.isDefinedDec = isDefinedAndNotNull(settings.dec); + this.showZeroDecimals = !!settings.showZeroDecimals; + } + + format(value: any): string { + if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) { + let formatted = value; + if (this.isDefinedDec) { + formatted = Number(formatted).toFixed(this.settings.dec); + } + if (!this.showZeroDecimals) { + formatted = Number(formatted) + } + formatted = formatted.toString(); + if (this.isDefinedUnit) { + formatted += ` ${this.settings.units}`; + } + return formatted; + } + return value ?? ''; + } +} + +export class ConvertUnitProcessor extends FormatValueProcessor { + + private readonly isDefinedDec: boolean; + private readonly showZeroDecimals: boolean; + private readonly unitConvertor: TbUnitConvertor; + private readonly unitAbbr: string; + + constructor(protected $injector: Injector, + protected settings: FormatValueSettingProcessor) { + super($injector, settings); + const unitService = this.$injector.get(UnitService); + const userUnitSystem = unitService.getUnitSystem(); + const unit = settings.units as TbUnitMapping; + const fromUnit = unit.from; + this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit; + try { + this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr); + } catch (e) {/**/} + + this.isDefinedDec = isDefinedAndNotNull(settings.dec); + this.showZeroDecimals = !!settings.showZeroDecimals; + } + + format(value: any): string { + if (isDefinedAndNotNull(value) && isNumeric(value)) { + let formatted: number | string = Number(value); + if (this.unitConvertor) { + formatted = this.unitConvertor(value); + } + if (this.isDefinedDec) { + formatted = Number(formatted).toFixed(this.settings.dec); + } + if (!this.showZeroDecimals) { + formatted = Number(formatted) + } + formatted = formatted.toString(); + if (this.unitAbbr) { + formatted += ` ${this.unitAbbr}`; + } + return formatted; + } + return value ?? ''; + } +} + const intervalToFormatTimeUnit = (interval: Interval): FormatTimeUnit => { const intervalValue = IntervalMath.numberValue(interval); if (intervalValue < SECOND) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 78cc2d6e2e..68c160a9ca 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5847,6 +5847,16 @@ "background-blur": "Background blur" }, "unit": { + "convert": { + "set-units-conversion-settings": "Set units conversion settings", + "units-conversion-settings": "Units conversion settings", + "convert-from": "Convert from", + "to-metric": "To metric", + "to-imperial": "To imperial", + "to-hybrid": "To hybrid", + "convert-unit": "Convert unit", + "convert-unit-hint": "Work only with the default unit" + }, "unit-system": "Unit system", "unit-system-type": { "AUTO": "Auto", From 82ced843d3a35fae459ea2909aed462b76ed2e07 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 29 Apr 2025 16:07:26 +0300 Subject: [PATCH 06/27] UI: Refactoring convert unit --- .../app/core/services/unit/converter-unit.ts | 429 ++++-------------- ui-ngx/src/app/shared/models/unit.models.ts | 17 + 2 files changed, 116 insertions(+), 330 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts index 8a6d707ed1..a97951db15 100644 --- a/ui-ngx/src/app/core/services/unit/converter-unit.ts +++ b/ui-ngx/src/app/core/services/unit/converter-unit.ts @@ -15,46 +15,20 @@ /// import { + Conversion, TbMeasure, TbUnitConvertor, Unit, + UnitCache, UnitDescription, UnitDescriptionGroupByMeasure, UnitSystem } from '@shared/models/unit.models'; import { AllMeasures } from '@core/services/unit/definitions/all'; import { TranslateService } from '@ngx-translate/core'; -import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; - -export interface Conversion< - TMeasures extends string, - TUnits extends string, -> { - abbr: TUnits; - measure: TMeasures; - system: UnitSystem; - unit: Unit; -} - -// export interface BestResult { -// val: number; -// unit: TUnits; -// name: string; -// tags: string[]; -// } type Entries = [S, T[keyof T]]; -export type UnitCache = Map< - string, - { - system: UnitSystem; - measure: TMeasures; - unit: Unit; - abbr: TUnits; - } ->; - export class Converter< TMeasures extends AllMeasures, TUnits extends string, @@ -78,77 +52,40 @@ export class Converter< this.unitCache = unitCache; } - convertor(from: TUnits | (string & {}), to: TUnits | (string & {})): TbUnitConvertor{ - const origin = this.getUnit(from); - if (origin === null) { - throw Error(`Unsupported unit ${from}`); - } - const destination = this.getUnit(to); - if (destination === null) { - throw Error(`Unsupported unit ${from}`); - } - if (origin.abbr === destination.abbr) { - return (value: number) => value; - } - if (destination.measure !== origin.measure) { - throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); - } - return (value: number): number => { - let result = value * origin.unit.to_anchor; - if (origin.unit.anchor_shift) { - result -= origin.unit.anchor_shift; - } - - if (origin.system !== destination.system) { - const measure = this.measureData[origin.measure]; - const transform = measure[origin.system]?.transform; - const ratio = measure[origin.system]?.ratio; - - if (typeof transform === 'function') { - result = transform(result); - } else if (typeof ratio === 'number') { - result *= ratio; - } else { - throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); - } - } - - if (destination.unit.anchor_shift) { - result += destination.unit.anchor_shift; - } - return result / destination.unit.to_anchor; - }; + convertor(from: TUnits | string, to: TUnits | string): TbUnitConvertor { + return (value: number) => this.convert(value, from, to); } - convert(value: number, from: TUnits | (string & {}), to: TUnits | (string & {})): number { + convert(value: number, from: TUnits | string, to: TUnits | string): number { const origin = this.getUnit(from); - if (origin === null) { - throw Error(`Unsupported unit ${from}`); - } const destination = this.getUnit(to); - if (destination === null) { - throw Error(`Unsupported unit ${from}`); + + if (!origin) { + throw new Error(`Unsupported unit: ${from}`); + } + if (!destination) { + throw new Error(`Unsupported unit: ${to}`); } if (origin.abbr === destination.abbr) { return value; } if (destination.measure !== origin.measure) { - throw Error(`Cannot convert incompatible measures of ${destination.measure} and ${origin.measure}`); + throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`); } let result = value * origin.unit.to_anchor; if (origin.unit.anchor_shift) { result -= origin.unit.anchor_shift; } if (origin.system !== destination.system) { - const measure = this.measureData[origin.measure]; - const transform = measure[origin.system]?.transform; - const ratio = measure[origin.system]?.ratio; + const measureUnits = this.measureData[origin.measure][origin.system]; + const transform = measureUnits?.transform; + const ratio = measureUnits?.ratio; if (typeof transform === 'function') { result = transform(result); } else if (typeof ratio === 'number') { result *= ratio; } else { - throw Error('A system anchor needs to either have a defined ratio number or a transform function.'); + throw Error('System anchor requires a defined ratio or transform function'); } } @@ -162,26 +99,16 @@ export class Converter< if (!this.isMeasure(measureName)) { return null; } - const measure = this.measureData[measureName]; - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem].units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem].units; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return null; - } + const units = this.getUnitsForMeasure(measureName, unitSystem); + if (!units) { + return null; } - for (const [abbr, unit] of Object.entries( - units as Partial> - ) as [TUnits, Unit][]) { - if (unit.to_anchor === 1 && (isUndefinedOrNull(unit.anchor_shift) || unit.anchor_shift === 0)) { + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { return abbr; } } + return null; } getUnit(abbr: TUnits | (string & {})): Conversion | null { @@ -189,271 +116,113 @@ export class Converter< } describe(abbr: TUnits | (string & {})): UnitDescription { - const result = this.getUnit(abbr); - - if (result != null) { - return this.describeUnit(result); - } - return null; + const unit = this.getUnit(abbr); + return unit ? this.describeUnit(unit) : null; } - private describeUnit(unit: Conversion): UnitDescription { - return { - abbr: unit.abbr, - measure: unit.measure, - system: unit.system, - name: unit.unit.name, - tags: unit.unit.tags - }; - } + list(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescription[] { + const results: UnitDescription[] = []; - list(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescription[] | never { - const list = []; + const measures = measureName + ? { [measureName]: this.measureData[measureName] } as Record> + : this.measureData; - if (isDefinedAndNotNull(measureName)) { - if (!this.isMeasure(measureName)) { - console.log(`Measure "${measureName}" not found.`); - return list; + for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; } - const measure = this.measureData[measureName]; - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem]; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem]; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return list; - } + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; } - for (const [abbr, unit] of Object.entries( - units.units - )) { - list.push( + + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + results.push( this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, + abbr, + measure: name as TMeasures, + system, + unit, }) ); } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } - } else { - for (const [name, measure] of Object.entries(this.measureData)) { - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = (measure as TbMeasure)[currentUnitSystem]?.units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = (measure as TbMeasure)[currentUnitSystem]?.units; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - continue; - } - } - for (const [abbr, unit] of Object.entries( - units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, - }) - ); - } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - list.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } } } - - return list; + return results; } - listGroupByMeasure(measureName?: TMeasures | (string & {}), unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { - const list: UnitDescriptionGroupByMeasure = {}; + listGroupByMeasure(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { + const results: UnitDescriptionGroupByMeasure = {}; - if (isDefinedAndNotNull(measureName)) { - if (!this.isMeasure(measureName)) { - console.log(`Measure "${measureName}" not found.`); - return list; + const measures = measureName + ? { [measureName]: this.measureData[measureName]} as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; } - const measure = this.measureData[measureName]; - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = measure[currentUnitSystem]; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = measure[currentUnitSystem]; - } - if (!units) { - console.log(`Measure "${measureName}" in ${currentUnitSystem} system is not found.`); - return list; - } + + results[name] = []; + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; } - list[measureName] = []; - const unitsDescription = list[measureName]; - for (const [abbr, unit] of Object.entries( - units.units - )) { - unitsDescription.push( + + for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { + results[name].push( this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, + abbr, + measure: name as TMeasures, + system, + unit, }) ); } - } else { - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - list[measureName] = []; - const unitsDescription = list[measureName]; - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - unitsDescription.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: measureName as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } - } else { - for (const [name, measure] of Object.entries(this.measureData)) { - if (isDefinedAndNotNull(unitSystem)) { - let currentUnitSystem = unitSystem; - let units = (measure as TbMeasure)[currentUnitSystem]?.units; - if (isUndefinedOrNull(units)) { - if (currentUnitSystem === UnitSystem.IMPERIAL) { - currentUnitSystem = UnitSystem.METRIC; - units = (measure as TbMeasure)[currentUnitSystem]?.units; - } - if (!units) { - console.log(`Measure "${name}" in ${currentUnitSystem} system is not found.`); - continue; - } - } - list[name] = []; - const unitsDescription = list[name]; - for (const [abbr, unit] of Object.entries( - units as Partial> - )) { - unitsDescription.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: currentUnitSystem, - unit: unit as Unit, - }) - ); - } - } else { - list[name] = []; - const unitsDescription = list[name]; - for (const [systemName, units] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [abbr, unit] of Object.entries( - units.units as Partial> - )) { - unitsDescription.push( - this.describeUnit({ - abbr: abbr as TUnits, - measure: name as TMeasures, - system: systemName, - unit: unit as Unit, - }) - ); - } - } - } } } + return results; + } - return list; + private describeUnit(unit: Conversion): UnitDescription { + return { + abbr: unit.abbr, + measure: unit.measure, + system: unit.system, + name: unit.unit.name, + tags: unit.unit.tags + }; } private isMeasure(measureName: string): measureName is TMeasures { return measureName in this.measureData; } - // possibilities(forMeasure?: TMeasures | (string & {})): TUnits[] { - // let possibilities: TUnits[] = []; - // let list_measures: TMeasures[] = []; - // - // if (typeof forMeasure == 'string' && this.isMeasure(forMeasure)) { - // list_measures.push(forMeasure); - // } else if (this.origin != null) { - // list_measures.push(this.origin.measure); - // } else { - // list_measures = Object.keys(this.measureData) as TMeasures[]; - // } - // - // for (const measure of list_measures) { - // const systems = this.measureData[measure].systems; - // - // for (const system of Object.values(systems)) { - // possibilities = [ - // ...possibilities, - // ...(Object.keys(system as Record) as TUnits[]), - // ]; - // } - // } - // - // return possibilities; - // } - - // measures(): TMeasures[] { - // return Object.keys(this.measureData) as TMeasures[]; - // } + private getUnitsForMeasure( + measureName: TMeasures, + unitSystem: UnitSystem + ): Partial> | null { + const measure = this.measureData[measureName]; + let system = unitSystem; + let units = measure[system]?.units; + if (!units && unitSystem === UnitSystem.IMPERIAL) { + system = UnitSystem.METRIC; + units = measure[system]?.units; + } + return units ?? null; + } } export function buildUnitCache< diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 8a62b2d9d7..01f88c3406 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -63,6 +63,23 @@ export interface TbMeasureUnits { units?: Partial>; } +export interface Conversion { + abbr: TUnits; + measure: TMeasures; + system: UnitSystem; + unit: Unit; +} + +export type UnitCache = Map< + string, + { + system: UnitSystem; + measure: TMeasures; + unit: Unit; + abbr: TUnits; + } +>; + const searchUnitTags = (unit: UnitDescription, searchText: string): boolean => !!unit.tags.find(t => t.toUpperCase().includes(searchText)); From d0e56d860ce1ae1c81233d686a7ae5cb9fc9d5a7 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Tue, 29 Apr 2025 22:04:18 +0300 Subject: [PATCH 07/27] UI: Add unit definitions; add service map unit service --- ui-ngx/src/app/core/services/public-api.ts | 1 + .../app/core/services/unit/definitions/all.ts | 16 ++ .../core/services/unit/definitions/energy.ts | 145 ++++++++++++++++++ .../core/services/unit/definitions/force.ts | 81 ++++++++++ .../services/unit/definitions/frequency.ts | 76 +++++++++ .../services/unit/definitions/illuminance.ts | 61 ++++++++ .../app/modules/home/models/services.map.ts | 2 + .../assets/locale/locale.constant-en_US.json | 11 ++ 8 files changed, 393 insertions(+) create mode 100644 ui-ngx/src/app/core/services/unit/definitions/energy.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/force.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/frequency.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/illuminance.ts diff --git a/ui-ngx/src/app/core/services/public-api.ts b/ui-ngx/src/app/core/services/public-api.ts index 073eefd5e8..f4e770bdb5 100644 --- a/ui-ngx/src/app/core/services/public-api.ts +++ b/ui-ngx/src/app/core/services/public-api.ts @@ -15,6 +15,7 @@ /// export * from './script/node-script-test.service'; +export * from './unit/unit.service'; export * from './broadcast.models'; export * from './broadcast.service'; export * from './dashboard-utils.service'; diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts index 9be86f61e5..7b8d838e45 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -22,6 +22,10 @@ import area, { AreaUnits } from '@core/services/unit/definitions/area'; import charge, { ChargeUnits } from '@core/services/unit/definitions/charge'; import digital, { DigitalUnits } from '@core/services/unit/definitions/digital'; import electricCurrent, { ElectricCurrentUnits } from '@core/services/unit/definitions/electric-current'; +import energy, { EnergyUnits } from '@core/services/unit/definitions/energy'; +import force, { ForceUnits } from '@core/services/unit/definitions/force'; +import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequency'; +import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; import temperature, { TemperatureUnits } from './temperature'; import time, { TimeUnits } from './time'; @@ -33,6 +37,10 @@ export type AllMeasuresUnits = | ChargeUnits | DigitalUnits | ElectricCurrentUnits + | EnergyUnits + | ForceUnits + | FrequencyUnits + | IlluminanceUnits | TemperatureUnits | TimeUnits; @@ -44,6 +52,10 @@ export type AllMeasures = | 'charge' | 'digital' | 'electric-current' + | 'energy' + | 'force' + | 'frequency' + | 'illuminance' | 'temperature' | 'time'; @@ -58,6 +70,10 @@ const allMeasures: Record< charge, digital, 'electric-current': electricCurrent, + energy, + force, + frequency, + illuminance, temperature, time, }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/core/services/unit/definitions/energy.ts new file mode 100644 index 0000000000..1ec341f565 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/energy.ts @@ -0,0 +1,145 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type EnergyUnits = EnergyMetricUnits | EnergyImperialUnits; + +export type EnergyMetricUnits = + | 'Wm' + | 'Wh' + | 'mWh' + | 'kWh' + | 'MWh' + | 'GWh' + | 'μJ' + | 'mJ' + | 'J' + | 'kJ' + | 'MJ' + | 'GJ' + | 'eV'; + +export type EnergyImperialUnits = 'kcal' | 'cal' | 'Cal' | 'BTU' | 'ft·lb'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 4.184, + units: { + Wm: { + name: 'unit.watt-minute', + tags: ['energy', 'watt-minute', 'watt-minutes', 'Wm'], + to_anchor: 60, + }, + Wh: { + name: 'unit.watt-hour', + tags: ['energy', 'watt-hour', 'watt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + to_anchor: 3600, + }, + mWh: { + name: 'unit.milliwatt-hour', + tags: ['energy', 'milliwatt-hour', 'milliwatt-hours', 'mWh'], + to_anchor: 3.6, + }, + kWh: { + name: 'unit.kilowatt-hour', + tags: ['energy', 'kilowatt-hour', 'kilowatt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + to_anchor: 3600000, + }, + MWh: { + name: 'unit.megawatt-hour', + tags: ['energy', 'megawatt-hour', 'megawatt-hours', 'MWh'], + to_anchor: 3600000000, + }, + GWh: { + name: 'unit.gigawatt-hour', + tags: ['energy', 'gigawatt-hour', 'gigawatt-hours', 'GWh'], + to_anchor: 3600000000000, + }, + μJ: { + name: 'unit.microjoule', + tags: ['energy', 'microjoule', 'microjoules', 'μJ'], + to_anchor: 0.000001, + }, + mJ: { + name: 'unit.millijoule', + tags: ['energy', 'millijoule', 'millijoules', 'mJ'], + to_anchor: 0.001, + }, + J: { + name: 'unit.joule', + tags: ['joule', 'joules', 'energy', 'work done', 'heat', 'electricity', 'mechanical work'], + to_anchor: 1, + }, + kJ: { + name: 'unit.kilojoule', + tags: ['energy', 'kilojoule', 'kilojoules', 'kJ'], + to_anchor: 1000, + }, + MJ: { + name: 'unit.megajoule', + tags: ['energy', 'megajoule', 'megajoules', 'MJ'], + to_anchor: 1000000, + }, + GJ: { + name: 'unit.gigajoule', + tags: ['energy', 'gigajoule', 'gigajoules', 'GJ'], + to_anchor: 1000000000, + }, + eV: { + name: 'unit.electron-volts', + tags: ['energy', 'subatomic particles', 'radiation'], + to_anchor: 1.602176634e-19, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 4.184, + units: { + cal: { + name: 'unit.small-calorie', + tags: ['energy', 'small calorie', 'calories', 'cal'], + to_anchor: 1, + }, + Cal: { + name: 'unit.calorie', + tags: ['energy', 'food energy', 'Calorie', 'Calories', 'Cal'], + to_anchor: 1000, + }, + kcal: { + name: 'unit.kilocalorie', + tags: ['energy', 'small calorie', 'kilocalories', 'kcal'], + to_anchor: 1000, + }, + BTU: { + name: 'british-thermal-unit', + tags: ['energy', 'heat', 'work done', 'british thermal unit', 'british thermal units', 'BTU'], + to_anchor: 252.164401, + }, + 'ft·lb': { + name: 'unit.foot-pound', + tags: ['energy', 'foot-pound', 'foot-pounds', 'ft·lb', 'ft⋅lbf'], + to_anchor: 0.32404875717017, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/force.ts b/ui-ngx/src/app/core/services/unit/definitions/force.ts new file mode 100644 index 0000000000..46ee193829 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/force.ts @@ -0,0 +1,81 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ForceUnits = ForceMetricUnits | ForceImperialUnits; + +export type ForceMetricUnits = 'N' | 'kN' | 'dyn'; +export type ForceImperialUnits = 'lbf' | 'kgf' | 'klbf' | 'pdl' | 'kip'; + +const METRIC: TbMeasureUnits = { + ratio: 0.224809, + units: { + N: { + name: 'unit.newton', + tags: ['force', 'pressure', 'newton', 'newtons', 'N', 'push', 'pull', 'weight', 'gravity', 'N'], + to_anchor: 1, + }, + kN: { + name: 'unit.kilonewton', + tags: ['force', 'kN'], + to_anchor: 1000, + }, + dyn: { + name: 'unit.dyne', + tags: ['force', 'dyn'], + to_anchor: 0.00001, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 4.44822, + units: { + lbf: { + name: 'unit.pound-force', + tags: ['force', 'lbf'], + to_anchor: 1, + }, + kgf: { + name: 'unit.kilogram-force', + tags: ['force', 'kgf'], + to_anchor: 2.20462, + }, + klbf: { + name: 'unit.kilopound-force', + tags: ['force', 'klbf'], + to_anchor: 1000, + }, + pdl: { + name: 'unit.poundal', + tags: ['force', 'pdl'], + to_anchor: 0.031081, + }, + kip: { + name: 'unit.kip', + tags: ['force', 'kip'], + to_anchor: 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/frequency.ts b/ui-ngx/src/app/core/services/unit/definitions/frequency.ts new file mode 100644 index 0000000000..ecff651b78 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/frequency.ts @@ -0,0 +1,76 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type FrequencyUnits = FrequencyMetricUnits; +export type FrequencyMetricUnits = 'mHz' | 'Hz' | 'kHz' | 'MHz' | 'GHz' | 'THz' | 'rpm' | 'deg/s' | 'rad/s'; + +const METRIC: TbMeasureUnits = { + units: { + mHz: { + name: 'unit.millihertz', + tags: ['frequency', 'cycles per second', 'millihertz', 'mHz'], + to_anchor: 1 / 1000, + }, + Hz: { + name: 'unit.hertz', + tags: ['frequency', 'cycles per second', 'hertz', 'Hz'], + to_anchor: 1, + }, + kHz: { + name: 'unit.kilohertz', + tags: ['frequency', 'cycles per second', 'kilohertz', 'kHz'], + to_anchor: 1000, + }, + MHz: { + name: 'unit.megahertz', + tags: ['frequency', 'cycles per second', 'megahertz', 'MHz'], + to_anchor: 1000 * 1000, + }, + GHz: { + name: 'unit.gigahertz', + tags: ['frequency', 'cycles per second', 'gigahertz', 'GHz'], + to_anchor: 1000 * 1000 * 1000, + }, + THz: { + name: 'unit.terahertz', + tags: ['frequency', 'terahertz', 'THz'], + to_anchor: 1000 * 1000 * 1000 * 1000, + }, + rpm: { + name: 'unit.rotation-per-minute', + tags: ['frequency', 'rotation per minute', 'rotations per minute', 'rpm', 'angular velocity'], + to_anchor: 1 / 60, + }, + 'deg/s': { + name: 'unit.deg-per-second', + tags: ['angular velocity', 'degrees per second', 'deg/s'], + to_anchor: 1 / 360, // 1 deg/s = 1/360 Hz + }, + 'rad/s': { + name: 'unit.radian-per-second', + tags: ['angular velocity', 'rotation speed', 'rad/s'], + to_anchor: 1 / (Math.PI * 2), + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts b/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts new file mode 100644 index 0000000000..6c1ae4d43d --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts @@ -0,0 +1,61 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type IlluminanceUnits = IlluminanceMetricUnits | IlluminanceImperialUnits; + +export type IlluminanceMetricUnits = 'lx' | 'cd/m²' | 'lm/m²'; +export type IlluminanceImperialUnits = 'fc'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 10.76391, + units: { + lx: { + name: 'unit.lux', + tags: ['illumination', 'light level on a surface', 'illuminance', 'Lux', 'lx'], + to_anchor: 1, + }, + 'cd/m²': { + name: 'unit.candela-per-square-meter', + tags: ['brightness', 'light level', 'Luminance', 'Candela per square meter', 'cd/m²'], + to_anchor: 1, + }, + 'lm/m²': { + name: 'unit.lumen-per-square-meter', + tags: ['illumination', 'light level', 'lumen per square meter', 'lm/m²'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 10.76391, + units: { + 'fc': { + name: 'unit.foot-candle', + tags: ['illuminance', 'light level', 'foot-candle', 'fc'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index e4bbc15936..481e070b90 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -51,6 +51,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service'; import { UiSettingsService } from '@core/http/ui-settings.service'; import { UsageInfoService } from '@core/http/usage-info.service'; import { EventService } from '@core/http/event.service'; +import { UnitService } from '@core/services/unit/unit.service'; import { AuditLogService } from '@core/http/audit-log.service'; export const ServicesMap = new Map>( @@ -91,6 +92,7 @@ export const ServicesMap = new Map>( ['usageInfoService', UsageInfoService], ['notificationService', NotificationService], ['eventService', EventService], + ['unitService', UnitService], ['auditLogService', AuditLogService] ] ); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 68c160a9ca..a40481ea51 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5872,6 +5872,10 @@ "charge": "Charge", "digital": "Digital", "electric-current": "Electric current", + "energy": "Energy", + "force": "Force", + "frequency": "Frequency", + "illuminance": "Illuminance", "temperature": "Temperature", "time": "Time" }, @@ -5984,7 +5988,11 @@ "megajoule": "Megajoule", "gigajoule": "Gigajoule", "watt-hour": "Watt-hour", + "watt-minute": "Watt-minute", "kilowatt-hour": "Kilowatt-hour", + "milliwatt-hour": "Milliwatt-hour", + "megawatt-hour": "Megawatt-hour", + "gigawatt-hour": "Gigawatt-hour", "electron-volts": "Electron volts", "joules-per-coulomb": "Joules per Coulomb", "british-thermal-unit": "British Thermal Units", @@ -6087,10 +6095,12 @@ "kilohm": "Kilohm", "megohm": "Megohm", "gigohm": "Gigohm", + "millihertz": "Millihertz", "hertz": "Hertz", "kilohertz": "Kilohertz", "megahertz": "Megahertz", "gigahertz": "Gigahertz", + "terahertz": "Terahertz", "rpm": "Revolutions Per Minute", "candela-per-square-meter": "Candela per square meter", "candela": "Candela", @@ -6290,6 +6300,7 @@ "radian-per-second-squared": "Radian per second squared", "revolutions-per-minute-per-second": "Angular acceleration", "deg-per-second": "deg/s", + "rotation-per-minute": "Rotation per minute", "degrees-brix": "Degrees Brix", "katal": "Katal", "katal-per-cubic-metre": "Katal per Cubic Metre" From 8c3f56da25e686a6c6d981c30c4cf1cad9a4c9ef Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Tue, 29 Apr 2025 23:02:13 +0300 Subject: [PATCH 08/27] UI: Add unit definitions --- .../app/core/services/unit/definitions/all.ts | 8 + .../core/services/unit/definitions/angle.ts | 6 +- .../core/services/unit/definitions/length.ts | 183 ++++++++++++++++++ .../core/services/unit/definitions/mass.ts | 121 ++++++++++++ .../assets/locale/locale.constant-en_US.json | 6 +- 5 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 ui-ngx/src/app/core/services/unit/definitions/length.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/mass.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts index 7b8d838e45..57ca762686 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -26,6 +26,8 @@ import energy, { EnergyUnits } from '@core/services/unit/definitions/energy'; import force, { ForceUnits } from '@core/services/unit/definitions/force'; import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequency'; import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; +import length, { LengthUnits } from '@core/services/unit/definitions/length'; +import mass, { MassUnits } from '@core/services/unit/definitions/mass'; import temperature, { TemperatureUnits } from './temperature'; import time, { TimeUnits } from './time'; @@ -41,6 +43,8 @@ export type AllMeasuresUnits = | ForceUnits | FrequencyUnits | IlluminanceUnits + | LengthUnits + | MassUnits | TemperatureUnits | TimeUnits; @@ -56,6 +60,8 @@ export type AllMeasures = | 'force' | 'frequency' | 'illuminance' + | 'length' + | 'mass' | 'temperature' | 'time'; @@ -74,6 +80,8 @@ const allMeasures: Record< force, frequency, illuminance, + length, + mass, temperature, time, }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/angle.ts b/ui-ngx/src/app/core/services/unit/definitions/angle.ts index 05a7593b03..5898034341 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/angle.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/angle.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type AngleUnits = AngleMetricUnits; -export type AngleMetricUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mil' | 'rev'; +export type AngleMetricUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mrad' | 'rev'; const METRIC: TbMeasureUnits = { units: { @@ -47,8 +47,8 @@ const METRIC: TbMeasureUnits = { tags: ['angle', 'arcsecond', 'arcseconds', 'arcsec'], to_anchor: 1 / 3600 }, - mil: { - name: 'unit.mil', + mrad: { + name: 'unit.milliradian', tags: ['angle', 'military angle', 'angular mil', 'mil'], to_anchor: 9 / (50 * Math.PI), }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/length.ts b/ui-ngx/src/app/core/services/unit/definitions/length.ts new file mode 100644 index 0000000000..4a13c73bb1 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/length.ts @@ -0,0 +1,183 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LengthUnits = LengthMetricUnits | LengthImperialUnits; + +export type LengthMetricUnits = 'nm' | 'μm' | 'mm' | 'cm' | 'dm' | 'm' | 'km' | 'angstrom'; +export type LengthImperialUnits = + | 'in' + | 'yd' + | 'ft-us' + | 'ft' + | 'fathom' + | 'mi' + | 'nmi' + | 'thou' + | 'barleycorn' + | 'hand' + | 'ch' + | 'fur' + | 'league' + | 'link' + | 'rod' + | 'cable' + | 'AU'; + +const METRIC: TbMeasureUnits = { + ratio: 3.28084, + units: { + nm: { + name: 'unit.nanometer', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nanoscale', 'atomic scale', 'molecular scale', 'nanometer', 'nanometers', 'nm'], + to_anchor: 1e-9, + }, + μm: { + name: 'unit.micrometer', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'microns', 'micrometer', 'micrometers', 'µm'], + to_anchor: 1e-6, + }, + mm: { + name: 'unit.millimeter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'millimeter', 'millimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'mm'], + to_anchor: 1e-3, + }, + cm: { + name: 'unit.centimeter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'centimeter', 'centimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'cm'], + to_anchor: 1e-2, + }, + dm: { + name: 'unit.decimeter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'decimeter', 'decimeters', 'dm'], + to_anchor: 1e-1, + }, + m: { + name: 'unit.meter', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'meter', 'meters', 'm'], + to_anchor: 1, + }, + km: { + name: 'unit.kilometer', + tags: ['distance', 'height', 'length', 'width', 'gap', 'depth', 'kilometer', 'kilometers', 'km'], + to_anchor: 1e3, + }, + angstrom: { + name: 'unit.angstrom', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'atomic scale', 'atomic distance', 'nanoscale', 'angstrom', 'angstroms', 'Å'], + to_anchor: 1e-10, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 3.28084, + units: { + in: { + name: 'unit.inch', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'inch', 'inches', 'in'], + to_anchor: 1 / 12, + }, + yd: { + name: 'unit.yard', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'yard', 'yards', 'yd'], + to_anchor: 3, + }, + 'ft-us': { + name: 'unit.foot-us', + tags: ['length', 'us survey foot', 'us survey feet', 'ft-us', 'surveying'], + to_anchor: 1.000002, + }, + ft: { + name: 'unit.foot', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'foot', 'feet', 'ft'], + to_anchor: 1, + }, + fathom: { + name: 'unit.fathom', + tags: ['depth', 'nautical measurement', 'fathom'], + to_anchor: 6, + }, + mi: { + name: 'unit.mile', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'mile', 'miles', 'mi'], + to_anchor: 5280, + }, + nmi: { + name: 'unit.nautical-mile', + tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nautical mile', 'nmi'], + to_anchor: 6076.12, + }, + thou: { + name: 'unit.thou', + tags: ['length', 'measurement', 'thou'], + to_anchor: 0.001 / 12, + }, + barleycorn: { + name: 'unit.barleycorn', + tags: ['length', 'shoe size', 'barleycorn'], + to_anchor: 1 / 36, + }, + hand: { + name: 'unit.hand', + tags: ['length', 'horse measurement', 'hand'], + to_anchor: 4 / 12, + }, + ch: { + name: 'unit.chain', + tags: ['length', 'land surveying', 'ch'], + to_anchor: 66, + }, + fur: { + name: 'unit.furlong', + tags: ['length', 'land surveying', 'fur'], + to_anchor: 660, + }, + league: { + name: 'unit.league', + tags: ['length', 'historical measurement', 'league'], + to_anchor: 3 * 5280, + }, + link: { + name: 'unit.link', + tags: ['length', 'land surveying', 'link'], + to_anchor: 0.66, + }, + rod: { + name: 'unit.rod', + tags: ['length', 'land surveying', 'rod'], + to_anchor: 16.5, + }, + cable: { + name: 'unit.cable', + tags: ['distance', 'nautical measurement', 'cable'], + to_anchor: 600, + }, + AU: { + name: 'unit.astronomical-unit', + tags: ['distance', 'celestial bodies', 'solar system', 'AU'], + to_anchor: 149597870700 * 3.28084, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/core/services/unit/definitions/mass.ts new file mode 100644 index 0000000000..41a9dde039 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/mass.ts @@ -0,0 +1,121 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MassUnits = MassMetricUnits | MassImperialUnits; + +export type MassMetricUnits = 'ng' | 'mcg' | 'mg' | 'g' | 'kg' | 't' | 'Da'; +export type MassImperialUnits = 'oz' | 'lb' | 'st' | 'short tons' | 'gr' | 'dr' | 'qr' | 'cwt' | 'slug'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 453.59237, + units: { + ng: { + name: 'unit.nanogram', + tags: ['mass', 'weight', 'heaviness', 'load', 'nanogram', 'nanograms', 'ng'], + to_anchor: 1e-9, + }, + mcg: { + name: 'unit.microgram', + tags: ['mass', 'weight', 'heaviness', 'load', 'μg', 'microgram'], + to_anchor: 1e-6, + }, + mg: { + name: 'unit.milligram', + tags: ['mass', 'weight', 'heaviness', 'load', 'milligram', 'miligrams', 'mg'], + to_anchor: 1e-3, + }, + g: { + name: 'unit.gram', + tags: ['mass', 'weight', 'heaviness', 'load', 'gram', 'grams', 'g'], + to_anchor: 1, + }, + kg: { + name: 'unit.kilogram', + tags: ['mass', 'weight', 'heaviness', 'load', 'kilogram', 'kilograms', 'kg'], + to_anchor: 1000, // 1 kg = 1000 g + }, + t: { + name: 'unit.tonne', + tags: ['mass', 'weight', 'heaviness', 'load', 'tonne', 'tons', 't'], + to_anchor: 1000000, + }, + Da: { + name: 'unit.dalton', + tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit', 'dalton', 'Da'], + to_anchor: 1.66053906660e-24, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 453.59237, + units: { + oz: { + name: 'unit.ounce', + tags: ['mass', 'weight', 'heaviness', 'load', 'ounce', 'ounces', 'oz'], + to_anchor: 1 / 16, + }, + lb: { + name: 'unit.pound', + tags: ['mass', 'weight', 'heaviness', 'load', 'pound', 'pounds', 'lb'], + to_anchor: 1, + }, + st: { + name: 'unit.stone', + tags: ['mass', 'weight', 'heaviness', 'load', 'stone', 'stones', 'st'], + to_anchor: 14, + }, + 'short tons': { + name: 'unit.short-tons', + tags: ['mass', 'weight', 'heaviness', 'load', 'short ton', 'short tons'], + to_anchor: 2000, + }, + gr: { + name: 'unit.grain', + tags: ['mass', 'measurement', 'grain', 'gr'], + to_anchor: 1 / 7000, + }, + dr: { + name: 'unit.drachm', + tags: ['mass', 'measurement', 'drachm', 'dr'], + to_anchor: 1 / 256, + }, + qr: { + name: 'unit.quarter', + tags: ['mass', 'measurement', 'quarter', 'qr'], + to_anchor: 28, + }, + cwt: { + name: 'unit.hundredweight-countt', + tags: ['mass', 'weight', 'heaviness', 'load', 'hundredweight count', 'cwt'], + to_anchor: 100, + }, + slug: { + name: 'unit.slug', + tags: ['mass', 'measurement', 'slug'], + to_anchor: 32.174, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index a40481ea51..b0d6454782 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5876,11 +5876,14 @@ "force": "Force", "frequency": "Frequency", "illuminance": "Illuminance", + "length": "Length", + "mass": "Mass", "temperature": "Temperature", "time": "Time" }, "millimeter": "Millimeter", "centimeter": "Centimeter", + "decimeter": "Decimeter", "angstrom": "Angstrom", "nanometer": "Nanometer", "micrometer": "Micrometer", @@ -5888,6 +5891,7 @@ "kilometer": "Kilometer", "inch": "Inch", "foot": "Foot", + "foot-us": "Foot (US survey)", "yard": "Yard", "mile": "Mile", "nautical-mile": "Nautical Mile", @@ -6229,7 +6233,7 @@ "gradian": "Gradian", "arcminute": "Arcminute", "arcsecond": "Arcsecond", - "mil": "Mil", + "milliradian": "Milliradian", "revolution": "Revolution", "siemens": "Siemens", "millisiemens": "Millisiemens", From 505dfb2e4d48724ae30ed5acefc85d51405eadcd Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Apr 2025 13:00:48 +0300 Subject: [PATCH 09/27] UI: Add new unit definition --- .../app/core/services/unit/definitions/all.ts | 36 +++- .../core/services/unit/definitions/angle.ts | 2 +- .../core/services/unit/definitions/energy.ts | 2 +- .../core/services/unit/definitions/mass.ts | 2 +- .../services/unit/definitions/parts-per.ts | 41 ++++ .../core/services/unit/definitions/power.ts | 96 ++++++++++ .../services/unit/definitions/pressure.ts | 175 ++++++++++++++++++ .../core/services/unit/definitions/speed.ts | 81 ++++++++ .../core/services/unit/definitions/torque.ts | 56 ++++++ .../core/services/unit/definitions/voltage.ts | 66 +++++++ .../unit/definitions/volume-flow-rate.ts | 114 ++++++++++++ .../core/services/unit/definitions/volume.ts | 168 +++++++++++++++++ .../assets/locale/locale.constant-en_US.json | 14 +- 13 files changed, 845 insertions(+), 8 deletions(-) create mode 100644 ui-ngx/src/app/core/services/unit/definitions/parts-per.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/power.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/pressure.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/speed.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/torque.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/voltage.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts create mode 100644 ui-ngx/src/app/core/services/unit/definitions/volume.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts index 57ca762686..7b447de453 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/all.ts @@ -28,8 +28,16 @@ import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequ import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; import length, { LengthUnits } from '@core/services/unit/definitions/length'; import mass, { MassUnits } from '@core/services/unit/definitions/mass'; +import partsPer, { PartsPerUnits } from '@core/services/unit/definitions/parts-per'; +import power, { PowerUnits } from '@core/services/unit/definitions/power'; +import pressure, { PressureUnits } from '@core/services/unit/definitions/pressure'; +import speed, { SpeedUnits } from '@core/services/unit/definitions/speed'; import temperature, { TemperatureUnits } from './temperature'; import time, { TimeUnits } from './time'; +import torque, { TorqueUnits } from '@core/services/unit/definitions/torque'; +import voltage, { VoltageUnits } from '@core/services/unit/definitions/voltage'; +import volume, { VolumeUnits } from '@core/services/unit/definitions/volume'; +import volumeFlowRate, { VolumeFlowRateUnits } from '@core/services/unit/definitions/volume-flow-rate'; export type AllMeasuresUnits = | AccelerationUnits @@ -45,8 +53,16 @@ export type AllMeasuresUnits = | IlluminanceUnits | LengthUnits | MassUnits + | PartsPerUnits + | PowerUnits + | PressureUnits + | SpeedUnits | TemperatureUnits - | TimeUnits; + | TimeUnits + | TorqueUnits + | VoltageUnits + | VolumeUnits + | VolumeFlowRateUnits; export type AllMeasures = | 'acceleration' @@ -62,8 +78,16 @@ export type AllMeasures = | 'illuminance' | 'length' | 'mass' + | 'parts-per' + | 'power' + | 'pressure' + | 'speed' | 'temperature' - | 'time'; + | 'time' + | 'torque' + | 'voltage' + | 'volume' + | 'volume-flow-rate'; const allMeasures: Record< AllMeasures, @@ -82,8 +106,16 @@ const allMeasures: Record< illuminance, length, mass, + 'parts-per': partsPer, + power, + pressure, + speed, temperature, time, + torque, + voltage, + volume, + 'volume-flow-rate': volumeFlowRate, }; export default allMeasures; diff --git a/ui-ngx/src/app/core/services/unit/definitions/angle.ts b/ui-ngx/src/app/core/services/unit/definitions/angle.ts index 5898034341..7082e5a986 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/angle.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/angle.ts @@ -53,7 +53,7 @@ const METRIC: TbMeasureUnits = { to_anchor: 9 / (50 * Math.PI), }, rev: { - name: 'revolution', + name: 'unit.revolution', tags: ['angle', 'revolution', 'full circle', 'complete turn', 'rev'], to_anchor: 360, }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/core/services/unit/definitions/energy.ts index 1ec341f565..06ee394121 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/energy.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/energy.ts @@ -125,7 +125,7 @@ const IMPERIAL: TbMeasureUnits = { to_anchor: 1000, }, BTU: { - name: 'british-thermal-unit', + name: 'unit.british-thermal-unit', tags: ['energy', 'heat', 'work done', 'british thermal unit', 'british thermal units', 'BTU'], to_anchor: 252.164401, }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/core/services/unit/definitions/mass.ts index 41a9dde039..2a52a4c960 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/mass.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/mass.ts @@ -101,7 +101,7 @@ const IMPERIAL: TbMeasureUnits = { to_anchor: 28, }, cwt: { - name: 'unit.hundredweight-countt', + name: 'unit.hundredweight-count', tags: ['mass', 'weight', 'heaviness', 'load', 'hundredweight count', 'cwt'], to_anchor: 100, }, diff --git a/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts b/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts new file mode 100644 index 0000000000..83f72447df --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts @@ -0,0 +1,41 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PartsPerUnits = PartsPerMetricUnits; +export type PartsPerMetricUnits = 'ppm' | 'ppb'; + +const METRIC: TbMeasureUnits = { + units: { + ppm: { + name: 'unit.ppm', + tags: ['carbon dioxide', 'co²', 'carbon monoxide', 'co', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'ppm'], + to_anchor: 1, + }, + ppb: { + name: 'unit.ppb', + tags: ['ozone', 'o³', 'nitrogen dioxide', 'no²', 'sulfur dioxide', 'so²', 'aqi', 'air quality', 'tvoc', 'ppb'], + to_anchor: 0.001, + } + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/power.ts b/ui-ngx/src/app/core/services/unit/definitions/power.ts new file mode 100644 index 0000000000..8b9c5eda81 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/power.ts @@ -0,0 +1,96 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PowerUnits = PowerMetricUnits | PowerImperialUnits; + +export type PowerMetricUnits = 'W' | 'μW' | 'mW' | 'kW' | 'MW' | 'GW' | 'PS'; +export type PowerImperialUnits = 'BTU/s' | 'ft-lb/s' | 'hp' | 'BTU/h'; + +const METRIC: TbMeasureUnits = { + ratio: 0.737562149, + units: { + W: { + name: 'unit.watt', + tags: ['power', 'horsepower', 'performance', 'watt', 'watts', 'electricity', 'W'], + to_anchor: 1, + }, + μW: { + name: 'unit.microwatt', + tags: ['power', 'horsepower', 'performance', 'microwatt', 'microwatts', 'electricity', 'μW'], + to_anchor: 0.000001, + }, + mW: { + name: 'unit.milliwatt', + tags: ['power', 'horsepower', 'performance', 'milliwatt', 'milliwatts', 'electricity', 'mW'], + to_anchor: 0.001, + }, + kW: { + name: 'unit.kilowatt', + tags: ['power', 'horsepower', 'performance', 'kilowatt', 'kilowatts', 'electricity', 'kW'], + to_anchor: 1000, + }, + MW: { + name: 'unit.megawatt', + tags: ['power', 'horsepower', 'performance', 'megawatt', 'megawatts', 'electricity', 'MW'], + to_anchor: 1000000, + }, + GW: { + name: 'unit.gigawatt', + tags: ['power', 'horsepower', 'performance', 'gigawatt', 'gigawatts', 'electricity', 'GW'], + to_anchor: 1000000000, + }, + PS: { + name: 'unit.metric-horsepower', + tags: ['power', 'performance', 'metric horsepower', 'PS'], + to_anchor: 735.49875, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.737562149, + units: { + 'BTU/s': { + name: 'unit.btu-per-second', + tags: ['power', 'heat transfer', 'thermal energy', 'british thermal unit per second', 'Btu/s'], + to_anchor: 778.16937, + }, + 'ft-lb/s': { + name: 'unit.foot-pound-per-second', + tags: ['power', 'foot-pound per second', 'foot-pounds per second', 'ft-lb/s', 'mechanical power'], + to_anchor: 1, + }, + hp: { + name: 'unit.horsepower', + tags: ['power', 'horsepower', 'performance', 'electricity', 'horsepowers', 'hp'], + to_anchor: 550, + }, + 'BTU/h': { + name: 'unit.btu-per-hour', + tags: ['power', 'heat transfer', 'thermal energy', 'HVAC', 'BTU/h'], + to_anchor: 0.216158, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/pressure.ts b/ui-ngx/src/app/core/services/unit/definitions/pressure.ts new file mode 100644 index 0000000000..ad5c9b0dbb --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/pressure.ts @@ -0,0 +1,175 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PressureUnits = PressureMetricUnits | PressureImperialUnits; + +export type PressureMetricUnits = + | 'Pa' + | 'kPa' + | 'MPa' + | 'GPa' + | 'hPa' + | 'mb' + | 'mbar' + | 'bar' + | 'kbar' + | 'Torr' + | 'mmHg' + | 'atm' + | 'Pa/m²' + | 'N/mm²' + | 'N/m²' + | 'kN/m²' + | 'kgf/m²' + | 'Pa/cm²'; + +export type PressureImperialUnits = 'psi' | 'ksi' | 'inHg' | 'psi/in²' | 'tonf/in²'; + +const METRIC: TbMeasureUnits = { + ratio: 0.00014503768078, + units: { + Pa: { + name: 'unit.pascal', + tags: ['pressure', 'force', 'compression', 'tension', 'pascal', 'pascals', 'Pa', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], + to_anchor: 0.001, // 1 Pa = 0.001 kPa + }, + kPa: { + name: 'unit.kilopascal', + tags: ['pressure', 'force', 'compression', 'tension', 'kilopascal', 'kilopascals', 'kPa'], + to_anchor: 1, + }, + MPa: { + name: 'unit.megapascal', + tags: ['pressure', 'force', 'compression', 'tension', 'megapascal', 'megapascals', 'MPa'], + to_anchor: 1000, + }, + GPa: { + name: 'unit.gigapascal', + tags: ['pressure', 'force', 'compression', 'tension', 'gigapascal', 'gigapascals', 'GPa'], + to_anchor: 1000000, + }, + hPa: { + name: 'unit.hectopascal', + tags: ['pressure', 'force', 'compression', 'tension', 'hectopascal', 'hectopascals', 'hPa', 'atmospheric pressure'], + to_anchor: 0.1, + }, + mbar: { + name: 'unit.millibar', + tags: ['pressure', 'force', 'compression', 'tension', 'millibar', 'millibars', 'mbar'], + to_anchor: 0.1, + }, + mb: { + name: 'unit.millibar', + tags: ['atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight', 'mb'], + to_anchor: 0.1, + }, + bar: { + name: 'unit.bar', + tags: ['pressure', 'force', 'compression', 'tension', 'bar', 'bars'], + to_anchor: 100, + }, + kbar: { + name: 'unit.kilobar', + tags: ['pressure', 'force', 'compression', 'tension', 'kilobar', 'kilobars', 'kbar'], + to_anchor: 100000, + }, + Torr: { + name: 'unit.torr', + tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'torr'], + to_anchor: 101325 / 760000, + }, + mmHg: { + name: 'unit.millimeters-of-mercury', + tags: ['pressure', 'force', 'compression', 'tension', 'millimeter of mercury', 'millimeters of mercury', 'mmHg', 'vacuum pressure'], + to_anchor: 0.133322, + }, + atm: { + name: 'unit.atmospheres', + tags: ['pressure', 'force', 'compression', 'tension', 'atmosphere', 'atmospheres', 'atmospheric pressure', 'atm'], + to_anchor: 101.325, + }, + 'Pa/m²': { + name: 'unit.pascal-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square meter', 'Pa/m²'], + to_anchor: 0.001, + }, + 'N/mm²': { + name: 'unit.newton-per-square-millimeter', + tags: ['pressure', 'stress', 'mechanical strength', 'newton per square millimeter', 'N/mm²'], + to_anchor: 1000, + }, + 'N/m²': { + name: 'unit.newton-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'newton per square meter', 'N/m²'], + to_anchor: 0.001, + }, + 'kN/m²': { + name: 'unit.kilonewton-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'kilonewton per square meter', 'kN/m²'], + to_anchor: 1, + }, + 'kgf/m²': { + name: 'unit.kilogram-force-per-square-meter', + tags: ['pressure', 'stress', 'mechanical strength', 'kilogram-force per square meter', 'kgf/m²'], + to_anchor: 0.00980665, + }, + 'Pa/cm²': { + name: 'unit.pascal-per-square-centimeter', + tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square centimeter', 'Pa/cm²'], + to_anchor: 0.1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.00014503768078, + units: { + psi: { + name: 'unit.pounds-per-square-inch', + tags: ['pressure', 'force', 'compression', 'tension', 'pounds per square inch', 'psi'], + to_anchor: 0.001, + }, + ksi: { + name: 'unit.kilopound-per-square-inch', + tags: ['pressure', 'force', 'compression', 'tension', 'kilopound per square inch', 'kilopounds per square inch', 'ksi'], + to_anchor: 1, + }, + inHg: { + name: 'unit.inch-of-mercury', + tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'inHg', 'atmospheric pressure', 'barometric pressure'], + to_anchor: 0.000491154, + }, + 'psi/in²': { + name: 'unit.pound-per-square-inch', + tags: ['pressure', 'stress', 'mechanical strength', 'pound per square inch', 'psi/in²'], + to_anchor: 0.001, + }, + 'tonf/in²': { + name: 'unit.ton-force-per-square-inch', + tags: ['pressure', 'stress', 'mechanical strength', 'ton-force per square inch', 'tonf/in²'], + to_anchor: 2, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/speed.ts b/ui-ngx/src/app/core/services/unit/definitions/speed.ts new file mode 100644 index 0000000000..095bb0c759 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/speed.ts @@ -0,0 +1,81 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpeedUnits = SpeedMetricUnits | SpeedImperialUnits; + +export type SpeedMetricUnits = 'm/s' | 'km/h' | 'mm/min'; +export type SpeedImperialUnits = 'mph' | 'kt' | 'ft/s' | 'ft/min' | 'in/h'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 1.609344, + units: { + 'm/s': { + name: 'unit.meter-per-second', + tags: ['speed', 'velocity', 'pace', 'meter per second', 'm/s', 'peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'wind speed', 'weather'], + to_anchor: 3.6, + }, + 'km/h': { + name: 'unit.kilometer-per-hour', + tags: ['speed', 'velocity', 'pace', 'kilometer per hour', 'km/h'], + to_anchor: 1, + }, + 'mm/min': { + name: 'unit.millimeters-per-minute', + tags: ['feed rate', 'cutting feed rate', 'millimeters per minute', 'mm/min'], + to_anchor: 0.06, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1.609344, + units: { + mph: { + name: 'unit.mile-per-hour', + tags: ['speed', 'velocity', 'pace', 'mile per hour', 'mph'], + to_anchor: 1, + }, + kt: { + name: 'unit.knot', + tags: ['speed', 'velocity', 'pace', 'knot', 'knots', 'kt'], + to_anchor: 1.150779, + }, + 'ft/s': { + name: 'unit.foot-per-second', + tags: ['speed', 'velocity', 'pace', 'foot per second', 'ft/s'], + to_anchor: 0.681818, // 1 ft/s ≈ 0.681818 mph + }, + 'ft/min': { + name: 'unit.foot-per-minute', + tags: ['speed', 'velocity', 'pace', 'foot per minute', 'ft/min'], + to_anchor: 0.0113636, + }, + 'in/h': { + name: 'unit.inch-per-hour', + tags: ['speed', 'velocity', 'pace', 'inch per hour', 'in/h'], + to_anchor: 0.00001578, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/torque.ts b/ui-ngx/src/app/core/services/unit/definitions/torque.ts new file mode 100644 index 0000000000..fa830a0a4a --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/torque.ts @@ -0,0 +1,56 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type TorqueUnits = TorqueMetricUnits | TorqueImperialUnits; + +export type TorqueMetricUnits = 'Nm'; +export type TorqueImperialUnits = 'lbf-ft' | 'in·lbf'; + +const METRIC: TbMeasureUnits = { + ratio: 1 / 1.355818, + units: { + Nm: { + name: 'unit.newton-meter', + tags: ['torque', 'rotational force', 'newton meter', 'Nm'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1.355818, + units: { + 'lbf-ft': { + name: 'unit.foot-pounds', + tags: ['torque', 'rotational force', 'foot-pound', 'foot-pounds', 'ft·lbf'], + to_anchor: 1, + }, + 'in·lbf': { + name: 'unit.inch-pounds', + tags: ['torque', 'rotational force', 'inch-pounds', 'inch-pound', 'in·lbf'], + to_anchor: 1 / 12, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/voltage.ts b/ui-ngx/src/app/core/services/unit/definitions/voltage.ts new file mode 100644 index 0000000000..54f0ec2c20 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/voltage.ts @@ -0,0 +1,66 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VoltageUnits = VoltageMetricUnits; +export type VoltageMetricUnits = 'pV' | 'nV' | 'μV' | 'mV' | 'V' | 'kV' | 'MV'; + +const METRIC: TbMeasureUnits = { + units: { + pV: { + name: 'unit.picovolt', + tags: ['voltage', 'volts', 'picovolt', 'pV'], + to_anchor: 1e-12, + }, + nV: { + name: 'unit.nanovolt', + tags: ['voltage', 'volts', 'nanovolt', 'nV'], + to_anchor: 1e-9, + }, + μV: { + name: 'unit.microvolt', + tags: ['electric potential', 'electric tension', 'voltage', 'microvolt', 'microvolts', 'μV'], + to_anchor: 1e-6, + }, + mV: { + name: 'unit.millivolt', + tags: ['electric potential', 'electric tension', 'voltage', 'millivolt', 'millivolts', 'mV'], + to_anchor: 0.001, // 1 mV = 1e-3 V + }, + V: { + name: 'unit.volt', + tags: ['electric potential', 'electric tension', 'voltage', 'volt', 'volts', 'V', 'power source', 'battery', 'battery level'], + to_anchor: 1, + }, + kV: { + name: 'unit.kilovolt', + tags: ['electric potential', 'electric tension', 'voltage', 'kilovolt', 'kilovolts', 'kV'], + to_anchor: 1000, + }, + MV: { + name: 'unit.megavolt', + tags: ['electric potential', 'electric tension', 'voltage', 'megavolt', 'megavolts', 'MV'], + to_anchor: 1e6, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts b/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts new file mode 100644 index 0000000000..fd1f25c1f4 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts @@ -0,0 +1,114 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VolumeFlowRateUnits = VolumeFlowRateMetricUnits | VolumeFlowRateImperialUnits; + +export type VolumeFlowRateMetricUnits = + | 'dm³/s' + | 'mL/min' + | 'L/s' + | 'L/min' + | 'L/hr' + | 'm³/s' + | 'm³/hr'; + +export type VolumeFlowRateImperialUnits = + | 'fl-oz/s' + | 'ft³/s' + | 'ft³/min' + | 'gal/hr' + | 'GPM'; + +const METRIC: TbMeasureUnits = { + ratio: 33.8140227, + units: { + 'dm³/s': { + name: 'unit.cubic-decimeter-per-second', + tags: ['volume flow', 'cubic decimeter per second', 'dm3/s'], + to_anchor: 1, + }, + 'mL/min': { + name: 'unit.milliliters-per-minute', + tags: ['volume flow', 'flow rate', 'fluid dynamics', 'milliliters per minute', 'mL/min'], + to_anchor: 1 / 60000, + }, + 'L/s': { + name: 'unit.liter-per-second', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per second', 'L/s'], + to_anchor: 1, + }, + 'L/min': { + name: 'unit.liter-per-minute', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per minute', 'L/min'], + to_anchor: 1 / 60, + }, + 'L/hr': { + name: 'unit.liters-per-hour', + tags: ['volume flow', 'fuel consumption', 'liter per hour', 'L/hr'], + to_anchor: 1 / 3600, + }, + 'm³/s': { + name: 'unit.cubic-meters-per-second', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per second', 'm³/s'], + to_anchor: 1000, + }, + 'm³/hr': { + name: 'unit.cubic-meters-per-hour', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per hour', 'm³/hr'], + to_anchor: 5 / 18, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 33.8140227, + units: { + 'fl-oz/s': { + name: 'unit.fluid-ounce-per-second', + tags: ['volume flow', 'fluid ounce per second', 'fl-oz/s'], + to_anchor: 1, + }, + 'ft³/s': { + name: 'unit.cubic-foot-per-second', + tags: ['volume flow', 'flow rate', 'fluid flow', 'cubic foot per second', 'cubic feet per second', 'ft³/s'], + to_anchor: 957.506, + }, + 'ft³/min': { + name: 'unit.cubic-foot-per-minute', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'CFM', 'flow rate', 'fluid flow', 'cubic foot per minute', 'ft³/min'], + to_anchor: 957.506 / 60, + }, + 'gal/hr': { + name: 'unit.gallons-per-hour', + tags: ['volume flow', 'fuel consumption', 'gallons per hour', 'gal/hr'], + to_anchor: 128 / 3600, + }, + 'GPM': { + name: 'unit.gallons-per-minute', + tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'gallons per minute', 'GPM'], + to_anchor: 128 / 60, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume.ts b/ui-ngx/src/app/core/services/unit/definitions/volume.ts new file mode 100644 index 0000000000..92e1a2dfa7 --- /dev/null +++ b/ui-ngx/src/app/core/services/unit/definitions/volume.ts @@ -0,0 +1,168 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VolumeUnits = VolumeMetricUnits | VolumeImperialUnits; + +export type VolumeMetricUnits = + | 'mm³' + | 'cm³' + | 'µL' + | 'mL' + | 'L' + | 'hL' + | 'm³' + | 'km³'; + +export type VolumeImperialUnits = + | 'tsp' + | 'tbsp' + | 'in³' + | 'fl-oz' + | 'cup' + | 'pt' + | 'qt' + | 'gal' + | 'ft³' + | 'yd³' + | 'bbl' + | 'gi' + | 'hhd'; + +const METRIC: TbMeasureUnits = { + ratio: 33.8140226, + units: { + 'mm³': { + name: 'unit.cubic-millimeter', + tags: ['volume', 'capacity', 'extent', 'cubic millimeter', 'mm³'], + to_anchor: 1 / 1000000, + }, + 'cm³': { + name: 'unit.cubic-centimeter', + tags: ['volume', 'capacity', 'extent', 'cubic centimeter', 'cubic centimeters', 'cm³'], + to_anchor: 1 / 1000, + }, + µL: { + name: 'unit.microliter', + tags: ['volume', 'liquid measurement', 'microliter', 'µL'], + to_anchor: 0.000001, + }, + mL: { + name: 'unit.milliliter', + tags: ['volume', 'capacity', 'extent', 'milliliter', 'milliliters', 'mL'], + to_anchor: 1 / 1000, + }, + L: { + name: 'unit.liter', + tags: ['volume', 'capacity', 'extent', 'liter', 'liters', 'l'], + to_anchor: 1, + }, + hL: { + name: 'unit.hectoliter', + tags: ['volume', 'capacity', 'extent', 'hectoliter', 'hectoliters', 'hl'], + to_anchor: 100, + }, + 'm³': { + name: 'unit.cubic-meter', + tags: ['volume', 'capacity', 'extent', 'cubic meter', 'cubic meters', 'm³'], + to_anchor: 1000, + }, + 'km³': { + name: 'unit.cubic-kilometer', + tags: ['volume', 'capacity', 'extent', 'cubic kilometer', 'cubic kilometers', 'km³'], + to_anchor: 1000000000000, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 33.8140226, + units: { + tsp: { + name: 'unit.teaspoon', + tags: ['volume', 'cooking measurement', 'tsp'], + to_anchor: 1 / 6, + }, + tbsp: { + name: 'unit.tablespoon', + tags: ['volume', 'cooking measurement', 'tbsp'], + to_anchor: 1 / 2, + }, + 'in³': { + name: 'unit.cubic-inch', + tags: ['volume', 'capacity', 'extent', 'cubic inch', 'cubic inches', 'in³'], + to_anchor: 0.55411, + }, + 'fl-oz': { + name: 'unit.fluid-ounce', + tags: ['volume', 'capacity', 'extent', 'fluid ounce', 'fluid ounces', 'fl-oz'], + to_anchor: 1, + }, + cup: { + name: 'unit.cup', + tags: ['volume', 'cooking measurement', 'cup'], + to_anchor: 8, + }, + pt: { + name: 'unit.pint', + tags: ['volume', 'capacity', 'extent', 'pint', 'pints', 'pt'], + to_anchor: 16, + }, + qt: { + name: 'unit.quart', + tags: ['volume', 'capacity', 'extent', 'quart', 'quarts', 'qt'], + to_anchor: 32, + }, + gal: { + name: 'unit.gallon', + tags: ['volume', 'capacity', 'extent', 'gallon', 'gallons', 'gal'], + to_anchor: 128, + }, + 'ft³': { + name: 'unit.cubic-foot', + tags: ['volume', 'capacity', 'extent', 'cubic foot', 'cubic feet', 'ft³'], + to_anchor: 957.506, + }, + 'yd³': { + name: 'unit.cubic-yard', + tags: ['volume', 'capacity', 'extent', 'cubic yard', 'cubic yards', 'yd³'], + to_anchor: 25852.7, + }, + bbl: { + name: 'unit.oil-barrels', + tags: ['volume', 'capacity', 'extent', 'oil barrel', 'oil barrels', 'bbl'], + to_anchor: 5376, + }, + gi: { + name: 'unit.gill', + tags: ['volume', 'liquid measurement', 'gi'], + to_anchor: 4, + }, + hhd: { + name: 'unit.hogshead', + tags: ['volume', 'liquid measurement', 'hhd'], + to_anchor: 8064, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b0d6454782..cebaf71745 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5938,6 +5938,7 @@ "cubic-foot": "Cubic Foot", "cubic-yard": "Cubic Yard", "fluid-ounce": "Fluid Ounce", + "fluid-ounce-per-second": "Fluid Ounce per second", "pint": "Pint", "quart": "Quart", "gallon": "Gallon", @@ -5956,8 +5957,10 @@ "meter-per-second": "Meter per Second", "kilometer-per-hour": "Kilometer per Hour", "foot-per-second": "Foot per Second", + "foot-per-minute": "Foot per Minute", "mile-per-hour": "Mile per Hour", "knot": "Knot", + "inch-per-hour": "Inch per Hour", "millimeters-per-minute": "Millimeters per minute", "kilometer-per-hour-squared": "Kilometer per hour squared", "foot-per-second-squared": "Foot per second squared", @@ -5975,6 +5978,7 @@ "newton-per-meter": "Newton per meter", "atmospheres": "Atmospheres", "pounds-per-square-inch": "Pounds per Square Inch", + "kilopound-per-square-inch": "Kilopound per Square Inch", "torr": "Torr", "inches-of-mercury": "Inches of Mercury", "pascal-per-square-meter": "Pascal per Square Meter", @@ -6031,6 +6035,8 @@ "kilowatt-per-square-inch": "Kilowatts per square inch", "horsepower": "Horsepower", "btu-per-hour": "British thermal units/hour", + "btu-per-second": "British thermal units/second", + "foot-pound-per-second": "foot-pound per second", "coulomb": "Coulomb", "millicoulomb": "Millicoulombs", "microcoulomb": "Microcoulomb", @@ -6081,10 +6087,11 @@ "ampere-meter-tags": "magnetic field, current loop, ampere-meter, A·m", "nanovolt": "Nanovolt", "picovolt": "Picovolt", - "millivolts": "Millivolts", - "microvolts": "Microvolts", + "millivolt": "Millivolts", + "microvolt": "Microvolts", "volt": "Volt", - "kilovolts": "Kilovolts", + "kilovolt": "Kilovolt", + "megavolt": "Megavolt", "dbmV": "dBmV", "dbm": "dBm", "volt-meter": "Volt-Meter", @@ -6209,6 +6216,7 @@ "gallons-per-minute": "Gallons Per Minute", "cubic-foot-per-second": "Cubic foot per second", "milliliters-per-minute": "Milliliters per minute", + "cubic-decimeter-per-second": "Cubic decimeter per second", "bit": "Bit", "byte": "Byte", "kilobyte": "Kilobyte", From 76fd2a3abbbfadeb449fa3835377d046709797ee Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 30 Apr 2025 18:31:57 +0300 Subject: [PATCH 10/27] UI: Add new unit definition --- .../core/services/unit/definitions/charge.ts | 17 ++++++++++++++++- .../unit/definitions/electric-current.ts | 9 +++++++-- .../core/services/unit/definitions/energy.ts | 7 ++++++- .../app/core/services/unit/definitions/mass.ts | 9 +++++++-- .../assets/locale/locale.constant-en_US.json | 10 ---------- 5 files changed, 36 insertions(+), 16 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit/definitions/charge.ts b/ui-ngx/src/app/core/services/unit/definitions/charge.ts index 933e821a6a..b06a0223d5 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/charge.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/charge.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ChargeUnits = ChargeMetricUnits; -export type ChargeMetricUnits = 'c' | 'mC' | 'μC' | 'nC' | 'pC'; +export type ChargeMetricUnits = 'c' | 'mC' | 'μC' | 'nC' | 'pC' | 'mAh' | 'Ah' | 'kAh'; const METRIC: TbMeasureUnits = { units: { @@ -47,6 +47,21 @@ const METRIC: TbMeasureUnits = { tags: ['charge', 'electricity', 'electrostatics', 'picocoulomb', 'pC'], to_anchor: 1e-12, }, + mAh: { + name: 'unit.milliampere-hour', + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'milliampere-hour', 'milliampere-hours', 'mAh'], + to_anchor: 3.6, + }, + Ah: { + name: 'unit.ampere-hours', + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'ampere', 'ampere-hours', 'Ah'], + to_anchor: 3600, + }, + kAh: { + name: 'unit.kiloampere-hours', + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'kiloampere-hours', 'kiloampere-hour', 'kAh'], + to_anchor: 3600000, + }, } }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts b/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts index 72a21ccf7f..9025e71c15 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ElectricCurrentUnits = ElectricCurrentMetricalUnits; -export type ElectricCurrentMetricalUnits = 'A' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; +export type ElectricCurrentMetricalUnits = 'A' | 'pA' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; const METRIC: TbMeasureUnits = { units: { @@ -27,9 +27,14 @@ const METRIC: TbMeasureUnits = { tags: ['electric current', 'current flow', 'flow of electricity', 'electrical flow', 'ampere', 'amperes', 'amperage', 'A'], to_anchor: 1, }, + pA: { + name: 'unit.picoampere', + tags: ['current', 'amperes', 'picoampere', 'pA'], + to_anchor: 1e-12, + }, nA: { name: 'unit.nanoampere', - tags: ['electric current', 'nanoampere', 'nanoamperes', 'nA'], + tags: ['electric current', 'amperes', 'nanoampere', 'nA'], to_anchor: 1e-9, }, μA: { diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/core/services/unit/definitions/energy.ts index 06ee394121..2f74091c10 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/energy.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/energy.ts @@ -33,7 +33,7 @@ export type EnergyMetricUnits = | 'GJ' | 'eV'; -export type EnergyImperialUnits = 'kcal' | 'cal' | 'Cal' | 'BTU' | 'ft·lb'; +export type EnergyImperialUnits = 'kcal' | 'cal' | 'Cal' | 'BTU' | 'ft·lb' | 'thm'; const METRIC: TbMeasureUnits = { ratio: 1 / 4.184, @@ -134,6 +134,11 @@ const IMPERIAL: TbMeasureUnits = { tags: ['energy', 'foot-pound', 'foot-pounds', 'ft·lb', 'ft⋅lbf'], to_anchor: 0.32404875717017, }, + thm: { + name: 'unit.therm', + tags: ['energy', 'natural gas consumption', 'BTU', 'therm', 'thm'], + to_anchor: 25219021.687207, + }, }, }; diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/core/services/unit/definitions/mass.ts index 2a52a4c960..fbed798da3 100644 --- a/ui-ngx/src/app/core/services/unit/definitions/mass.ts +++ b/ui-ngx/src/app/core/services/unit/definitions/mass.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type MassUnits = MassMetricUnits | MassImperialUnits; -export type MassMetricUnits = 'ng' | 'mcg' | 'mg' | 'g' | 'kg' | 't' | 'Da'; +export type MassMetricUnits = 'ng' | 'mcg' | 'mg' | 'g' | 'kg' | 't' | 'Da' | 'ct'; export type MassImperialUnits = 'oz' | 'lb' | 'st' | 'short tons' | 'gr' | 'dr' | 'qr' | 'cwt' | 'slug'; const METRIC: TbMeasureUnits = { @@ -47,7 +47,7 @@ const METRIC: TbMeasureUnits = { kg: { name: 'unit.kilogram', tags: ['mass', 'weight', 'heaviness', 'load', 'kilogram', 'kilograms', 'kg'], - to_anchor: 1000, // 1 kg = 1000 g + to_anchor: 1000, }, t: { name: 'unit.tonne', @@ -59,6 +59,11 @@ const METRIC: TbMeasureUnits = { tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit', 'dalton', 'Da'], to_anchor: 1.66053906660e-24, }, + ct: { + name: 'unit.carat', + tags: ['gemstone', 'pearl', 'jewelry', 'carat', 'ct'], + to_anchor: 0.2, + }, }, }; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index cebaf71745..3001671891 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6059,11 +6059,8 @@ "barn": "Barn", "circular-inch": "Circular Inch", "milliampere-hour": "Milliampere-hour", - "milliampere-hour-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, milliampere-hour, milliampere-hours, mAh", "ampere-hours": "Ampere-hours", - "ampere-hours-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, ampere, ampere-hours, Ah", "kiloampere-hours": "Kiloampere-hours", - "kiloampere-hours-tags": "electric current, current flow, electric charge, current capacity, flow of electricity, electrical flow, kiloampere-hours, kiloampere-hour, kAh", "nanoampere": "Nanoampere", "microampere": "Microampere", "milliampere": "Milliampere", @@ -6072,19 +6069,12 @@ "megaampere": "Megaampere", "gigaampere": "Gigaampere", "microampere-per-square-centimeter": "Microampere per square centimeter", - "microampere-per-square-centimeter-tags": "Current density, microampere per square centimeter, µA/cm²", "ampere-per-square-meter": "Ampere per Square Meter", - "ampere-per-square-meter-tags": "current density, current per unit area, ampere per square meter, A/m²", "ampere-per-meter": "Ampere per Meter", - "ampere-per-meter-tags": "magnetic field strength, magnetic field intensity, ampere per meter, A/m", "oersted": "Oersted", - "oersted-tags": "magnetic field, oersted, Oe", "bohr-magneton": "Bohr Magneton", - "bohr-magneton-tags": "atomic physics, magnetic moment, bohr magneton, μB", "ampere-meter-squared": "Ampere-Meter Squared", - "ampere-meter-squared-tags": "magnetic moment, dipole moment, ampere-meter squared, A·m²", "ampere-meter": "Ampere-Meter", - "ampere-meter-tags": "magnetic field, current loop, ampere-meter, A·m", "nanovolt": "Nanovolt", "picovolt": "Picovolt", "millivolt": "Millivolts", From 1734a890e3ec640e465219dcd34a64dcf53df3d3 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 1 May 2025 16:05:35 +0300 Subject: [PATCH 11/27] UI: Refactoring units service --- ui-ngx/src/app/app.component.ts | 2 +- ui-ngx/src/app/core/services/public-api.ts | 2 +- .../core/services/{unit => }/unit.service.ts | 71 ++-- .../app/core/services/unit/converter-unit.ts | 272 -------------- .../app/core/services/unit/definitions/all.ts | 121 ------ .../lib/cards/value-card-widget.component.ts | 10 +- .../app/modules/home/models/services.map.ts | 2 +- .../home/pages/profile/profile.component.ts | 2 +- .../convert-unit-settings-panel.component.ts | 13 +- .../shared/components/unit-input.component.ts | 37 +- ui-ngx/src/app/shared/models/public-api.ts | 1 + ui-ngx/src/app/shared/models/unit.models.ts | 350 +++++++++++++++++- .../models/units}/acceleration.ts | 0 .../models/units}/angle.ts | 0 .../models/units}/angular-acceleration.ts | 0 .../models/units}/area.ts | 0 .../models/units}/charge.ts | 0 .../models/units}/digital.ts | 0 .../models/units}/electric-current.ts | 0 .../models/units}/energy.ts | 0 .../models/units}/force.ts | 0 .../models/units}/frequency.ts | 0 .../models/units}/illuminance.ts | 0 .../models/units}/length.ts | 0 .../models/units}/mass.ts | 0 .../models/units}/parts-per.ts | 0 .../models/units}/power.ts | 0 .../models/units}/pressure.ts | 0 .../models/units}/speed.ts | 0 .../models/units}/temperature.ts | 0 .../models/units}/time.ts | 0 .../models/units}/torque.ts | 0 .../models/units}/voltage.ts | 0 .../models/units}/volume-flow-rate.ts | 0 .../models/units}/volume.ts | 0 .../shared/models/widget-settings.models.ts | 56 ++- 36 files changed, 445 insertions(+), 494 deletions(-) rename ui-ngx/src/app/core/services/{unit => }/unit.service.ts (50%) delete mode 100644 ui-ngx/src/app/core/services/unit/converter-unit.ts delete mode 100644 ui-ngx/src/app/core/services/unit/definitions/all.ts rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/acceleration.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/angle.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/angular-acceleration.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/area.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/charge.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/digital.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/electric-current.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/energy.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/force.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/frequency.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/illuminance.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/length.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/mass.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/parts-per.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/power.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/pressure.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/speed.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/temperature.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/time.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/torque.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/voltage.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/volume-flow-rate.ts (100%) rename ui-ngx/src/app/{core/services/unit/definitions => shared/models/units}/volume.ts (100%) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 6e04735706..85bddd56cb 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -33,7 +33,7 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-root', diff --git a/ui-ngx/src/app/core/services/public-api.ts b/ui-ngx/src/app/core/services/public-api.ts index f4e770bdb5..c728565c8b 100644 --- a/ui-ngx/src/app/core/services/public-api.ts +++ b/ui-ngx/src/app/core/services/public-api.ts @@ -15,7 +15,6 @@ /// export * from './script/node-script-test.service'; -export * from './unit/unit.service'; export * from './broadcast.models'; export * from './broadcast.service'; export * from './dashboard-utils.service'; @@ -29,5 +28,6 @@ export * from './raf.service'; export * from './resources.service'; export * from './time.service'; export * from './title.service'; +export * from './unit.service'; export * from './utils.service'; export * from './window.service'; diff --git a/ui-ngx/src/app/core/services/unit/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts similarity index 50% rename from ui-ngx/src/app/core/services/unit/unit.service.ts rename to ui-ngx/src/app/core/services/unit.service.ts index 6945c89d3a..3d1fc4de0a 100644 --- a/ui-ngx/src/app/core/services/unit/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -17,18 +17,19 @@ import { Injectable } from '@angular/core'; import moment from 'moment-timezone'; import { - TbUnitConvertor, - UnitDescription, - UnitDescriptionGroupByMeasure, + AllMeasures, + AllMeasuresUnits, + Converter, + getUnitConverter, + TbUnitConverter, + TbUnitMapping, + UnitInfo, + UnitInfoGroupByMeasure, UnitSystem } from '@shared/models/unit.models'; -import { isNotEmptyStr } from '@core/utils'; -import { configureMeasurements, Converter } from '@core/services/unit/converter-unit'; -import allMeasures, { AllMeasures, AllMeasuresUnits } from '@core/services/unit/definitions/all'; +import { isNotEmptyStr, isObject } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; @Injectable({ providedIn: 'root' @@ -36,18 +37,17 @@ import { AppState } from '@core/core.state'; export class UnitService { private currentUnitSystem: UnitSystem = UnitSystem.METRIC; - private converter: Converter; + private converter: Converter; - constructor(private store: Store, - private translate: TranslateService) { + constructor(private translate: TranslateService) { this.translate.onLangChange.pipe( takeUntilDestroyed() ).subscribe(() => { - this.converter = configureMeasurements(allMeasures, this.translate); - console.warn(this.converter?.list()); - console.warn(this.converter?.list('temperature')); - console.warn(this.converter?.list('temperature', UnitSystem.METRIC)); - console.warn(this.converter?.list(null, UnitSystem.IMPERIAL)); + this.converter = getUnitConverter(this.translate); + console.warn(this.converter?.listUnits()); + console.warn(this.converter?.listUnits('temperature')); + console.warn(this.converter?.listUnits('temperature', UnitSystem.METRIC)); + console.warn(this.converter?.listUnits(null, UnitSystem.IMPERIAL)); }); } @@ -64,28 +64,47 @@ export class UnitService { console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); } - getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescription[] { - return this.converter?.list(measure, unitSystem); + getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { + return this.converter?.listUnits(measure, unitSystem); } - getUnitsGroupByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure { - return this.converter?.listGroupByMeasure(measure, unitSystem); + getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { + return this.converter?.unitsGroupByMeasure(measure, unitSystem); } - getUnitDescription(abbr: AllMeasuresUnits | string): UnitDescription { - return this.converter.describe(abbr); + getUnitInfo(symbol: AllMeasuresUnits | string): UnitInfo { + return this.converter.describe(symbol); } getDefaultUnit(measure: AllMeasures, unitSystem: UnitSystem): AllMeasuresUnits { return this.converter.getDefaultUnit(measure, unitSystem); } - geUnitConvertor(from: string, to: string): TbUnitConvertor { - return this.converter.convertor(from, to); + geUnitConverter(unit: TbUnitMapping): TbUnitConverter; + geUnitConverter(from: string, to: string): TbUnitConverter; + geUnitConverter(unit: TbUnitMapping | string, to?: string): TbUnitConverter { + if (unit !== null && typeof unit === 'object') { + const target = this.getTargetUnitSymbol(unit); + return this.converter.getUnitConverter(unit.from, target); + } + return this.converter.getUnitConverter(unit as string, to); } - convertValue(value: number, from: string, to: string): number { - return this.converter.convert(value, from, to); + getTargetUnitSymbol(unit: TbUnitMapping): string { + if (isObject(unit)) { + return isNotEmptyStr(unit[this.currentUnitSystem]) ? unit[this.currentUnitSystem] : unit.from; + } + return null; + } + + convertUnitValue(value: number, unit: TbUnitMapping): number; + convertUnitValue(value: number, from: string, to: string): number; + convertUnitValue(value: number, unit: string | TbUnitMapping, to?: string): number { + if (unit !== null && typeof unit === 'object') { + const target = this.getTargetUnitSymbol(unit); + return this.converter.convert(value, unit.from, target); + } + return this.converter.convert(value, unit as string, to); } private getUnitSystemByTimezone(): UnitSystem { diff --git a/ui-ngx/src/app/core/services/unit/converter-unit.ts b/ui-ngx/src/app/core/services/unit/converter-unit.ts deleted file mode 100644 index a97951db15..0000000000 --- a/ui-ngx/src/app/core/services/unit/converter-unit.ts +++ /dev/null @@ -1,272 +0,0 @@ -/// -/// Copyright © 2016-2025 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { - Conversion, - TbMeasure, - TbUnitConvertor, - Unit, - UnitCache, - UnitDescription, - UnitDescriptionGroupByMeasure, - UnitSystem -} from '@shared/models/unit.models'; -import { AllMeasures } from '@core/services/unit/definitions/all'; -import { TranslateService } from '@ngx-translate/core'; - -type Entries = [S, T[keyof T]]; - -export class Converter< - TMeasures extends AllMeasures, - TUnits extends string, -> { - private readonly measureData: Record>; - private unitCache: Map< - string, - { - system: UnitSystem; - measure: TMeasures; - unit: Unit; - abbr: TUnits; - } - >; - - constructor( - measures: Record>, - unitCache: UnitCache - ) { - this.measureData = measures; - this.unitCache = unitCache; - } - - convertor(from: TUnits | string, to: TUnits | string): TbUnitConvertor { - return (value: number) => this.convert(value, from, to); - } - - convert(value: number, from: TUnits | string, to: TUnits | string): number { - const origin = this.getUnit(from); - const destination = this.getUnit(to); - - if (!origin) { - throw new Error(`Unsupported unit: ${from}`); - } - if (!destination) { - throw new Error(`Unsupported unit: ${to}`); - } - if (origin.abbr === destination.abbr) { - return value; - } - if (destination.measure !== origin.measure) { - throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`); - } - let result = value * origin.unit.to_anchor; - if (origin.unit.anchor_shift) { - result -= origin.unit.anchor_shift; - } - if (origin.system !== destination.system) { - const measureUnits = this.measureData[origin.measure][origin.system]; - const transform = measureUnits?.transform; - const ratio = measureUnits?.ratio; - if (typeof transform === 'function') { - result = transform(result); - } else if (typeof ratio === 'number') { - result *= ratio; - } else { - throw Error('System anchor requires a defined ratio or transform function'); - } - } - - if (destination.unit.anchor_shift) { - result += destination.unit.anchor_shift; - } - return result / destination.unit.to_anchor; - } - - getDefaultUnit(measureName: TMeasures | (string & {}), unitSystem: UnitSystem): TUnits { - if (!this.isMeasure(measureName)) { - return null; - } - const units = this.getUnitsForMeasure(measureName, unitSystem); - if (!units) { - return null; - } - for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { - if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { - return abbr; - } - } - return null; - } - - getUnit(abbr: TUnits | (string & {})): Conversion | null { - return this.unitCache.get(abbr) ?? null; - } - - describe(abbr: TUnits | (string & {})): UnitDescription { - const unit = this.getUnit(abbr); - return unit ? this.describeUnit(unit) : null; - } - - list(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescription[] { - const results: UnitDescription[] = []; - - const measures = measureName - ? { [measureName]: this.measureData[measureName] } as Record> - : this.measureData; - - for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { - if (!this.isMeasure(name)) { - continue; - } - - const systems = unitSystem - ? [unitSystem] - : (Object.keys(measure) as UnitSystem[]); - - for (const system of systems) { - const units = this.getUnitsForMeasure(name, system); - if (!units) { - continue; - } - - for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { - results.push( - this.describeUnit({ - abbr, - measure: name as TMeasures, - system, - unit, - }) - ); - } - } - } - return results; - } - - listGroupByMeasure(measureName?: TMeasures, unitSystem?: UnitSystem): UnitDescriptionGroupByMeasure | never { - const results: UnitDescriptionGroupByMeasure = {}; - - const measures = measureName - ? { [measureName]: this.measureData[measureName]} as Record> - : this.measureData; - - for (const [name, measure] of Object.entries(measures) as [TMeasures, TbMeasure][]) { - if (!this.isMeasure(name)) { - continue; - } - - results[name] = []; - - const systems = unitSystem - ? [unitSystem] - : (Object.keys(measure) as UnitSystem[]); - - for (const system of systems) { - const units = this.getUnitsForMeasure(name, system); - if (!units) { - continue; - } - - for (const [abbr, unit] of Object.entries(units) as [TUnits, Unit][]) { - results[name].push( - this.describeUnit({ - abbr, - measure: name as TMeasures, - system, - unit, - }) - ); - } - } - } - return results; - } - - private describeUnit(unit: Conversion): UnitDescription { - return { - abbr: unit.abbr, - measure: unit.measure, - system: unit.system, - name: unit.unit.name, - tags: unit.unit.tags - }; - } - - private isMeasure(measureName: string): measureName is TMeasures { - return measureName in this.measureData; - } - - private getUnitsForMeasure( - measureName: TMeasures, - unitSystem: UnitSystem - ): Partial> | null { - const measure = this.measureData[measureName]; - let system = unitSystem; - let units = measure[system]?.units; - if (!units && unitSystem === UnitSystem.IMPERIAL) { - system = UnitSystem.METRIC; - units = measure[system]?.units; - } - return units ?? null; - } -} - -export function buildUnitCache< - TMeasures extends string, - TUnits extends string, ->(measures: Record>, - translate: TranslateService -) { - const unitCache: UnitCache = new Map(); - for (const [measureName, measure] of Object.entries(measures) as Entries< - typeof measures, - TMeasures - >[]) { - for (const [systemName, system] of Object.entries( - measure - ) as Entries, UnitSystem>[]) { - for (const [testAbbr, unit] of Object.entries(system.units) as Entries< - Record, - TUnits - >[]) { - unit.name = translate.instant(unit.name); - unitCache.set(testAbbr, { - measure: measureName, - system: systemName, - abbr: testAbbr, - unit, - }); - } - } - } - return unitCache; -} - -export function configureMeasurements< - TMeasures extends AllMeasures, - TUnits extends string, ->( - measures: Record>, - translate: TranslateService -): Converter { - if (typeof measures !== 'object') { - throw new TypeError('The measures argument needs to be an object'); - } - - const unitCache = buildUnitCache(measures, translate); - return new Converter(measures, unitCache); -} diff --git a/ui-ngx/src/app/core/services/unit/definitions/all.ts b/ui-ngx/src/app/core/services/unit/definitions/all.ts deleted file mode 100644 index 7b447de453..0000000000 --- a/ui-ngx/src/app/core/services/unit/definitions/all.ts +++ /dev/null @@ -1,121 +0,0 @@ -/// -/// Copyright © 2016-2025 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -import { TbMeasure } from '@shared/models/unit.models'; -import acceleration, { AccelerationUnits } from '@core/services/unit/definitions/acceleration'; -import angle, { AngleUnits } from '@core/services/unit/definitions/angle'; -import angularAcceleration, { AngularAccelerationUnits } from '@core/services/unit/definitions/angular-acceleration'; -import area, { AreaUnits } from '@core/services/unit/definitions/area'; -import charge, { ChargeUnits } from '@core/services/unit/definitions/charge'; -import digital, { DigitalUnits } from '@core/services/unit/definitions/digital'; -import electricCurrent, { ElectricCurrentUnits } from '@core/services/unit/definitions/electric-current'; -import energy, { EnergyUnits } from '@core/services/unit/definitions/energy'; -import force, { ForceUnits } from '@core/services/unit/definitions/force'; -import frequency, { FrequencyUnits } from '@core/services/unit/definitions/frequency'; -import illuminance,{ IlluminanceUnits } from '@core/services/unit/definitions/illuminance'; -import length, { LengthUnits } from '@core/services/unit/definitions/length'; -import mass, { MassUnits } from '@core/services/unit/definitions/mass'; -import partsPer, { PartsPerUnits } from '@core/services/unit/definitions/parts-per'; -import power, { PowerUnits } from '@core/services/unit/definitions/power'; -import pressure, { PressureUnits } from '@core/services/unit/definitions/pressure'; -import speed, { SpeedUnits } from '@core/services/unit/definitions/speed'; -import temperature, { TemperatureUnits } from './temperature'; -import time, { TimeUnits } from './time'; -import torque, { TorqueUnits } from '@core/services/unit/definitions/torque'; -import voltage, { VoltageUnits } from '@core/services/unit/definitions/voltage'; -import volume, { VolumeUnits } from '@core/services/unit/definitions/volume'; -import volumeFlowRate, { VolumeFlowRateUnits } from '@core/services/unit/definitions/volume-flow-rate'; - -export type AllMeasuresUnits = - | AccelerationUnits - | AngleUnits - | AngularAccelerationUnits - | AreaUnits - | ChargeUnits - | DigitalUnits - | ElectricCurrentUnits - | EnergyUnits - | ForceUnits - | FrequencyUnits - | IlluminanceUnits - | LengthUnits - | MassUnits - | PartsPerUnits - | PowerUnits - | PressureUnits - | SpeedUnits - | TemperatureUnits - | TimeUnits - | TorqueUnits - | VoltageUnits - | VolumeUnits - | VolumeFlowRateUnits; - -export type AllMeasures = - | 'acceleration' - | 'angle' - | 'angular-acceleration' - | 'area' - | 'charge' - | 'digital' - | 'electric-current' - | 'energy' - | 'force' - | 'frequency' - | 'illuminance' - | 'length' - | 'mass' - | 'parts-per' - | 'power' - | 'pressure' - | 'speed' - | 'temperature' - | 'time' - | 'torque' - | 'voltage' - | 'volume' - | 'volume-flow-rate'; - -const allMeasures: Record< - AllMeasures, - TbMeasure -> = { - acceleration, - angle, - 'angular-acceleration': angularAcceleration, - area, - charge, - digital, - 'electric-current': electricCurrent, - energy, - force, - frequency, - illuminance, - length, - mass, - 'parts-per': partsPer, - power, - pressure, - speed, - temperature, - time, - torque, - voltage, - volume, - 'volume-flow-rate': volumeFlowRate, -}; - -export default allMeasures; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts index 55abf8dad0..ff54db2fc3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts @@ -33,14 +33,14 @@ import { ColorProcessor, ComponentStyle, DateFormatProcessor, - FormatValueProcessor, getDataKey, getLabel, getSingleTsValue, iconStyle, overlayStyle, resolveCssSize, - textStyle + textStyle, + ValueFormatProcessor } from '@shared/models/widget-settings.models'; import { valueCardDefaultSettings, ValueCardLayout, ValueCardWidgetSettings } from './value-card-widget.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; @@ -101,7 +101,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro private panelResize$: ResizeObserver; private horizontal = false; - private formatValue: FormatValueProcessor; + private valueFormat: ValueFormatProcessor; constructor(private imagePipe: ImagePipe, private sanitizer: DomSanitizer, @@ -125,7 +125,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro if (dataKey?.units) { units = dataKey.units; } - this.formatValue = FormatValueProcessor.fromSettings(this.ctx.$injector, {units: units, dec: decimals}); + this.valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {units: units, dec: decimals}); this.layout = this.settings.layout; @@ -188,7 +188,7 @@ export class ValueCardWidgetComponent implements OnInit, AfterViewInit, OnDestro if (tsValue && isDefinedAndNotNull(tsValue[1]) && tsValue[0] !== 0) { ts = tsValue[0]; value = tsValue[1]; - this.valueText = this.formatValue.format(value); // formatValue(value, this.decimals, this.units, false); + this.valueText = this.valueFormat.update(value); // formatValue(value, this.decimals, this.units, false); } else { this.valueText = 'N/A'; } diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index 481e070b90..94c09bb3d3 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -51,7 +51,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service'; import { UiSettingsService } from '@core/http/ui-settings.service'; import { UsageInfoService } from '@core/http/usage-info.service'; import { EventService } from '@core/http/event.service'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; import { AuditLogService } from '@core/http/audit-log.service'; export const ServicesMap = new Map>( diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts index ebac40571e..7d1aa2aa4e 100644 --- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts +++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts @@ -32,7 +32,7 @@ import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { AuthService } from '@core/auth/auth.service'; import { UnitSystem, UnitSystems } from '@shared/models/unit.models'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-profile', diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts index e8afedfffa..624bd9ec33 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -15,12 +15,11 @@ /// import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { TbUnit, UnitDescription, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { UnitService } from '@core/services/unit/unit.service'; -import { AllMeasures } from '@core/services/unit/definitions/all'; +import { UnitService } from '@core/services/unit.service'; import { debounceTime, first } from 'rxjs/operators'; import { isEmptyStr } from '@core/utils'; import type { UnitInputComponent } from '@shared/components/unit-input.component'; @@ -69,7 +68,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { debounceTime(200), takeUntilDestroyed() ).subscribe(unit => { - const unitDescription = this.unitService.getUnitDescription(unit); + const unitDescription = this.unitService.getUnitInfo(unit); if (unitDescription) { this.convertUnitForm.get('convertUnit').enable({emitEvent: true}); this.measure = unitDescription.measure; @@ -109,7 +108,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { } ngOnInit() { - let unitDescription: UnitDescription; + let unitDescription: UnitInfo; if (this.required) { this.convertUnitForm.get('from').setValidators(Validators.required); this.convertUnitForm.get('from').updateValueAndValidity({emitEvent: false}); @@ -117,13 +116,13 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { if (typeof this.unit === 'string') { this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); this.convertUnitForm.get('from').setValue(this.unit, {emitEvent: true}); - unitDescription = this.unitService.getUnitDescription(this.unit); + unitDescription = this.unitService.getUnitInfo(this.unit); } else if (this.unit === null) { this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); this.convertUnitForm.get('from').setValue(null, {emitEvent: true}); } else { this.convertUnitForm.patchValue(this.unit, {emitEvent: false}); - unitDescription = this.unitService.getUnitDescription(this.unit.from); + unitDescription = this.unitService.getUnitInfo(this.unit.from); } if (unitDescription?.measure) { diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 9cd044d3dd..9b582d1266 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -31,10 +31,9 @@ import { } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, of, shareReplay } from 'rxjs'; -import { searchUnits, TbUnit, UnitDescription, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, searchUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; -import { AllMeasures } from '@core/services/unit/definitions/all'; -import { UnitService } from '@core/services/unit/unit.service'; +import { UnitService } from '@core/services/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component'; import { isNotEmptyStr, isObject } from '@core/utils'; @@ -57,7 +56,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang @HostBinding('style.display') readonly hostDisplay = 'flex'; @ViewChild('unitInput', {static: true}) unitInput: ElementRef; - unitsFormControl: FormControl; + unitsFormControl: FormControl; @Input({transform: booleanAttribute}) disabled: boolean; @@ -77,7 +76,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang @Input({transform: booleanAttribute}) allowConverted = false; - filteredUnits: Observable]>>; + filteredUnits: Observable]>>; searchText = ''; @@ -87,7 +86,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private modelValue: TbUnit | null; - private fetchUnits$: Observable]>> = null; + private fetchUnits$: Observable]>> = null; private propagateChange = (_val: any) => {}; @@ -100,7 +99,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } ngOnInit() { - this.unitsFormControl = this.fb.control('', this.required ? [Validators.required] : []); + this.unitsFormControl = this.fb.control('', this.required ? [Validators.required] : []); this.filteredUnits = this.unitsFormControl.valueChanges .pipe( map(value => { @@ -127,7 +126,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang this.searchText = ''; this.modelValue = symbol; if (typeof symbol === 'string') { - this.unitsFormControl.patchValue(this.unitService.getUnitDescription(symbol) ?? symbol, {emitEvent: false}); + this.unitsFormControl.patchValue(this.unitService.getUnitInfo(symbol) ?? symbol, {emitEvent: false}); this.isUnitMapping = false; } else { this.unitsFormControl.patchValue(symbol, {emitEvent: false}); @@ -143,7 +142,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - displayUnitFn(unit?: TbUnit | UnitDescription): string | undefined { + displayUnitFn(unit?: TbUnit | UnitInfo): string | undefined { if (unit) { return this.getUnitSymbol(unit); } @@ -210,7 +209,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - private updateView(value: UnitDescription | TbUnit ) { + private updateView(value: UnitInfo | TbUnit ) { const res = this.getTbUnit(value); if (this.modelValue !== res) { this.modelValue = res; @@ -219,22 +218,22 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } } - private fetchUnits(searchText?: string): Observable]>> { + private fetchUnits(searchText?: string): Observable]>> { this.searchText = searchText; return this.unitsConstant().pipe( map(unit => this.searchUnit(unit, searchText)) ); } - private unitsConstant(): Observable]>> { + private unitsConstant(): Observable]>> { if (this.fetchUnits$ === null) { - this.fetchUnits$ = of(this.unitService.getUnitsGroupByMeasure(this.measure, this.unitSystem)).pipe( + this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem)).pipe( map(data => { - let objectData = Object.entries(data) as Array<[AllMeasures, UnitDescription[]]>; + let objectData = Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>; if (this.tagFilter) { objectData = objectData - .map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitDescription[]]) + .map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitInfo[]]) .filter((measure) => measure[1].length > 0); } return objectData; @@ -245,17 +244,17 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang return this.fetchUnits$; } - private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { + private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { const filterValue = searchText.trim().toUpperCase() return units - .map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitDescription[]]) + .map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitInfo[]]) .filter((measure) => measure[1].length > 0); } return units; } - private getUnitSymbol(value: TbUnit | UnitDescription | null): string { + private getUnitSymbol(value: TbUnit | UnitInfo | null): string { if (value === null) { return ''; } @@ -268,7 +267,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang return value.from; } - private getTbUnit(value: TbUnit | UnitDescription | null): TbUnit { + private getTbUnit(value: TbUnit | UnitInfo | null): TbUnit { if (value === null) { return null; } diff --git a/ui-ngx/src/app/shared/models/public-api.ts b/ui-ngx/src/app/shared/models/public-api.ts index 53e4bb286e..fbee1ec6bb 100644 --- a/ui-ngx/src/app/shared/models/public-api.ts +++ b/ui-ngx/src/app/shared/models/public-api.ts @@ -54,6 +54,7 @@ export * from './rule-node.models'; export * from './settings.models'; export * from './tenant.model'; export * from './user.model'; +export * from './unit.models'; export * from './user-settings.models'; export * from './widget-settings.models'; export * from './widget.models'; diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 01f88c3406..4f73fc4fbc 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -14,16 +14,118 @@ /// limitations under the License. /// -import { AllMeasures } from '@core/services/unit/definitions/all'; +import acceleration, { AccelerationUnits } from '@shared/models/units/acceleration'; +import angle, { AngleUnits } from '@shared/models/units/angle'; +import angularAcceleration, { AngularAccelerationUnits } from '@shared/models/units/angular-acceleration'; +import area, { AreaUnits } from '@shared/models/units/area'; +import charge, { ChargeUnits } from '@shared/models/units/charge'; +import digital, { DigitalUnits } from '@shared/models/units/digital'; +import electricCurrent, { ElectricCurrentUnits } from '@shared/models/units/electric-current'; +import energy, { EnergyUnits } from '@shared/models/units/energy'; +import force, { ForceUnits } from '@shared/models/units/force'; +import frequency, { FrequencyUnits } from '@shared/models/units/frequency'; +import illuminance, { IlluminanceUnits } from '@shared/models/units/illuminance'; +import length, { LengthUnits } from '@shared/models/units/length'; +import mass, { MassUnits } from '@shared/models/units/mass'; +import partsPer, { PartsPerUnits } from '@shared/models/units/parts-per'; +import power, { PowerUnits } from '@shared/models/units/power'; +import pressure, { PressureUnits } from '@shared/models/units/pressure'; +import speed, { SpeedUnits } from '@shared/models/units/speed'; +import temperature, { TemperatureUnits } from '@shared/models/units/temperature'; +import time, { TimeUnits } from '@shared/models/units/time'; +import torque, { TorqueUnits } from '@shared/models/units/torque'; +import voltage, { VoltageUnits } from '@shared/models/units/voltage'; +import volume, { VolumeUnits } from '@shared/models/units/volume'; +import volumeFlowRate, { VolumeFlowRateUnits } from '@shared/models/units/volume-flow-rate'; +import { TranslateService } from '@ngx-translate/core'; + +export type AllMeasuresUnits = + | AccelerationUnits + | AngleUnits + | AngularAccelerationUnits + | AreaUnits + | ChargeUnits + | DigitalUnits + | ElectricCurrentUnits + | EnergyUnits + | ForceUnits + | FrequencyUnits + | IlluminanceUnits + | LengthUnits + | MassUnits + | PartsPerUnits + | PowerUnits + | PressureUnits + | SpeedUnits + | TemperatureUnits + | TimeUnits + | TorqueUnits + | VoltageUnits + | VolumeUnits + | VolumeFlowRateUnits; + +export type AllMeasures = + | 'acceleration' + | 'angle' + | 'angular-acceleration' + | 'area' + | 'charge' + | 'digital' + | 'electric-current' + | 'energy' + | 'force' + | 'frequency' + | 'illuminance' + | 'length' + | 'mass' + | 'parts-per' + | 'power' + | 'pressure' + | 'speed' + | 'temperature' + | 'time' + | 'torque' + | 'voltage' + | 'volume' + | 'volume-flow-rate'; + +const allMeasures: Record< + AllMeasures, + TbMeasure +> = Object.freeze({ + acceleration, + angle, + 'angular-acceleration': angularAcceleration, + area, + charge, + digital, + 'electric-current': electricCurrent, + energy, + force, + frequency, + illuminance, + length, + mass, + 'parts-per': partsPer, + power, + pressure, + speed, + temperature, + time, + torque, + voltage, + volume, + 'volume-flow-rate': volumeFlowRate, +}); export enum UnitsType { capacity = 'capacity' } -export type TbUnitConvertor = (value: number) => number; -export type UnitDescriptionGroupByMeasure = Partial>; +export type TbUnitConverter = (value: number) => number; +export type UnitInfoGroupByMeasure = Partial>; -export interface UnitDescription { +export interface UnitInfo { abbr: string; measure: AllMeasures; system: UnitSystem; @@ -70,21 +172,247 @@ export interface Conversion { unit: Unit; } -export type UnitCache = Map< - string, - { +export type UnitCache = Map; -const searchUnitTags = (unit: UnitDescription, searchText: string): boolean => +const searchUnitTags = (unit: UnitInfo, searchText: string): boolean => !!unit.tags.find(t => t.toUpperCase().includes(searchText)); -export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( +export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( u => u.abbr.toUpperCase().includes(searchText) || u.name.toUpperCase().includes(searchText) || searchUnitTags(u, searchText) ); + +type Entries = [S, T[keyof T]]; + +export class Converter { + private readonly measureData: Record>; + private unitCache: Map< + string, + { + system: UnitSystem; + measure: AllMeasures; + unit: Unit; + abbr: AllMeasuresUnits; + } + >; + + constructor( + measures: Record>, + unitCache: UnitCache + ) { + this.measureData = measures; + this.unitCache = unitCache; + } + + getUnitConverter(from: AllMeasuresUnits | string, to: AllMeasuresUnits | string): TbUnitConverter { + return (value: number) => this.convert(value, from, to); + } + + convert(value: number, from: AllMeasuresUnits | string, to: AllMeasuresUnits | string): number { + const origin = this.getUnit(from); + const destination = this.getUnit(to); + + if (!origin) { + throw new Error(`Unsupported unit: ${from}`); + } + if (!destination) { + throw new Error(`Unsupported unit: ${to}`); + } + if (origin.abbr === destination.abbr) { + return value; + } + if (destination.measure !== origin.measure) { + throw Error(`Cannot convert incompatible measures: ${origin.measure} to ${destination.measure}`); + } + let result = value * origin.unit.to_anchor; + if (origin.unit.anchor_shift) { + result -= origin.unit.anchor_shift; + } + if (origin.system !== destination.system) { + const measureUnits = this.measureData[origin.measure][origin.system]; + const transform = measureUnits?.transform; + const ratio = measureUnits?.ratio; + if (typeof transform === 'function') { + result = transform(result); + } else if (typeof ratio === 'number') { + result *= ratio; + } else { + throw Error('System anchor requires a defined ratio or transform function'); + } + } + + if (destination.unit.anchor_shift) { + result += destination.unit.anchor_shift; + } + return result / destination.unit.to_anchor; + } + + getDefaultUnit(measureName: AllMeasures | (string & {}), unitSystem: UnitSystem): AllMeasuresUnits { + if (!this.isMeasure(measureName)) { + return null; + } + const units = this.getUnitsForMeasure(measureName, unitSystem); + if (!units) { + return null; + } + for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { + if (unit.to_anchor === 1 && (!unit.anchor_shift || unit.anchor_shift === 0)) { + return abbr; + } + } + return null; + } + + getUnit(abbr: AllMeasuresUnits | string): Conversion | null { + return this.unitCache.get(abbr) ?? null; + } + + describe(abbr: AllMeasuresUnits | string): UnitInfo { + const unit = this.getUnit(abbr); + return unit ? this.describeUnit(unit) : null; + } + + listUnits(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { + const results: UnitInfo[] = []; + + const measures = measureName + ? { [measureName]: this.measureData[measureName] } as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [AllMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; + } + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; + } + + for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { + results.push( + this.describeUnit({ + abbr, + measure: name as AllMeasures, + system, + unit, + }) + ); + } + } + } + return results; + } + + unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure | never { + const results: UnitInfoGroupByMeasure = {}; + + const measures = measureName + ? { [measureName]: this.measureData[measureName]} as Record> + : this.measureData; + + for (const [name, measure] of Object.entries(measures) as [AllMeasures, TbMeasure][]) { + if (!this.isMeasure(name)) { + continue; + } + + results[name] = []; + + const systems = unitSystem + ? [unitSystem] + : (Object.keys(measure) as UnitSystem[]); + + for (const system of systems) { + const units = this.getUnitsForMeasure(name, system); + if (!units) { + continue; + } + + for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { + results[name].push( + this.describeUnit({ + abbr, + measure: name as AllMeasures, + system, + unit, + }) + ); + } + } + } + return results; + } + + private describeUnit(unit: Conversion): UnitInfo { + return { + abbr: unit.abbr, + measure: unit.measure, + system: unit.system, + name: unit.unit.name, + tags: unit.unit.tags + }; + } + + private isMeasure(measureName: string): boolean { + return measureName in this.measureData; + } + + private getUnitsForMeasure( + measureName: AllMeasures | string, + unitSystem: UnitSystem + ): Partial> | null { + const measure = this.measureData[measureName]; + let system = unitSystem; + let units = measure[system]?.units; + if (!units && unitSystem === UnitSystem.IMPERIAL) { + system = UnitSystem.METRIC; + units = measure[system]?.units; + } + return units ?? null; + } +} + +function buildUnitCache(measures: Record>, + translate: TranslateService +) { + const unitCache: UnitCache = new Map(); + for (const [measureName, measure] of Object.entries(measures) as Entries< + typeof measures, + AllMeasures + >[]) { + for (const [systemName, system] of Object.entries( + measure + ) as Entries, UnitSystem>[]) { + for (const [testAbbr, unit] of Object.entries(system.units) as Entries< + Record, + AllMeasuresUnits + >[]) { + unit.name = translate.instant(unit.name); + unitCache.set(testAbbr, { + measure: measureName, + system: systemName, + abbr: testAbbr, + unit, + }); + } + } + } + return unitCache; +} + +export function getUnitConverter(translate: TranslateService): Converter { + const unitCache = buildUnitCache(allMeasures, translate); + return new Converter(allMeasures, unitCache); +} diff --git a/ui-ngx/src/app/core/services/unit/definitions/acceleration.ts b/ui-ngx/src/app/shared/models/units/acceleration.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/acceleration.ts rename to ui-ngx/src/app/shared/models/units/acceleration.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/angle.ts b/ui-ngx/src/app/shared/models/units/angle.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/angle.ts rename to ui-ngx/src/app/shared/models/units/angle.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/angular-acceleration.ts b/ui-ngx/src/app/shared/models/units/angular-acceleration.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/angular-acceleration.ts rename to ui-ngx/src/app/shared/models/units/angular-acceleration.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/area.ts b/ui-ngx/src/app/shared/models/units/area.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/area.ts rename to ui-ngx/src/app/shared/models/units/area.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/charge.ts b/ui-ngx/src/app/shared/models/units/charge.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/charge.ts rename to ui-ngx/src/app/shared/models/units/charge.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/digital.ts b/ui-ngx/src/app/shared/models/units/digital.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/digital.ts rename to ui-ngx/src/app/shared/models/units/digital.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/electric-current.ts b/ui-ngx/src/app/shared/models/units/electric-current.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/electric-current.ts rename to ui-ngx/src/app/shared/models/units/electric-current.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/energy.ts b/ui-ngx/src/app/shared/models/units/energy.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/energy.ts rename to ui-ngx/src/app/shared/models/units/energy.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/force.ts b/ui-ngx/src/app/shared/models/units/force.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/force.ts rename to ui-ngx/src/app/shared/models/units/force.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/frequency.ts b/ui-ngx/src/app/shared/models/units/frequency.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/frequency.ts rename to ui-ngx/src/app/shared/models/units/frequency.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/illuminance.ts b/ui-ngx/src/app/shared/models/units/illuminance.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/illuminance.ts rename to ui-ngx/src/app/shared/models/units/illuminance.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/length.ts b/ui-ngx/src/app/shared/models/units/length.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/length.ts rename to ui-ngx/src/app/shared/models/units/length.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/mass.ts b/ui-ngx/src/app/shared/models/units/mass.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/mass.ts rename to ui-ngx/src/app/shared/models/units/mass.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/parts-per.ts b/ui-ngx/src/app/shared/models/units/parts-per.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/parts-per.ts rename to ui-ngx/src/app/shared/models/units/parts-per.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/power.ts b/ui-ngx/src/app/shared/models/units/power.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/power.ts rename to ui-ngx/src/app/shared/models/units/power.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/pressure.ts b/ui-ngx/src/app/shared/models/units/pressure.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/pressure.ts rename to ui-ngx/src/app/shared/models/units/pressure.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/speed.ts b/ui-ngx/src/app/shared/models/units/speed.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/speed.ts rename to ui-ngx/src/app/shared/models/units/speed.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/temperature.ts b/ui-ngx/src/app/shared/models/units/temperature.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/temperature.ts rename to ui-ngx/src/app/shared/models/units/temperature.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/time.ts b/ui-ngx/src/app/shared/models/units/time.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/time.ts rename to ui-ngx/src/app/shared/models/units/time.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/torque.ts b/ui-ngx/src/app/shared/models/units/torque.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/torque.ts rename to ui-ngx/src/app/shared/models/units/torque.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/voltage.ts b/ui-ngx/src/app/shared/models/units/voltage.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/voltage.ts rename to ui-ngx/src/app/shared/models/units/voltage.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts b/ui-ngx/src/app/shared/models/units/volume-flow-rate.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/volume-flow-rate.ts rename to ui-ngx/src/app/shared/models/units/volume-flow-rate.ts diff --git a/ui-ngx/src/app/core/services/unit/definitions/volume.ts b/ui-ngx/src/app/shared/models/units/volume.ts similarity index 100% rename from ui-ngx/src/app/core/services/unit/definitions/volume.ts rename to ui-ngx/src/app/shared/models/units/volume.ts diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 89c2824545..d6f46b0815 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -53,8 +53,8 @@ import { WidgetSubscriptionCallbacks, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { UnitService } from '@core/services/unit/unit.service'; -import { TbUnit, TbUnitConvertor, TbUnitMapping } from '@shared/models/unit.models'; +import { UnitService } from '@core/services/unit.service'; +import { TbUnit, TbUnitConverter, TbUnitMapping } from '@shared/models/unit.models'; export type ComponentStyle = {[klass: string]: any}; @@ -862,50 +862,50 @@ export class AutoDateFormatProcessor extends DateFormatProcessor { } } -export interface FormatValueSettingProcessor { +export interface ValueFormatSettingProcessor { dec?: number; units?: TbUnit; showZeroDecimals?: boolean; } -export abstract class FormatValueProcessor { +export abstract class ValueFormatProcessor { - static fromSettings($injector: Injector, settings: FormatValueSettingProcessor): FormatValueProcessor { - if (typeof settings.units !== 'string' && isDefinedAndNotNull(settings.units?.from)) { - return new ConvertUnitProcessor($injector, settings) + static fromSettings($injector: Injector, settings: ValueFormatSettingProcessor): ValueFormatProcessor { + if (settings.units !== null && typeof settings.units === 'object') { + return new ConverterValueFormatProcessor($injector, settings) } else { - return new SimpleUnitProcessor($injector, settings); + return new SimpleValueFormatProcessor($injector, settings); } } protected constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { + protected settings: ValueFormatSettingProcessor) { } - abstract format(value: any): string; + abstract update(value: any): string; } -export class SimpleUnitProcessor extends FormatValueProcessor { +export class SimpleValueFormatProcessor extends ValueFormatProcessor { private readonly isDefinedUnit: boolean; private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; + private readonly hideZeroDecimals: boolean; constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { + protected settings: ValueFormatSettingProcessor) { super($injector, settings); this.isDefinedUnit = isNotEmptyStr(settings.units); this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; + this.hideZeroDecimals = !settings.showZeroDecimals; } - format(value: any): string { + update(value: any): string { if (isDefinedAndNotNull(value) && isNumeric(value) && (this.isDefinedDec || this.isDefinedUnit || Number(value).toString() === value)) { let formatted = value; if (this.isDefinedDec) { formatted = Number(formatted).toFixed(this.settings.dec); } - if (!this.showZeroDecimals) { + if (this.hideZeroDecimals) { formatted = Number(formatted) } formatted = formatted.toString(); @@ -918,39 +918,37 @@ export class SimpleUnitProcessor extends FormatValueProcessor { } } -export class ConvertUnitProcessor extends FormatValueProcessor { +export class ConverterValueFormatProcessor extends ValueFormatProcessor { private readonly isDefinedDec: boolean; - private readonly showZeroDecimals: boolean; - private readonly unitConvertor: TbUnitConvertor; + private readonly hideZeroDecimals: boolean; + private readonly unitConverter: TbUnitConverter; private readonly unitAbbr: string; constructor(protected $injector: Injector, - protected settings: FormatValueSettingProcessor) { + protected settings: ValueFormatSettingProcessor) { super($injector, settings); const unitService = this.$injector.get(UnitService); - const userUnitSystem = unitService.getUnitSystem(); const unit = settings.units as TbUnitMapping; - const fromUnit = unit.from; - this.unitAbbr = isNotEmptyStr(unit[userUnitSystem]) ? unit[userUnitSystem] : fromUnit; + this.unitAbbr = unitService.getTargetUnitSymbol(unit); try { - this.unitConvertor = unitService.geUnitConvertor(fromUnit, this.unitAbbr); + this.unitConverter = unitService.geUnitConverter(unit); } catch (e) {/**/} this.isDefinedDec = isDefinedAndNotNull(settings.dec); - this.showZeroDecimals = !!settings.showZeroDecimals; + this.hideZeroDecimals = !settings.showZeroDecimals; } - format(value: any): string { + update(value: any): string { if (isDefinedAndNotNull(value) && isNumeric(value)) { let formatted: number | string = Number(value); - if (this.unitConvertor) { - formatted = this.unitConvertor(value); + if (this.unitConverter) { + formatted = this.unitConverter(value); } if (this.isDefinedDec) { formatted = Number(formatted).toFixed(this.settings.dec); } - if (!this.showZeroDecimals) { + if (this.hideZeroDecimals) { formatted = Number(formatted) } formatted = formatted.toString(); From 83e5305966eb8756ee1a866759c69bf9f7fbf15c Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 2 May 2025 20:29:10 +0300 Subject: [PATCH 12/27] UI: Add new unit definition --- ui-ngx/src/app/shared/models/unit.models.ts | 7 ++ .../models/units/Magnetic-permeability.ts | 37 ++++++++++ .../shared/models/units/air-quality-index.ts | 19 +++++ .../models/units/amount-of-substance.ts | 41 +++++++++++ ui-ngx/src/app/shared/models/units/area.ts | 7 +- .../app/shared/models/units/blood-glucose.ts | 20 ++++++ .../app/shared/models/units/capacitance.ts | 59 +++++++++++++++ .../shared/models/units/catalytic-activity.ts | 19 +++++ .../models/units/concentration-gradient.ts | 40 +++++++++++ .../models/units/concentration-volume.ts | 63 ++++++++++++++++ .../shared/models/units/current-density.ts | 26 +++++++ .../shared/models/units/data-transfer-rate.ts | 59 +++++++++++++++ ui-ngx/src/app/shared/models/units/density.ts | 50 +++++++++++++ .../models/units/dimensionless-ratio.ts | 21 ++++++ .../shared/models/units/dynamic-viscosity.ts | 60 ++++++++++++++++ .../models/units/earthquake-magnitude.ts | 19 +++++ .../models/units/electric-field-strength.ts | 29 ++++++++ .../app/shared/models/units/electric-flux.ts | 46 ++++++++++++ .../shared/models/units/electric-moment.ts | 26 +++++++ .../models/units/electric-permittivity.ts | 21 ++++++ .../models/units/electrical-conductance.ts | 44 ++++++++++++ .../models/units/electrical-conductivity.ts | 30 ++++++++ .../src/app/shared/models/units/frequency.ts | 17 ++++- .../shared/models/units/fuel-efficiency.ts | 46 ++++++++++++ .../app/shared/models/units/heat-capacity.ts | 21 ++++++ .../src/app/shared/models/units/humidity.ts | 20 ++++++ .../src/app/shared/models/units/inductance.ts | 40 +++++++++++ .../models/units/kinematic-viscosity.ts | 55 ++++++++++++++ .../app/shared/models/units/light-exposure.ts | 21 ++++++ .../models/units/liner-charge-density.ts | 21 ++++++ .../shared/models/units/logarithmic-units.ts | 29 ++++++++ .../shared/models/units/luminous-efficacy.ts | 21 ++++++ .../app/shared/models/units/luminous-flux.ts | 21 ++++++ .../shared/models/units/luminous-intensity.ts | 21 ++++++ .../models/units/magnetic-field-gradient.ts | 25 +++++++ .../app/shared/models/units/magnetic-field.ts | 71 +++++++++++++++++++ .../app/shared/models/units/magnetic-flux.ts | 44 ++++++++++++ .../shared/models/units/magnetic-moment.ts | 26 +++++++ .../app/shared/models/units/molar-energy.ts | 21 ++++++ .../models/units/molar-heat-capacity.ts | 21 ++++++ .../src/app/shared/models/units/molar-mass.ts | 29 ++++++++ .../models/units/particle-concentration.ts | 19 +++++ .../src/app/shared/models/units/percentage.ts | 20 ++++++ ui-ngx/src/app/shared/models/units/ph.ts | 19 +++++ .../app/shared/models/units/polarization.ts | 21 ++++++ .../app/shared/models/units/power-density.ts | 65 +++++++++++++++++ .../src/app/shared/models/units/pressure.ts | 16 ++++- .../src/app/shared/models/units/radiance.ts | 19 +++++ .../shared/models/units/radiant-intensity.ts | 20 ++++++ .../app/shared/models/units/radiation-dose.ts | 50 +++++++++++++ .../models/units/radioactive-decay-rate.ts | 25 +++++++ .../units/radioactivity-concentration.ts | 25 +++++++ .../app/shared/models/units/radioactivity.ts | 40 +++++++++++ .../src/app/shared/models/units/resistance.ts | 46 ++++++++++++ .../shared/models/units/reynolds-number.ts | 21 ++++++ .../shared/models/units/signal-strength.ts | 31 ++++++++ .../app/shared/models/units/solid-angle.ts | 21 ++++++ .../shared/models/units/specific-energy.ts | 21 ++++++ .../models/units/specific-heat-capacity.ts | 21 ++++++ .../shared/models/units/specific-volume.ts | 21 ++++++ ui-ngx/src/app/shared/models/units/speed.ts | 7 +- .../app/shared/models/units/sugar-content.ts | 19 +++++ .../models/units/surface-charge-density.ts | 21 ++++++ .../shared/models/units/surface-tension.ts | 21 ++++++ .../models/units/thermal-conductivity.ts | 21 ++++++ .../src/app/shared/models/units/turbidity.ts | 20 ++++++ .../models/units/volume-charge-density.ts | 21 ++++++ .../src/app/shared/models/units/wavenumber.ts | 21 ++++++ .../assets/locale/locale.constant-en_US.json | 2 - 69 files changed, 1980 insertions(+), 7 deletions(-) create mode 100644 ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts create mode 100644 ui-ngx/src/app/shared/models/units/air-quality-index.ts create mode 100644 ui-ngx/src/app/shared/models/units/amount-of-substance.ts create mode 100644 ui-ngx/src/app/shared/models/units/blood-glucose.ts create mode 100644 ui-ngx/src/app/shared/models/units/capacitance.ts create mode 100644 ui-ngx/src/app/shared/models/units/catalytic-activity.ts create mode 100644 ui-ngx/src/app/shared/models/units/concentration-gradient.ts create mode 100644 ui-ngx/src/app/shared/models/units/concentration-volume.ts create mode 100644 ui-ngx/src/app/shared/models/units/current-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/data-transfer-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/density.ts create mode 100644 ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts create mode 100644 ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts create mode 100644 ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-field-strength.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-flux.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-moment.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-permittivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/electrical-conductance.ts create mode 100644 ui-ngx/src/app/shared/models/units/electrical-conductivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/fuel-efficiency.ts create mode 100644 ui-ngx/src/app/shared/models/units/heat-capacity.ts create mode 100644 ui-ngx/src/app/shared/models/units/humidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/inductance.ts create mode 100644 ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts create mode 100644 ui-ngx/src/app/shared/models/units/light-exposure.ts create mode 100644 ui-ngx/src/app/shared/models/units/liner-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/logarithmic-units.ts create mode 100644 ui-ngx/src/app/shared/models/units/luminous-efficacy.ts create mode 100644 ui-ngx/src/app/shared/models/units/luminous-flux.ts create mode 100644 ui-ngx/src/app/shared/models/units/luminous-intensity.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-field.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-flux.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-moment.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-energy.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-mass.ts create mode 100644 ui-ngx/src/app/shared/models/units/particle-concentration.ts create mode 100644 ui-ngx/src/app/shared/models/units/percentage.ts create mode 100644 ui-ngx/src/app/shared/models/units/ph.ts create mode 100644 ui-ngx/src/app/shared/models/units/polarization.ts create mode 100644 ui-ngx/src/app/shared/models/units/power-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/radiance.ts create mode 100644 ui-ngx/src/app/shared/models/units/radiant-intensity.ts create mode 100644 ui-ngx/src/app/shared/models/units/radiation-dose.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/resistance.ts create mode 100644 ui-ngx/src/app/shared/models/units/reynolds-number.ts create mode 100644 ui-ngx/src/app/shared/models/units/signal-strength.ts create mode 100644 ui-ngx/src/app/shared/models/units/solid-angle.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-energy.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-volume.ts create mode 100644 ui-ngx/src/app/shared/models/units/sugar-content.ts create mode 100644 ui-ngx/src/app/shared/models/units/surface-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/surface-tension.ts create mode 100644 ui-ngx/src/app/shared/models/units/thermal-conductivity.ts create mode 100644 ui-ngx/src/app/shared/models/units/turbidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/volume-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/wavenumber.ts diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 4f73fc4fbc..27cca5f603 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -146,6 +146,7 @@ export interface Unit { tags: string[]; to_anchor: number; anchor_shift?: number; + transform?: (value: number) => number; } export type TbUnit = string | TbUnitMapping; @@ -235,6 +236,9 @@ export class Converter { if (origin.unit.anchor_shift) { result -= origin.unit.anchor_shift; } + if (typeof origin.unit.transform === 'function') { + result = origin.unit.transform(result); + } if (origin.system !== destination.system) { const measureUnits = this.measureData[origin.measure][origin.system]; const transform = measureUnits?.transform; @@ -251,6 +255,9 @@ export class Converter { if (destination.unit.anchor_shift) { result += destination.unit.anchor_shift; } + if (typeof destination.unit.transform === 'function') { + result = destination.unit.transform(result); + } return result / destination.unit.to_anchor; } diff --git a/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts b/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts new file mode 100644 index 0000000000..be120c4b15 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts @@ -0,0 +1,37 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticPermeabilityMetricUnits = 'H/m'; +export type MagneticPermeabilityImperialUnits = 'G/Oe'; + +export type MagneticPermeabilityUnits = + | MagneticPermeabilityMetricUnits + | MagneticPermeabilityImperialUnits; + +const METRIC: TbMeasureUnits = { + transform: (Hm) => Hm * 795774.715, + units: { + 'H/m': { + name: 'unit.henry-per-meter', + tags: ['magnetic permeability', 'henry per meter', 'H/m'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + transform: (GOe) => GOe / 795774.715, + units: { + 'G/Oe': { + name: 'unit.gauss-per-oersted', + tags: ['magnetic field', 'Gauss per Oersted', 'G/Oe'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/air-quality-index.ts b/ui-ngx/src/app/shared/models/units/air-quality-index.ts new file mode 100644 index 0000000000..907071b594 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/air-quality-index.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AirQualityIndexUnits = 'aqi'; + +const METRIC: TbMeasureUnits = { + units: { + aqi: { + name: 'unit.aqi', + tags: ['AQI', 'air quality index'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts new file mode 100644 index 0000000000..47f798c388 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts @@ -0,0 +1,41 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AmountOfSubstanceMetricUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; + +export type AmountOfSubstanceUnits = AmountOfSubstanceMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'mol': { + name: 'unit.mole', + tags: ['amount of substance', 'chemical amount', 'mole', 'mol'], + to_anchor: 1, + }, + 'nmol': { + name: 'unit.nanomole', + tags: ['amount of substance', 'nanomole', 'nmol'], + to_anchor: 0.000000001, + }, + 'μmol': { + name: 'unit.micromole', + tags: ['amount of substance', 'micromole', 'μmol'], + to_anchor: 0.000001, + }, + 'mmol': { + name: 'unit.millimole', + tags: ['amount of substance', 'millimole', 'mmol'], + to_anchor: 0.001, + }, + 'kmol': { + name: 'unit.kilomole', + tags: ['amount of substance', 'kilomole', 'kmol'], + to_anchor: 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/area.ts b/ui-ngx/src/app/shared/models/units/area.ts index 6a364496b5..6c36d6d976 100644 --- a/ui-ngx/src/app/shared/models/units/area.ts +++ b/ui-ngx/src/app/shared/models/units/area.ts @@ -16,7 +16,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AreaMetricUnits = 'mm²' | 'cm²' | 'm²' | 'a' | 'ha' | 'km²'; +export type AreaMetricUnits = 'mm²' | 'cm²' | 'm²' | 'a' | 'ha' | 'km²' | 'barn'; export type AreaImperialUnits = 'in²' | 'yd²' | 'ft²' | 'ac' | 'ml²' | 'cin'; export type AreaUnits = AreaMetricUnits | AreaImperialUnits; @@ -54,6 +54,11 @@ const METRIC: TbMeasureUnits = { tags: ['area','lot','zone','space','region','square kilometer','square kilometers','km²','sq-km'], to_anchor: 1000000, }, + barn: { + name: 'unit.barn', + tags: ['cross-sectional area', 'particle physics', 'nuclear physics', 'barn'], + to_anchor: 1e-28, + }, } }; diff --git a/ui-ngx/src/app/shared/models/units/blood-glucose.ts b/ui-ngx/src/app/shared/models/units/blood-glucose.ts new file mode 100644 index 0000000000..18b0902591 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/blood-glucose.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type BloodGlucoseUnits = BloodGlucoseMetricUnits; +export type BloodGlucoseMetricUnits = 'mg/dL'; + +const METRIC: TbMeasureUnits = { + units: { + 'mg/dL': { + name: 'unit.milligrams-per-deciliter', + tags: ['glucose', 'blood sugar', 'glucose level', 'mg/dL'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/capacitance.ts b/ui-ngx/src/app/shared/models/units/capacitance.ts new file mode 100644 index 0000000000..4999afd536 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/capacitance.ts @@ -0,0 +1,59 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type CapacitanceUnits = 'F' | 'mF' | 'μF' | 'nF' | 'pF' | 'kF' | 'MF' | 'GF' | 'TF'; + +const METRIC: TbMeasureUnits = { + units: { + 'F': { + name: 'unit.farad', + tags: ['electric capacitance', 'capacitance', 'farad', 'F'], + to_anchor: 1, + }, + 'mF': { + name: 'unit.millifarad', + tags: ['electric capacitance', 'capacitance', 'millifarad', 'mF'], + to_anchor: 1e-3, + }, + 'μF': { + name: 'unit.microfarad', + tags: ['electric capacitance', 'capacitance', 'microfarad', 'μF'], + to_anchor: 1e-6, + }, + 'nF': { + name: 'unit.nanofarad', + tags: ['electric capacitance', 'capacitance', 'nanofarad', 'nF'], + to_anchor: 1e-9, + }, + 'pF': { + name: 'unit.picofarad', + tags: ['electric capacitance', 'capacitance', 'picofarad', 'pF'], + to_anchor: 1e-12, + }, + 'kF': { + name: 'unit.kilofarad', + tags: ['electric capacitance', 'capacitance', 'kilofarad', 'kF'], + to_anchor: 1e3, + }, + 'MF': { + name: 'unit.megafarad', + tags: ['electric capacitance', 'capacitance', 'megafarad', 'MF'], + to_anchor: 1e6, + }, + 'GF': { + name: 'unit.gigafarad', + tags: ['electric capacitance', 'capacitance', 'gigafarad', 'GF'], + to_anchor: 1e9, + }, + 'TF': { + name: 'unit.terafarad', + tags: ['electric capacitance', 'capacitance', 'terafarad', 'TF'], + to_anchor: 1e12, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/catalytic-activity.ts b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts new file mode 100644 index 0000000000..7198bf4d9c --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AirQualityIndexUnits = 'kat'; + +const METRIC: TbMeasureUnits = { + units: { + kat: { + name: 'unit.katal', + tags: ['catalytic activity', 'enzyme activity', 'kat'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/concentration-gradient.ts b/ui-ngx/src/app/shared/models/units/concentration-gradient.ts new file mode 100644 index 0000000000..ef406445d2 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/concentration-gradient.ts @@ -0,0 +1,40 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ConcentrationMetricUnits = 'mol/m³' | 'mg/mL' | 'mg/m³' | 'µg/m³' | 'particles/mL'; +export type ConcentrationUnits = ConcentrationMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'mol/m³': { + name: 'unit.mole-per-cubic-meter', + tags: ['concentration', 'amount of substance per unit volume', 'mole per cubic meter', 'mol/m³'], + to_anchor: 1, + }, + 'mg/mL': { + name: 'unit.milligram-per-milliliter', + tags: ['concentration', 'mass per unit volume', 'milligram per milliliter', 'mg/mL'], + to_anchor: 1, + }, + 'mg/m³': { + name: 'unit.milligram-per-cubic-meter', + tags: ['concentration', 'mass per unit volume', 'milligram per cubic meter', 'mg/m³'], + to_anchor: 1, + }, + 'µg/m³': { + name: 'unit.micrograms-per-cubic-meter', + tags: ['concentration', 'air quality', 'particulate matter', 'PM2.5', 'PM10', 'micrograms per cubic meter', 'µg/m³'], + to_anchor: 1, + }, + 'particles/mL': { + name: 'unit.particle-density', + tags: ['concentration', 'particle density', 'particles per milliliter', 'particles/mL'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/concentration-volume.ts b/ui-ngx/src/app/shared/models/units/concentration-volume.ts new file mode 100644 index 0000000000..94c8050fee --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/concentration-volume.ts @@ -0,0 +1,63 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ConcentrationVolumeMetricUnits = + 'mol/m³' + | 'µg/m³' + | 'mg/m³' + | 'g/m³' + | 'mg/L' + | 'mg/mL' + | 'kat/m³' + | '°Bx'; +export type ConcentrationVolumeUnits = ConcentrationVolumeMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'mol/m³': { + name: 'unit.mole-per-cubic-meter', + tags: ['concentration', 'amount of substance', 'mole per cubic meter', 'mol/m³'], + to_anchor: 1, + }, + 'µg/m³': { + name: 'unit.micrograms-per-cubic-meter', + tags: ['coarse particulate matter', 'pm10', 'fine particulate matter', 'pm2.5', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'micrograms per cubic meter', 'µg/m³'], + to_anchor: 1e-9, + }, + 'mg/m³': { + name: 'unit.milligram-per-cubic-meter', + tags: ['concentration', 'mass per volume', 'mg/m³'], + to_anchor: 1e-6, + }, + 'g/m³': { + name: 'unit.gram-per-cubic-meter', + tags: ['humidity', 'moisture', 'absolute humidity', 'g/m³'], + to_anchor: 1 / 1000, + }, + 'mg/L': { + name: 'unit.mg-per-liter', + tags: ['dissolved oxygen', 'water quality', 'mg/L'], + to_anchor: 1e-6, + }, + 'mg/mL': { + name: 'unit.milligram-per-milliliter', + tags: ['concentration', 'mass per volume', 'mg/mL'], + to_anchor: 1 / 1000, + }, + 'kat/m³': { + name: 'unit.katal-per-cubic-metre', + tags: ['catalytic activity concentration', 'enzyme concentration', 'kat/m³'], + to_anchor: 1, + }, + '°Bx': { + name: 'unit.degrees-brix', + tags: ['sugar content', 'fruit ripeness', 'Bx'], + to_anchor: 10.04 * 1e-3, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/current-density.ts b/ui-ngx/src/app/shared/models/units/current-density.ts new file mode 100644 index 0000000000..7b332c57f4 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/current-density.ts @@ -0,0 +1,26 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type CurrentDensityMetricUnits = 'µA/cm²' | 'A/m²'; + +export type CurrentDensityUnits = CurrentDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'µA/cm²': { + name: 'unit.microampere-per-square-centimeter', + tags: ['current density', 'microampere per square centimeter', 'µA/cm²'], + to_anchor: 10000, + }, + 'A/m²': { + name: 'unit.ampere-per-square-meter', + tags: ['current density', 'current per unit area', 'ampere per square meter', 'A/m²'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts new file mode 100644 index 0000000000..e3b9095700 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts @@ -0,0 +1,59 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DataTransferRateUnits = 'bps' | 'kbps' | 'Mbps' | 'Gbps' | 'Tbps' | 'B/s' | 'KB/s' | 'MB/s' | 'GB/s'; + +const METRIC: TbMeasureUnits = { + units: { + 'bps': { + name: 'unit.bit-per-second', + tags: ['data transfer rate', 'bps'], + to_anchor: 1, + }, + 'kbps': { + name: 'unit.kilobit-per-second', + tags: ['data transfer rate', 'kbps'], + to_anchor: 1e3, + }, + 'Mbps': { + name: 'unit.megabit-per-second', + tags: ['data transfer rate', 'Mbps'], + to_anchor: 1e6, + }, + 'Gbps': { + name: 'unit.gigabit-per-second', + tags: ['data transfer rate', 'Gbps'], + to_anchor: 1e9, + }, + 'Tbps': { + name: 'unit.terabit-per-second', + tags: ['data transfer rate', 'Tbps'], + to_anchor: 1e12, + }, + 'B/s': { + name: 'unit.byte-per-second', + tags: ['data transfer rate', 'B/s'], + to_anchor: 8, + }, + 'KB/s': { + name: 'unit.kilobyte-per-second', + tags: ['data transfer rate', 'KB/s'], + to_anchor: 8e3, + }, + 'MB/s': { + name: 'unit.megabyte-per-second', + tags: ['data transfer rate', 'MB/s'], + to_anchor: 8e6, + }, + 'GB/s': { + name: 'unit.gigabyte-per-second', + tags: ['data transfer rate', 'GB/s'], + to_anchor: 8e9, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/density.ts b/ui-ngx/src/app/shared/models/units/density.ts new file mode 100644 index 0000000000..8603de1976 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/density.ts @@ -0,0 +1,50 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DensityMetricUnits = 'kg/m³' | 'g/cm³'; +export type DensityImperialUnits = 'lb/ft³' | 'oz/in³' | 'ton/yd³'; + +export type DensityUnits = DensityMetricUnits | DensityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 0.062428, + units: { + 'kg/m³': { + name: 'unit.kilogram-per-cubic-meter', + tags: ['density', 'mass per unit volume', 'kg/m³'], + to_anchor: 1, // Base unit: kg/m³ + }, + 'g/cm³': { + name: 'unit.gram-per-cubic-centimeter', + tags: ['density', 'mass per unit volume', 'g/cm³'], + to_anchor: 1000, // 1 g/cm³ = 10³ kg/m³ + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.062428, + units: { + 'lb/ft³': { + name: 'unit.pound-per-cubic-foot', + tags: ['density', 'mass per unit volume', 'lb/ft³'], + to_anchor: 1, + }, + 'oz/in³': { + name: 'unit.ounces-per-cubic-inch', + tags: ['density', 'mass per unit volume', 'oz/in³'], + to_anchor: 1728, + }, + 'ton/yd³': { + name: 'unit.tons-per-cubic-yard', + tags: ['density', 'mass per unit volume', 'ton/yd³'], + to_anchor: 74.074, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts b/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts new file mode 100644 index 0000000000..71972aa6e8 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DimensionlessRatioMetricUnits = 'm/m'; + +export type DimensionRatioUnits = DimensionlessRatioMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'm/m': { + name: 'unit.meter-per-meter', + tags: ['ratio of length to length', 'meter per meter', 'm/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts new file mode 100644 index 0000000000..272892f9df --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts @@ -0,0 +1,60 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DynamicViscosityMetricUnits = 'Pa·s' | 'cP' | 'P' | 'N·s/m²' | 'dyn·s/cm²' | 'kg/(m·s)'; +export type DynamicViscosityImperialUnits = 'lb/(ft·h)'; + +export type DynamicViscosityUnits = DynamicViscosityMetricUnits | DynamicViscosityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 2419.0883293091, + units: { + 'Pa·s': { + name: 'unit.pascal-second', + tags: ['dynamic viscosity', 'viscosity', 'fluid mechanics', 'Pa·s'], + to_anchor: 1, + }, + 'cP': { + name: 'unit.centipoise', + tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'centipoise', 'cP'], + to_anchor: 0.001, + }, + 'P': { + name: 'unit.poise', + tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'poise', 'P'], + to_anchor: 0.1, + }, + 'N·s/m²': { + name: 'unit.newton-second-per-square-meter', + tags: ['newton second per square meter', 'N·s/m²'], + to_anchor: 1, + }, + 'dyn·s/cm²': { + name: 'unit.dyne-second-per-square-centimeter', + tags: ['dyne second per square centimeter', 'dyn·s/cm²'], + to_anchor: 0.1, + }, + 'kg/(m·s)': { + name: 'unit.kilogram-per-meter-second', + tags: ['kilogram per meter-second', 'kg/(m·s)'], + to_anchor: 1, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 0.00041337887, + units: { + 'lb/(ft·h)': { + name: 'unit.pound-per-foot-hour', + tags: ['pound per foot-hour', 'lb/(ft·h)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts new file mode 100644 index 0000000000..02658073b5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type EarthquakeMagnitudeUnits = 'richter'; + +const METRIC: TbMeasureUnits = { + units: { + richter: { + name: 'unit.richter-scale', + tags: ['earthquake', 'seismic activity', 'richter'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-field-strength.ts b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts new file mode 100644 index 0000000000..f8d753c401 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts @@ -0,0 +1,29 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricFieldStrengthUnits = 'V/m' | 'mV/m' | 'kV/m'; + +const METRIC: TbMeasureUnits = { + units: { + 'V/m': { + name: 'unit.volts-per-meter', + tags: ['electric field strength', 'volts per meter', 'V/m'], + to_anchor: 1, + }, + 'mV/m': { + name: 'unit.millivolts-per-meter', + tags: ['electric field strength', 'millivolts per meter', 'mV/m'], + to_anchor: 1e-3, + }, + 'kV/m': { + name: 'unit.kilovolts-per-meter', + tags: ['electric field strength', 'kilovolts per meter', 'kV/m'], + to_anchor: 1e3, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-flux.ts b/ui-ngx/src/app/shared/models/units/electric-flux.ts new file mode 100644 index 0000000000..ac40a58293 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-flux.ts @@ -0,0 +1,46 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricFluxMetricUnits = 'V·m' | 'kV·m' | 'MV·m' | 'µV·m' | 'mV·m' | 'nV·m'; + +export type ElectricFluxUnits = ElectricFluxMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'V·m': { + name: 'unit.volt-meter', + tags: ['electric flux', 'volt-meter', 'V·m'], + to_anchor: 1, + }, + 'kV·m': { + name: 'unit.kilovolt-meter', + tags: ['electric flux', 'kilovolt-meter', 'kV·m'], + to_anchor: 1000, + }, + 'MV·m': { + name: 'unit.megavolt-meter', + tags: ['electric flux', 'megavolt-meter', 'MV·m'], + to_anchor: 1000000, + }, + 'µV·m': { + name: 'unit.microvolt-meter', + tags: ['electric flux', 'microvolt-meter', 'µV·m'], + to_anchor: 0.000001, + }, + 'mV·m': { + name: 'unit.millivolt-meter', + tags: ['electric flux', 'millivolt-meter', 'mV·m'], + to_anchor: 0.001, + }, + 'nV·m': { + name: 'unit.nanovolt-meter', + tags: ['electric flux', 'nanovolt-meter', 'nV·m'], + to_anchor: 0.000000001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-moment.ts b/ui-ngx/src/app/shared/models/units/electric-moment.ts new file mode 100644 index 0000000000..47a8e55830 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-moment.ts @@ -0,0 +1,26 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricMomentMetricUnits = 'C·m' | 'D'; + +export type ElectricMomentUnits = ElectricMomentMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m': { + name: 'unit.electric-dipole-moment', + tags: ['electric dipole', 'dipole moment', 'coulomb meter', 'C·m'], + to_anchor: 1, + }, + 'D': { + name: 'unit.debye', + tags: ['polarization', 'electric dipole moment', 'debye', 'D'], + to_anchor: 3.33564e-30 + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-permittivity.ts b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts new file mode 100644 index 0000000000..d9bea899a7 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricPermittivityMetricUnits = 'F/m'; + +export type ElectricPermittivityUnits = ElectricPermittivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'F/m': { + name: 'unit.farad-per-meter', + tags: ['electric permittivity', 'farad per meter', 'F/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts new file mode 100644 index 0000000000..0a1fe6bebe --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts @@ -0,0 +1,44 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricalConductanceUnits = 'S' | 'mS' | 'μS' | 'kS' | 'MS' | 'GS'; + +const METRIC: TbMeasureUnits = { + units: { + 'S': { + name: 'unit.siemens', + tags: ['electrical conductance', 'conductance', 'siemens', 'S'], + to_anchor: 1, + }, + 'mS': { + name: 'unit.millisiemens', + tags: ['electrical conductance', 'conductance', 'millisiemens', 'mS'], + to_anchor: 1e-3, + }, + 'μS': { + name: 'unit.microsiemens', + tags: ['electrical conductance', 'conductance', 'microsiemens', 'μS'], + to_anchor: 1e-6, + }, + 'kS': { + name: 'unit.kilosiemens', + tags: ['electrical conductance', 'conductance', 'kilosiemens', 'kS'], + to_anchor: 1e3, + }, + 'MS': { + name: 'unit.megasiemens', + tags: ['electrical conductance', 'conductance', 'megasiemens', 'MS'], + to_anchor: 1e6, + }, + 'GS': { + name: 'unit.gigasiemens', + tags: ['electrical conductance', 'conductance', 'gigasiemens', 'GS'], + to_anchor: 1e9, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts new file mode 100644 index 0000000000..9bf7245ff1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts @@ -0,0 +1,30 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricalConductivityMetricUnits = 'µS/cm' | 'mS/m' | 'S/m'; +export type ElectricalConductivityUnits = ElectricalConductivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'S/m': { + name: 'unit.siemens-per-meter', + tags: ['Electrical conductivity', 'water quality', 'soil quality', 'siemens per meter', 'S/m'], + to_anchor: 1, + }, + 'µS/cm': { + name: 'unit.microsiemens-per-centimeter', + tags: ['Electrical conductivity', 'water quality', 'soil quality', 'microsiemens per centimeter', 'µS/cm'], + to_anchor: 0.0001, + }, + 'mS/m': { + name: 'unit.millisiemens-per-meter', + tags: ['Electrical conductivity', 'water quality', 'soil quality', 'millisiemens per meter', 'mS/m'], + to_anchor: 0.001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/frequency.ts b/ui-ngx/src/app/shared/models/units/frequency.ts index ecff651b78..f677803fe8 100644 --- a/ui-ngx/src/app/shared/models/units/frequency.ts +++ b/ui-ngx/src/app/shared/models/units/frequency.ts @@ -17,7 +17,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type FrequencyUnits = FrequencyMetricUnits; -export type FrequencyMetricUnits = 'mHz' | 'Hz' | 'kHz' | 'MHz' | 'GHz' | 'THz' | 'rpm' | 'deg/s' | 'rad/s'; +export type FrequencyMetricUnits = 'mHz' | 'Hz' | 'kHz' | 'MHz' | 'GHz' | 'THz' | 'rpm' | 'deg/s' | 'rad/s' | 'RPM' | 'λ' | 'bpm'; const METRIC: TbMeasureUnits = { units: { @@ -56,6 +56,21 @@ const METRIC: TbMeasureUnits = { tags: ['frequency', 'rotation per minute', 'rotations per minute', 'rpm', 'angular velocity'], to_anchor: 1 / 60, }, + RPM: { + name: 'unit.rpm', + tags: ['rotational speed', 'angular velocity', 'revolutions per minute', 'RPM'], + to_anchor: 1 / 60, + }, + 'λ': { + name: 'unit.lambda', + tags: ['wavelength', 'lambda', 'λ'], + to_anchor: 299792458, + }, + bpm: { + name: 'unit.beats-per-minute', + tags: ['heart rate', 'pulse', 'bpm'], + to_anchor: 0.0167 + }, 'deg/s': { name: 'unit.deg-per-second', tags: ['angular velocity', 'degrees per second', 'deg/s'], diff --git a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts new file mode 100644 index 0000000000..f097382089 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts @@ -0,0 +1,46 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type FuelEfficiencyMetricUnits = 'km/L' | 'L/100km'; +export type FuelEfficiencyImperialUnits = 'mpg' | 'gal/mi'; + +export type FuelEfficiencyUnits = FuelEfficiencyMetricUnits | FuelEfficiencyImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 2.35214583, + units: { + 'km/L': { + name: 'unit.kilometers-per-liter', + tags: ['fuel efficiency', 'km/L'], + to_anchor: 1, + }, + 'L/100km': { + name: 'unit.liters-per-100-km', + tags: ['fuel efficiency', 'L/100km'], + to_anchor: 1, + transform: (value) => 100 / value, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 0.425144, + units: { + 'mpg': { + name: 'unit.miles-per-gallon', + tags: ['fuel efficiency', 'mpg'], + to_anchor: 0.425144, + }, + 'gal/mi': { + name: 'unit.gallons-per-mile', + tags: ['fuel efficiency', 'gal/mi'], + to_anchor: 2.35214583, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/heat-capacity.ts b/ui-ngx/src/app/shared/models/units/heat-capacity.ts new file mode 100644 index 0000000000..05b56cd0a7 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/heat-capacity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type HeatCapacityMetricUnits = 'J/K'; + +export type HeatCapacityUnits = HeatCapacityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/K': { + name: 'unit.joule-per-kelvin', + tags: ['specific heat capacity', 'heat capacity per unit temperature', 'joule per kelvin', 'J/K'], + to_anchor: 1, // Base unit: J/K + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/humidity.ts b/ui-ngx/src/app/shared/models/units/humidity.ts new file mode 100644 index 0000000000..22bf1d6902 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/humidity.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type HumidityUnits = HumidityMetricUnits; +export type HumidityMetricUnits = 'g/kg'; + +const METRIC: TbMeasureUnits = { + units: { + 'g/kg': { + name: 'unit.gram-per-kilogram', + tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/inductance.ts b/ui-ngx/src/app/shared/models/units/inductance.ts new file mode 100644 index 0000000000..9f0d0bb3eb --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/inductance.ts @@ -0,0 +1,40 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type InductanceMetricUnits = 'H' | 'mH' | 'µH' | 'nH' | 'T·m/A'; +export type InductanceUnits = InductanceMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + H: { + name: 'unit.henry', + tags: ['inductance', 'magnetic induction', 'H'], + to_anchor: 1, + }, + mH: { + name: 'unit.millihenry', + tags: ['inductance', 'millihenry', 'mH'], + to_anchor: 0.001, + }, + µH: { + name: 'unit.microhenry', + tags: ['inductance', 'microhenry', 'µH'], + to_anchor: 1e-6, + }, + nH: { + name: 'unit.nanohenry', + tags: ['inductance', 'nanohenry', 'nH'], + to_anchor: 1e-9, + }, + 'T·m/A': { + name: 'unit.tesla-meter-per-ampere', + tags: ['magnetic field', 'Tesla Meter per Ampere', 'T·m/A', 'magnetic flux'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts new file mode 100644 index 0000000000..6a9104ff9f --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts @@ -0,0 +1,55 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type KinematicViscosityMetricUnits = 'm²/s' | 'cm²/s' | 'St' | 'cSt'; +export type KinematicViscosityImperialUnits = 'ft²/s' | 'in²/s'; + +export type KinematicViscosityUnits = KinematicViscosityMetricUnits | KinematicViscosityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 10.7639104167097, + units: { + 'm²/s': { + name: 'unit.square-meter-per-second', + tags: ['kinematic viscosity', 'm²/s'], + to_anchor: 1, + }, + 'cm²/s': { + name: 'unit.square-centimeter-per-second', + tags: ['kinematic viscosity', 'cm²/s'], + to_anchor: 1e-4, + }, + 'St': { + name: 'unit.stoke', + tags: ['kinematic viscosity', 'stokes', 'St'], + to_anchor: 1e-4, // St to m²/s + }, + 'cSt': { + name: 'unit.centistokes', + tags: ['kinematic viscosity', 'centistokes', 'cSt'], + to_anchor: 1e-6, // cSt to m²/s + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 0.09290304, + units: { + 'ft²/s': { + name: 'unit.square-foot-per-second', + tags: ['kinematic viscosity', 'ft²/s'], + to_anchor: 0.09290304, + }, + 'in²/s': { + name: 'unit.square-inch-per-second', + tags: ['kinematic viscosity', 'in²/s'], + to_anchor: 0.00064516, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/light-exposure.ts b/ui-ngx/src/app/shared/models/units/light-exposure.ts new file mode 100644 index 0000000000..353cf57a37 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/light-exposure.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LightExposureMetricUnits = 'lx·s'; + +export type LightExposureUnits = LightExposureMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'lx·s': { + name: 'unit.lux-second', + tags: ['light exposure', 'illuminance over time', 'lux-second', 'lx·s'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/liner-charge-density.ts b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts new file mode 100644 index 0000000000..4a2239b1d7 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LinerChargeDensityMetricUnits = 'C/m'; + +export type LinerChargeDensityUnits = LinerChargeDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m': { + name: 'unit.coulomb-per-meter', + tags: ['electric displacement field per length', 'coulomb per meter', 'C/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-units.ts b/ui-ngx/src/app/shared/models/units/logarithmic-units.ts new file mode 100644 index 0000000000..ccbfd9af8e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/logarithmic-units.ts @@ -0,0 +1,29 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LogarithmicUnits = 'dB' | 'B' | 'Np'; + +const METRIC: TbMeasureUnits = { + units: { + 'dB': { + name: 'unit.decibel', + tags: ['noise level', 'sound level', 'volume', 'acoustics', 'decibel', 'dB'], + to_anchor: 1, + }, + 'B': { + name: 'unit.bel', + tags: ['logarithmic unit', 'power ratio', 'intensity ratio', 'bel', 'B'], + to_anchor: 10, + }, + 'Np': { + name: 'unit.neper', + tags: ['logarithmic unit', 'ratio', 'gain', 'loss', 'attenuation', 'neper', 'Np'], + to_anchor: 8.685889638, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts new file mode 100644 index 0000000000..1aa5c496fb --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LuminousEfficacyMetricUnits = 'lm/W'; + +export type LuminousEfficacyUnits = LuminousEfficacyMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'lm/W': { + name: 'unit.lumens-per-watt', + tags: ['luminous efficacy', 'lighting efficiency', 'lumens per watt', 'lm/W'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-flux.ts b/ui-ngx/src/app/shared/models/units/luminous-flux.ts new file mode 100644 index 0000000000..ff1b052919 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/luminous-flux.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LuminousFluxMetricUnits = 'lm'; + +export type LuminousFluxUnits = LuminousFluxMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'lm': { + name: 'unit.lumen', + tags: ['luminous flux', 'total light output', 'lumen', 'lm'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts new file mode 100644 index 0000000000..7168aab088 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LuminousIntensityMetricUnits = 'cd'; + +export type LuminousIntensityUnits = LuminousIntensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'cd': { + name: 'unit.candela', + tags: ['luminous intensity', 'light intensity', 'candela', 'cd'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts new file mode 100644 index 0000000000..41e8fade91 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts @@ -0,0 +1,25 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFieldGradientUnits = MagneticFieldGradientMetricUnits; +export type MagneticFieldGradientMetricUnits = 'T/m' | 'G/cm'; + +const METRIC: TbMeasureUnits = { + units: { + 'T/m': { + name: 'unit.tesla-per-meter', + tags: ['magnetic field', 'tesla per meter', 'T/m'], + to_anchor: 1, + }, + 'G/cm': { + name: 'unit.gauss-per-centimeter', + tags: ['magnetic field', 'gauss per centimeter', 'G/cm'], + to_anchor: 0.01, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field.ts b/ui-ngx/src/app/shared/models/units/magnetic-field.ts new file mode 100644 index 0000000000..8996c7911b --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-field.ts @@ -0,0 +1,71 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFieldMetricUnits = 'T' | 'mT' | 'μT' | 'nT' | 'kT' | 'MT' | 'G' | 'kG' | 'γ' | 'A/m' | 'Oe'; + +export type MagneticFieldUnits = MagneticFieldMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'T': { + name: 'unit.tesla', + tags: ['magnetic field', 'magnetic field strength', 'tesla', 'T', 'magnetic flux density'], + to_anchor: 1, + }, + 'mT': { + name: 'unit.millitesla', + tags: ['magnetic field', 'magnetic field strength', 'millitesla', 'mT'], + to_anchor: 0.001, + }, + 'μT': { + name: 'unit.microtesla', + tags: ['magnetic field', 'magnetic field strength', 'microtesla', 'μT'], + to_anchor: 0.000001, + }, + 'nT': { + name: 'unit.nanotesla', + tags: ['magnetic field', 'magnetic field strength', 'nanotesla', 'nT'], + to_anchor: 0.000000001, + }, + 'kT': { + name: 'unit.kilotesla', + tags: ['magnetic field', 'magnetic field strength', 'kilotesla', 'kT'], + to_anchor: 1000, + }, + 'MT': { + name: 'unit.megatesla', + tags: ['magnetic field', 'magnetic field strength', 'megatesla', 'MT'], + to_anchor: 1000000, + }, + 'G': { + name: 'unit.gauss', + tags: ['magnetic field', 'magnetic field strength', 'gauss', 'G', 'magnetic flux density'], + to_anchor: 0.0001, + }, + 'kG': { + name: 'unit.kilogauss', + tags: ['magnetic field', 'magnetic field strength', 'kilogauss', 'kG', 'magnetic flux density'], + to_anchor: 0.1, + }, + 'γ': { + name: 'unit.gamma', + tags: ['magnetic flux density', 'gamma', 'γ'], + to_anchor: 0.000000001, + }, + 'A/m': { + name: 'unit.ampere-per-meter', + tags: ['magnetic field strength', 'magnetic field intensity', 'ampere per meter', 'A/m'], + to_anchor: 0.00000125663706143591, + }, + 'Oe': { + name: 'unit.oersted', + tags: ['magnetic field', 'oersted', 'Oe'], + to_anchor: 0.0001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-flux.ts b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts new file mode 100644 index 0000000000..467d317d7a --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts @@ -0,0 +1,44 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFluxUnits = 'Wb' | 'µWb' | 'mWb' | 'Mx' | 'G·cm²' | 'kG·cm²'; + +const METRIC: TbMeasureUnits = { + units: { + 'Wb': { + name: 'unit.weber', + tags: ['magnetic flux', 'weber', 'Wb'], + to_anchor: 1, + }, + 'µWb': { + name: 'unit.microweber', + tags: ['magnetic flux', 'microweber', 'µWb'], + to_anchor: 1e-6, + }, + 'mWb': { + name: 'unit.milliweber', + tags: ['magnetic flux', 'milliweber', 'mWb'], + to_anchor: 1e-3, + }, + 'Mx': { + name: 'unit.maxwell', + tags: ['magnetic flux', 'magnetic field', 'maxwell', 'Mx'], + to_anchor: 1e-8, + }, + 'G·cm²': { + name: 'unit.gauss-square-centimeter', + tags: ['magnetic flux', 'gauss-square centimeter', 'G·cm²'], + to_anchor: 1e-8, + }, + 'kG·cm²': { + name: 'unit.kilogauss-square-centimeter', + tags: ['magnetic flux', 'kilogauss-square centimeter', 'kG·cm²'], + to_anchor: 1e-5, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-moment.ts b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts new file mode 100644 index 0000000000..d7771bdf33 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts @@ -0,0 +1,26 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticMomentMetricUnits = 'A·m²' | 'μB'; + +export type MagneticMomentUnits = MagneticMomentMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'A·m²': { + name: 'unit.magnetic-dipole-moment', + tags: ['magnetic dipole', 'dipole moment', 'ampere square meter', 'A·m²'], + to_anchor: 1, + }, + 'μB': { + name: 'unit.bohr-magneton', + tags: ['atomic physics', 'magnetic moment', 'bohr magneton', 'μB'], + to_anchor: 9.274e-24, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-energy.ts b/ui-ngx/src/app/shared/models/units/molar-energy.ts new file mode 100644 index 0000000000..41e7b5a75e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-energy.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarEnergyMetricUnits = 'J/mol'; + +export type MolarEnergyUnits = MolarEnergyMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/mol': { + name: 'unit.joule-per-mole', + tags: ['molar energy', 'joule per mole', 'J/mol'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts new file mode 100644 index 0000000000..104a44b3de --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarHeatCapacityMetricUnits = 'J/(mol·K)'; + +export type MolarHeatCapacityUnits = MolarHeatCapacityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/(mol·K)': { + name: 'unit.joule-per-mole-kelvin', + tags: ['molar heat capacity', 'joule per mole-kelvin', 'J/(mol·K)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-mass.ts b/ui-ngx/src/app/shared/models/units/molar-mass.ts new file mode 100644 index 0000000000..c9a6b89639 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-mass.ts @@ -0,0 +1,29 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarMassUnits = 'kg/mol' | 'g/mol' | 'mg/mol'; + +const METRIC: TbMeasureUnits = { + units: { + 'g/mol': { + name: 'unit.gram-per-mole', + tags: ['molar mass', 'gram per mole', 'g/mol'], + to_anchor: 1, + }, + 'kg/mol': { + name: 'unit.kilogram-per-mole', + tags: ['molar mass', 'kilogram per mole', 'kg/mol'], + to_anchor: 1e3, + }, + 'mg/mol': { + name: 'unit.milligram-per-mole', + tags: ['molar mass', 'milligram per mole', 'mg/mol'], + to_anchor: 1e-3, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/particle-concentration.ts b/ui-ngx/src/app/shared/models/units/particle-concentration.ts new file mode 100644 index 0000000000..991d5a9f78 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/particle-concentration.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ParticleConcentrationUnits = 'particles/mL'; + +const METRIC: TbMeasureUnits = { + units: { + 'particles/mL': { + name: 'unit.particle-density', + tags: ['particle concentration', 'count', 'particles/mL'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/percentage.ts b/ui-ngx/src/app/shared/models/units/percentage.ts new file mode 100644 index 0000000000..1bd796b0f5 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/percentage.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PercentageMetricUnits = '%'; +export type PercentageUnits = PercentageMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + '%': { + name: 'unit.percent', + tags: ['power source', 'state of charge (SoC)', 'battery', 'battery level', 'level', 'humidity', 'moisture', 'percentage', 'relative humidity', 'water content', 'soil moisture', 'irrigation', 'water in soil', 'soil water content', 'VWC', 'Volumetric Water Content', 'Total Harmonic Distortion', 'THD', 'power quality', 'UV Transmittance', '%', 'capacity'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/ph.ts b/ui-ngx/src/app/shared/models/units/ph.ts new file mode 100644 index 0000000000..d394d0f8ec --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/ph.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PHUnits = 'pH'; + +const METRIC: TbMeasureUnits = { + units: { + pH: { + name: 'unit.ph-level', + tags: ['acidity', 'alkalinity', 'neutral', 'acid', 'base', 'pH', 'soil pH', 'water quality', 'water pH'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/polarization.ts b/ui-ngx/src/app/shared/models/units/polarization.ts new file mode 100644 index 0000000000..09528228a0 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/polarization.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PolarizationMetricUnits = 'C·m²/V'; + +export type PolarizationUnits = PolarizationMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m²/V': { + name: 'unit.coulomb-per-square-meter-per-volt', + tags: ['polarization', 'electric field', 'coulomb per square meter per volt', 'C·m²/V'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/power-density.ts b/ui-ngx/src/app/shared/models/units/power-density.ts new file mode 100644 index 0000000000..38ec33fd6e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/power-density.ts @@ -0,0 +1,65 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type PowerDensityMetricUnits = 'mW/cm²' | 'W/cm²' | 'kW/cm²' | 'mW/m²' | 'W/m²' | 'kW/m²'; +export type PowerDensityImperialUnits = 'W/in²' | 'kW/in²'; + +export type PowerDensityUnits = PowerDensityMetricUnits | PowerDensityImperialUnits; + +const METRIC: TbMeasureUnits = { + ratio: 0.00064516, + units: { + 'mW/cm²': { + name: 'unit.milliwatt-per-square-centimeter', + tags: ['power density', 'radiation intensity', 'sunlight intensity', 'signal power', 'intensity', 'milliwatts per square centimeter', 'UV Intensity', 'mW/cm²'], + to_anchor: 10000, + }, + 'W/cm²': { + name: 'unit.watt-per-square-centimeter', + tags: ['power density', 'intensity of power', 'watts per square centimeter', 'W/cm²'], + to_anchor: 10000, + }, + 'kW/cm²': { + name: 'unit.kilowatt-per-square-centimeter', + tags: ['power density', 'intensity of power', 'kilowatts per square centimeter', 'kW/cm²'], + to_anchor: 10000000, + }, + 'mW/m²': { + name: 'unit.milliwatt-per-square-meter', + tags: ['power density', 'intensity of power', 'milliwatts per square meter', 'mW/m²'], + to_anchor: 0.001, + }, + 'W/m²': { + name: 'unit.watt-per-square-meter', + tags: ['power density', 'intensity of power', 'watts per square meter', 'W/m²'], + to_anchor: 1, + }, + 'kW/m²': { + name: 'unit.kilowatt-per-square-meter', + tags: ['power density', 'intensity of power', 'kilowatts per square meter', 'kW/m²'], + to_anchor: 1000, + }, + }, +}; + +const IMPERIAL: TbMeasureUnits = { + ratio: 1 / 0.00064516, + units: { + 'W/in²': { + name: 'unit.watt-per-square-inch', + tags: ['power density', 'intensity of power', 'watts per square inch', 'W/in²'], + to_anchor: 1, + }, + 'kW/in²': { + name: 'unit.kilowatt-per-square-inch', + tags: ['power density', 'intensity of power', 'kilowatts per square inch', 'kW/in²'], + to_anchor: 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, + IMPERIAL, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/pressure.ts b/ui-ngx/src/app/shared/models/units/pressure.ts index ad5c9b0dbb..7443763754 100644 --- a/ui-ngx/src/app/shared/models/units/pressure.ts +++ b/ui-ngx/src/app/shared/models/units/pressure.ts @@ -36,7 +36,9 @@ export type PressureMetricUnits = | 'N/m²' | 'kN/m²' | 'kgf/m²' - | 'Pa/cm²'; + | 'Pa/cm²' + | 'J/m³' + | 'kg/m²'; export type PressureImperialUnits = 'psi' | 'ksi' | 'inHg' | 'psi/in²' | 'tonf/in²'; @@ -46,7 +48,7 @@ const METRIC: TbMeasureUnits = { Pa: { name: 'unit.pascal', tags: ['pressure', 'force', 'compression', 'tension', 'pascal', 'pascals', 'Pa', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], - to_anchor: 0.001, // 1 Pa = 0.001 kPa + to_anchor: 0.001, }, kPa: { name: 'unit.kilopascal', @@ -133,6 +135,16 @@ const METRIC: TbMeasureUnits = { tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square centimeter', 'Pa/cm²'], to_anchor: 0.1, }, + 'J/m³': { + name: 'unit.joule-per-cubic-meter', + tags: ['energy density', 'joule per cubic meter', 'J/m³'], + to_anchor: 0.001, + }, + 'kg/m²': { + name: 'unit.kilogram-per-square-meter', + tags: ['density','surface density','areal density','mass per unit area','kg/m²'], + to_anchor: 0.00980665 + } }, }; diff --git a/ui-ngx/src/app/shared/models/units/radiance.ts b/ui-ngx/src/app/shared/models/units/radiance.ts new file mode 100644 index 0000000000..38fb646fab --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radiance.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadianceUnits = 'W/(m²·sr)'; + +const METRIC: TbMeasureUnits = { + units: { + 'W/(m²·sr)': { + name: 'unit.watt-per-square-metre-steradian', + tags: ['radiance', 'radiant flux density', 'wTape per square metre-steradian', 'W/(m²·sr)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radiant-intensity.ts b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts new file mode 100644 index 0000000000..78ee45ee5d --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadiantIntensityUnits = RadiantIntensityMetricUnits; +export type RadiantIntensityMetricUnits = 'W/sr'; + +const METRIC: TbMeasureUnits = { + units: { + 'W/sr': { + name: 'unit.watt-per-steradian', + tags: ['radiant intensity', 'power per unit solid angle', 'watt per steradian', 'W/sr'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radiation-dose.ts b/ui-ngx/src/app/shared/models/units/radiation-dose.ts new file mode 100644 index 0000000000..1619a48780 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radiation-dose.ts @@ -0,0 +1,50 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadiationDoseMetricUnits = 'Gy' | 'Sv' | 'Rad' | 'Rem' | 'R' | 'C/kg' | 'Gy/s'; +export type RadiationDoseUnits = RadiationDoseMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Sv': { + name: 'unit.sievert', + tags: ['radiation dose', 'sievert', 'radiation dose equivalent', 'Sv'], + to_anchor: 1, + }, + 'Gy': { + name: 'unit.gray', + tags: ['radiation dose', 'absorbed dose', 'gray', 'Gy'], + to_anchor: 1, + }, + 'Rad': { + name: 'unit.rad', + tags: ['radiation dose', 'rad'], + to_anchor: 0.01, + }, + 'Rem': { + name: 'unit.rem', + tags: ['radiation dose equivalent', 'rem'], + to_anchor: 0.01, + }, + 'R': { + name: 'unit.roentgen', + tags: ['radiation exposure', 'roentgen', 'R'], + to_anchor: 0.0093, + }, + 'C/kg': { + name: 'unit.coulombs-per-kilogram', + tags: ['radiation exposure', 'dose', 'coulombs per kilogram', 'electric charge-to-mass ratio', 'C/kg'], + to_anchor: 34, + }, + 'Gy/s': { + name: 'unit.gy-per-second', + tags: ['absorbed dose rate', 'radiation dose rate', 'gray per second', 'Gy/s'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts b/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts new file mode 100644 index 0000000000..d357c11b72 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts @@ -0,0 +1,25 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactiveDecayRateUnits = 'Bq/s' | 'Ci/s'; + +const METRIC: TbMeasureUnits = { + transform: (Bq_s) => Bq_s / 3.7e10, // Convert Bq/s to Ci/s + units: { + 'Bq/s': { + name: 'unit.becquerels-per-second', + tags: ['radioactive decay rate', 'becquerels per second', 'Bq/s'], + to_anchor: 1, + }, + 'Ci/s': { + name: 'unit.curies-per-second', + tags: ['radioactive decay rate', 'curies per second', 'Ci/s'], + to_anchor: 3.7e10, + }, + } +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts new file mode 100644 index 0000000000..8036332f13 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts @@ -0,0 +1,25 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactivityConcentrationMetricUnits = 'Bq/m³' | 'Ci/L'; +export type RadioactivityConcentrationUnits = RadioactivityConcentrationMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Bq/m³': { + name: 'unit.becquerels-per-cubic-meter', + tags: ['radioactivity', 'radiation', 'becquerels per cubic meter', 'Bq/m³'], + to_anchor: 1, + }, + 'Ci/L': { + name: 'unit.curies-per-liter', + tags: ['radioactivity', 'radiation', 'curies per liter', 'Ci/L'], + to_anchor: 3.7e10 * 1000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactivity.ts b/ui-ngx/src/app/shared/models/units/radioactivity.ts new file mode 100644 index 0000000000..8db76da7af --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactivity.ts @@ -0,0 +1,40 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactivityMetricUnits = 'Bq' | 'Ci' | 'Rd' | 'dps' | 'cps'; +export type RadioactivityUnits = RadioactivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Bq': { + name: 'unit.becquerel', + tags: ['radioactivity', 'decay rate', 'becquerel', 'Bq'], + to_anchor: 1, + }, + 'Ci': { + name: 'unit.curie', + tags: ['radioactivity', 'radiation', 'curie', 'Ci'], + to_anchor: 3.7e10, + }, + 'Rd': { + name: 'unit.rutherford', + tags: ['radioactive decay', 'radioactivity', 'rutherford', 'Rd'], + to_anchor: 1e6, + }, + 'dps': { + name: 'unit.dps', + tags: ['radioactive decay', 'radioactivity', 'disintegrations per second', 'dps'], + to_anchor: 1, + }, + cps: { + name: 'unit.cps', + tags: ['radiation detection', 'counts per second', 'cps'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/resistance.ts b/ui-ngx/src/app/shared/models/units/resistance.ts new file mode 100644 index 0000000000..6f14faf4c4 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/resistance.ts @@ -0,0 +1,46 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ResistanceMetricUnits = 'Ω' | 'μΩ' | 'mΩ' | 'kΩ' | 'MΩ' | 'GΩ'; + +export type ResistanceUnits = ResistanceMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'Ω': { + name: 'unit.ohm', + tags: ['electrical resistance', 'resistance', 'impedance', 'ohm'], + to_anchor: 1, + }, + 'μΩ': { + name: 'unit.microohm', + tags: ['electrical resistance', 'resistance', 'microohm', 'μΩ'], + to_anchor: 0.000001, + }, + 'mΩ': { + name: 'unit.milliohm', + tags: ['electrical resistance', 'resistance', 'milliohm', 'mΩ'], + to_anchor: 0.001, + }, + 'kΩ': { + name: 'unit.kilohm', + tags: ['electrical resistance', 'resistance', 'kilohm', 'kΩ'], + to_anchor: 1000, + }, + 'MΩ': { + name: 'unit.megohm', + tags: ['electrical resistance', 'resistance', 'megohm', 'MΩ'], + to_anchor: 1000000, + }, + 'GΩ': { + name: 'unit.gigohm', + tags: ['electrical resistance', 'resistance', 'gigohm', 'GΩ'], + to_anchor: 1000000000, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/reynolds-number.ts b/ui-ngx/src/app/shared/models/units/reynolds-number.ts new file mode 100644 index 0000000000..a9e8b8c01e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/reynolds-number.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ReynoldsNumberMetricUnits = 'Re'; + +export type ReynoldsNumberUnits = ReynoldsNumberMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + Re: { + name: 'unit.reynolds', + tags: ['fluid flow regime', 'fluid mechanics', 'reynolds', 'Re'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/signal-strength.ts b/ui-ngx/src/app/shared/models/units/signal-strength.ts new file mode 100644 index 0000000000..ef6a595ea3 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/signal-strength.ts @@ -0,0 +1,31 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SignalStrengthMetricUnits = 'dBmV' | 'dBm' | 'rssi'; + +export type SignalStrengthUnits = SignalStrengthMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'dBmV': { + name: 'unit.dbmV', + tags: ['decibels millivolt', 'voltage level', 'signal', 'dBmV'], + to_anchor: 1, + }, + 'dBm': { + name: 'unit.dbm', + tags: ['decibel milliwatts', 'output power', 'signal', 'dBm'], + to_anchor: 1, + }, + 'rssi': { + name: 'unit.rssi', + tags: ['signal strength', 'signal level', 'received signal strength indicator', 'rssi', 'dBm'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/solid-angle.ts b/ui-ngx/src/app/shared/models/units/solid-angle.ts new file mode 100644 index 0000000000..c339884b65 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/solid-angle.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SolidAngleMetricUnits = 'sr'; + +export type SolidAngleUnits = SolidAngleMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'sr': { + name: 'unit.steradian', + tags: ['solid angle', 'spatial extent', 'steradian', 'sr'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/specific-energy.ts b/ui-ngx/src/app/shared/models/units/specific-energy.ts new file mode 100644 index 0000000000..3ca9d44221 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-energy.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificEnergyMetricUnits = 'J/kg'; + +export type SpecificEnergyUnits = SpecificEnergyMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/kg': { + name: 'unit.joule-per-kilogram', + tags: ['specific energy', 'specific energy capacity', 'joule per kilogram', 'J/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts new file mode 100644 index 0000000000..9bdf38e80c --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificHeatCapacityMetricUnits = 'J/(kg·K)'; + +export type SpecificHeatCapacityUnits = SpecificHeatCapacityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'J/(kg·K)': { + name: 'unit.joule-per-kilogram-kelvin', + tags: ['specific heat capacity', 'heat capacity per unit mass and temperature', 'joule per kilogram-kelvin', 'J/(kg·K)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/specific-volume.ts b/ui-ngx/src/app/shared/models/units/specific-volume.ts new file mode 100644 index 0000000000..9968454e41 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-volume.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificVolumeMetricUnits = 'm³/kg'; + +export type SpecificVolumeUnits = SpecificVolumeMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'm³/kg': { + name: 'unit.cubic-meter-per-kilogram', + tags: ['specific volume', 'volume per unit mass', 'cubic meter per kilogram', 'm³/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/speed.ts b/ui-ngx/src/app/shared/models/units/speed.ts index 095bb0c759..f44553a7a7 100644 --- a/ui-ngx/src/app/shared/models/units/speed.ts +++ b/ui-ngx/src/app/shared/models/units/speed.ts @@ -18,7 +18,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type SpeedUnits = SpeedMetricUnits | SpeedImperialUnits; -export type SpeedMetricUnits = 'm/s' | 'km/h' | 'mm/min'; +export type SpeedMetricUnits = 'm/s' | 'km/h' | 'mm/min' | 'mm/s'; export type SpeedImperialUnits = 'mph' | 'kt' | 'ft/s' | 'ft/min' | 'in/h'; const METRIC: TbMeasureUnits = { @@ -39,6 +39,11 @@ const METRIC: TbMeasureUnits = { tags: ['feed rate', 'cutting feed rate', 'millimeters per minute', 'mm/min'], to_anchor: 0.06, }, + 'mm/s': { + name: 'unit.millimeters-per-second', + tags: ['velocity', 'speed', 'vibration rate', 'millimeters per second', 'mm/s'], + to_anchor: 0.0036, + }, }, }; diff --git a/ui-ngx/src/app/shared/models/units/sugar-content.ts b/ui-ngx/src/app/shared/models/units/sugar-content.ts new file mode 100644 index 0000000000..1badaf3ce0 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/sugar-content.ts @@ -0,0 +1,19 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SugarContentUnits = '°Bx'; + +const METRIC: TbMeasureUnits = { + units: { + '°Bx': { + name: 'unit.degrees-brix', + tags: ['sugar content', 'fruit ripeness', 'Bx'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/surface-charge-density.ts b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts new file mode 100644 index 0000000000..306649db3c --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SurfaceChargeDensityMetricUnits = 'C/m²'; + +export type SurfaceChargeDensityUnits = SurfaceChargeDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m²': { + name: 'unit.coulomb-per-square-meter', + tags: ['electric surface charge density', 'coulomb per square meter', 'C/m²'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/surface-tension.ts b/ui-ngx/src/app/shared/models/units/surface-tension.ts new file mode 100644 index 0000000000..b1c5593354 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/surface-tension.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SurfaceTensionMetricUnits = 'N/m'; + +export type SurfaceTensionhUnits = SurfaceTensionMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'N/m': { + name: 'unit.newton-per-meter', + tags: ['linear density', 'force per unit length', 'newton per meter', 'N/m'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts new file mode 100644 index 0000000000..3c27727919 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ThermalConductivityMetricUnits = 'W/(m·K)'; + +export type ThermalConductivityUnits = ThermalConductivityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'W/(m·K)': { + name: 'unit.watt-per-meter-kelvin', + tags: ['thermal conductivity', 'watt per meter-kelvin', 'W/(m·K)'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/turbidity.ts b/ui-ngx/src/app/shared/models/units/turbidity.ts new file mode 100644 index 0000000000..92b1122437 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/turbidity.ts @@ -0,0 +1,20 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type TurbidityUnits = TurbidityMetricUnits; +export type TurbidityMetricUnits = 'NTU'; + +const METRIC: TbMeasureUnits = { + units: { + NTU: { + name: 'unit.turbidity', + tags: ['water turbidity', 'water clarity', 'Nephelometric Turbidity Units', 'NTU'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/volume-charge-density.ts b/ui-ngx/src/app/shared/models/units/volume-charge-density.ts new file mode 100644 index 0000000000..0b172962fd --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/volume-charge-density.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type VolumeChargeDensityMetricUnits = 'C/m³'; + +export type VolumeChargeDensityUnits = VolumeChargeDensityMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m³': { + name: 'unit.coulomb-per-cubic-meter', + tags: ['electric charge density', 'coulomb per cubic meter', 'C/m³'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/wavenumber.ts b/ui-ngx/src/app/shared/models/units/wavenumber.ts new file mode 100644 index 0000000000..6745601328 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/wavenumber.ts @@ -0,0 +1,21 @@ +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type WavenumberMetricUnits = 'm⁻¹'; + +export type WavenumberUnits = WavenumberMetricUnits; + +const METRIC: TbMeasureUnits = { + units: { + 'm⁻¹': { + name: 'unit.reciprocal-metre', + tags: ['wavenumber', 'wave density', 'wave frequency', 'm⁻¹'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3001671891..c685b6171e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6074,7 +6074,6 @@ "oersted": "Oersted", "bohr-magneton": "Bohr Magneton", "ampere-meter-squared": "Ampere-Meter Squared", - "ampere-meter": "Ampere-Meter", "nanovolt": "Nanovolt", "picovolt": "Picovolt", "millivolt": "Millivolts", @@ -6112,7 +6111,6 @@ "lux-second": "Lux second", "lumen-second": "Lumen second", "lumens-per-watt": "Lumens per watt", - "absorbance": "Absorbance", "mole": "Mole", "nanomole": "Nanomole", "micromole": "MicroMole", From 43488081fc3ea35a394f4ac0b5e3acac978a1bd0 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 5 May 2025 13:46:29 +0300 Subject: [PATCH 13/27] UI: Updated unit definition --- ui-ngx/src/app/shared/models/unit.models.ts | 157 +++++++++++++++++- .../models/units/Magnetic-permeability.ts | 37 ----- .../app/shared/models/units/acceleration.ts | 8 +- .../shared/models/units/air-quality-index.ts | 18 +- .../models/units/amount-of-substance.ts | 32 +++- ui-ngx/src/app/shared/models/units/angle.ts | 20 +-- .../models/units/angular-acceleration.ts | 4 +- ui-ngx/src/app/shared/models/units/area.ts | 26 +-- .../app/shared/models/units/blood-glucose.ts | 20 --- .../app/shared/models/units/capacitance.ts | 34 +++- .../shared/models/units/catalytic-activity.ts | 24 ++- .../models/units/catalytic-concentration.ts | 35 ++++ ui-ngx/src/app/shared/models/units/charge.ts | 16 +- .../models/units/concentration-gradient.ts | 40 ----- .../models/units/concentration-volume.ts | 63 ------- .../shared/models/units/current-density.ts | 26 ++- .../shared/models/units/data-transfer-rate.ts | 25 ++- ui-ngx/src/app/shared/models/units/density.ts | 62 ++++++- ui-ngx/src/app/shared/models/units/digital.ts | 26 ++- .../shared/models/units/dimension-ratio.ts | 35 ++++ .../models/units/dimensionless-ratio.ts | 21 --- .../shared/models/units/dynamic-viscosity.ts | 30 +++- .../models/units/earthquake-magnitude.ts | 18 +- .../shared/models/units/electric-current.ts | 22 ++- .../models/units/electric-dipole-moment.ts | 39 +++++ .../models/units/electric-field-strength.ts | 19 ++- .../app/shared/models/units/electric-flux.ts | 28 ++-- .../shared/models/units/electric-moment.ts | 26 --- .../models/units/electric-permittivity.ts | 23 ++- .../models/units/electrical-conductance.ts | 22 ++- .../models/units/electrical-conductivity.ts | 27 ++- ui-ngx/src/app/shared/models/units/energy.ts | 26 +-- ui-ngx/src/app/shared/models/units/force.ts | 9 +- .../src/app/shared/models/units/frequency.ts | 25 ++- .../shared/models/units/fuel-efficiency.ts | 20 ++- .../app/shared/models/units/heat-capacity.ts | 26 ++- .../src/app/shared/models/units/humidity.ts | 20 --- .../app/shared/models/units/illuminance.ts | 8 +- .../src/app/shared/models/units/inductance.ts | 16 ++ .../models/units/kinematic-viscosity.ts | 26 ++- ui-ngx/src/app/shared/models/units/length.ts | 50 +++--- .../app/shared/models/units/light-exposure.ts | 24 ++- .../models/units/liner-charge-density.ts | 24 ++- .../shared/models/units/logarithmic-ratio.ts | 45 +++++ .../shared/models/units/logarithmic-units.ts | 29 ---- .../shared/models/units/luminous-efficacy.ts | 24 ++- .../app/shared/models/units/luminous-flux.ts | 24 ++- .../shared/models/units/luminous-intensity.ts | 24 ++- .../models/units/magnetic-field-gradient.ts | 23 ++- .../app/shared/models/units/magnetic-field.ts | 71 -------- .../models/units/magnetic-flux-density.ts | 84 ++++++++++ .../app/shared/models/units/magnetic-flux.ts | 31 ++-- .../shared/models/units/magnetic-moment.ts | 28 +++- .../models/units/magnetic-permeability.ts | 40 +++++ .../app/shared/models/units/mass-fraction.ts | 35 ++++ ui-ngx/src/app/shared/models/units/mass.ts | 32 ++-- .../models/units/molar-concentration.ts | 35 ++++ .../app/shared/models/units/molar-energy.ts | 23 ++- .../models/units/molar-heat-capacity.ts | 23 ++- .../src/app/shared/models/units/molar-mass.ts | 19 ++- .../shared/models/units/specific-humidity.ts | 35 ++++ .../assets/locale/locale.constant-en_US.json | 36 ++++ 62 files changed, 1280 insertions(+), 638 deletions(-) delete mode 100644 ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts delete mode 100644 ui-ngx/src/app/shared/models/units/blood-glucose.ts create mode 100644 ui-ngx/src/app/shared/models/units/catalytic-concentration.ts delete mode 100644 ui-ngx/src/app/shared/models/units/concentration-gradient.ts delete mode 100644 ui-ngx/src/app/shared/models/units/concentration-volume.ts create mode 100644 ui-ngx/src/app/shared/models/units/dimension-ratio.ts delete mode 100644 ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts delete mode 100644 ui-ngx/src/app/shared/models/units/electric-moment.ts delete mode 100644 ui-ngx/src/app/shared/models/units/humidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts delete mode 100644 ui-ngx/src/app/shared/models/units/logarithmic-units.ts delete mode 100644 ui-ngx/src/app/shared/models/units/magnetic-field.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/magnetic-permeability.ts create mode 100644 ui-ngx/src/app/shared/models/units/mass-fraction.ts create mode 100644 ui-ngx/src/app/shared/models/units/molar-concentration.ts create mode 100644 ui-ngx/src/app/shared/models/units/specific-humidity.ts diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 27cca5f603..c2763ff632 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -15,21 +15,59 @@ /// import acceleration, { AccelerationUnits } from '@shared/models/units/acceleration'; +import airQualityIndex, { AirQualityIndexUnits } from '@shared/models/units/air-quality-index'; +import amountOfSubstance, { AmountOfSubstanceUnits } from '@shared/models/units/amount-of-substance'; import angle, { AngleUnits } from '@shared/models/units/angle'; import angularAcceleration, { AngularAccelerationUnits } from '@shared/models/units/angular-acceleration'; import area, { AreaUnits } from '@shared/models/units/area'; +import capacitance, { CapacitanceUnits } from '@shared/models/units/capacitance'; +import catalyticActivity, { CatalyticActivityUnits } from '@shared/models/units/catalytic-activity'; +import catalyticConcentration, { CatalyticConcentrationUnits } from '@shared/models/units/catalytic-concentration'; import charge, { ChargeUnits } from '@shared/models/units/charge'; +import currentDensity, { CurrentDensityUnits } from '@shared/models/units/current-density'; +import dataTransferRate, { DataTransferRateUnits } from '@shared/models/units/data-transfer-rate'; +import density, { DensityUnits } from '@shared/models/units/density'; import digital, { DigitalUnits } from '@shared/models/units/digital'; +import dimensionRatio, { DimensionRatioUnits } from '@shared/models/units/dimension-ratio'; +import dynamicViscosity, { DynamicViscosityUnits } from '@shared/models/units/dynamic-viscosity'; +import earthquakeMagnitude, { EarthquakeMagnitudeUnits } from '@shared/models/units/earthquake-magnitude'; import electricCurrent, { ElectricCurrentUnits } from '@shared/models/units/electric-current'; +import electricDipoleMoment, { ElectricDipoleMomentUnits } from '@shared/models/units/electric-dipole-moment'; +import electricFieldStrength, { ElectricFieldStrengthUnits } from '@shared/models/units/electric-field-strength'; +import electricFlux, { ElectricFluxUnits } from '@shared/models/units/electric-flux'; +import electricPermittivity, { ElectricPermittivityUnits } from '@shared/models/units/electric-permittivity'; +import electricalConductance, { ElectricalConductanceUnits } from '@shared/models/units/electrical-conductance'; +import electricalConductivity, { ElectricalConductivityUnits } from '@shared/models/units/electrical-conductivity'; import energy, { EnergyUnits } from '@shared/models/units/energy'; import force, { ForceUnits } from '@shared/models/units/force'; +import fuelEfficiency, { FuelEfficiencyUnits } from '@shared/models/units/fuel-efficiency'; import frequency, { FrequencyUnits } from '@shared/models/units/frequency'; +import heatCapacity, { HeatCapacityUnits } from '@shared/models/units/heat-capacity'; import illuminance, { IlluminanceUnits } from '@shared/models/units/illuminance'; +import inductance, { InductanceUnits } from '@shared/models/units/inductance'; +import kinematicViscosity, { KinematicViscosityUnits } from '@shared/models/units/kinematic-viscosity'; import length, { LengthUnits } from '@shared/models/units/length'; +import lightExposure, { LightExposureUnits } from '@shared/models/units/light-exposure'; +import linerChargeDensity, { LinerChargeDensityUnits } from '@shared/models/units/liner-charge-density'; +import logarithmicRatio, { LogarithmicRatioUnits } from '@shared/models/units/logarithmic-ratio'; +import luminousEfficacy, { LuminousEfficacyUnits } from '@shared/models/units/luminous-efficacy'; +import luminousFlux, { LuminousFluxUnits } from '@shared/models/units/luminous-flux'; +import luminousIntensity, { LuminousIntensityUnits } from '@shared/models/units/luminous-intensity'; +import magneticFieldGradient, { MagneticFieldGradientUnits } from '@shared/models/units/magnetic-field-gradient'; +import magneticFlux, { MagneticFluxUnits } from '@shared/models/units/magnetic-flux'; +import magneticFluxDensity, { MagneticFluxDensityUnits } from '@shared/models/units/magnetic-flux-density'; +import magneticMoment, { MagneticMomentUnits } from '@shared/models/units/magnetic-moment'; +import magneticPermeability, { MagneticPermeabilityUnits } from '@shared/models/units/magnetic-permeability'; import mass, { MassUnits } from '@shared/models/units/mass'; +import massFraction, { MassFractionUnits } from '@shared/models/units/mass-fraction'; +import molarConcentration, { MolarConcentrationUnits } from '@shared/models/units/molar-concentration'; +import molarEnergy, { MolarEnergyUnits } from '@shared/models/units/molar-energy'; +import molarHeatCapacity, { MolarHeatCapacityUnits } from '@shared/models/units/molar-heat-capacity'; +import molarMass, { MolarMassUnits } from '@shared/models/units/molar-mass'; import partsPer, { PartsPerUnits } from '@shared/models/units/parts-per'; import power, { PowerUnits } from '@shared/models/units/power'; import pressure, { PressureUnits } from '@shared/models/units/pressure'; +import specificHumidity, { SpecificHumidityUnits } from '@shared/models/units/specific-humidity'; import speed, { SpeedUnits } from '@shared/models/units/speed'; import temperature, { TemperatureUnits } from '@shared/models/units/temperature'; import time, { TimeUnits } from '@shared/models/units/time'; @@ -41,21 +79,59 @@ import { TranslateService } from '@ngx-translate/core'; export type AllMeasuresUnits = | AccelerationUnits + | AirQualityIndexUnits + | AmountOfSubstanceUnits | AngleUnits | AngularAccelerationUnits | AreaUnits + | CapacitanceUnits + | CatalyticActivityUnits + | CatalyticConcentrationUnits | ChargeUnits + | CurrentDensityUnits + | DataTransferRateUnits + | DensityUnits | DigitalUnits + | DimensionRatioUnits + | DynamicViscosityUnits + | EarthquakeMagnitudeUnits | ElectricCurrentUnits + | ElectricDipoleMomentUnits + | ElectricFieldStrengthUnits + | ElectricFluxUnits + | ElectricPermittivityUnits + | ElectricalConductanceUnits + | ElectricalConductivityUnits | EnergyUnits | ForceUnits | FrequencyUnits + | FuelEfficiencyUnits + | HeatCapacityUnits | IlluminanceUnits + | InductanceUnits + | KinematicViscosityUnits | LengthUnits + | LightExposureUnits + | LinerChargeDensityUnits + | LogarithmicRatioUnits + | LuminousEfficacyUnits + | LuminousFluxUnits + | LuminousIntensityUnits + | MagneticFieldGradientUnits + | MagneticFluxUnits + | MagneticFluxDensityUnits + | MagneticMomentUnits + | MagneticPermeabilityUnits | MassUnits + | MassFractionUnits + | MolarConcentrationUnits + | MolarEnergyUnits + | MolarHeatCapacityUnits + | MolarMassUnits | PartsPerUnits | PowerUnits | PressureUnits + | SpecificHumidityUnits | SpeedUnits | TemperatureUnits | TimeUnits @@ -66,21 +142,59 @@ export type AllMeasuresUnits = export type AllMeasures = | 'acceleration' + | 'air-quality-index' + | 'amount-of-substance' | 'angle' | 'angular-acceleration' | 'area' + | 'capacitance' + | 'catalytic-activity' + | 'catalytic-concentration' | 'charge' + | 'current-density' + | 'data-transfer-rate' + | 'density' | 'digital' + | 'dimension-ratio' + | 'dynamic-viscosity' + | 'earthquake-magnitude' | 'electric-current' + | 'electric-dipole-moment' + | 'electric-field-strength' + | 'electric-flux' + | 'electric-permittivity' + | 'electrical-conductance' + | 'electrical-conductivity' | 'energy' | 'force' | 'frequency' + | 'fuel-efficiency' + | 'heat-capacity' | 'illuminance' + | 'inductance' + | 'kinematic-viscosity' | 'length' + | 'light-exposure' + | 'linear-charge-density' + | 'logarithmic-ratio' + | 'luminous-efficacy' + | 'luminous-flux' + | 'luminous-intensity' + | 'magnetic-field-gradient' + | 'magnetic-flux' + | 'magnetic-flux-density' + | 'magnetic-moment' + | 'magnetic-permeability' | 'mass' + | 'mass-fraction' + | 'molar-concentration' + | 'molar-energy' + | 'molar-heat-capacity' + | 'molar-mass' | 'parts-per' | 'power' | 'pressure' + | 'specific-humidity' | 'speed' | 'temperature' | 'time' @@ -94,21 +208,59 @@ const allMeasures: Record< TbMeasure > = Object.freeze({ acceleration, + 'air-quality-index': airQualityIndex, + 'amount-of-substance': amountOfSubstance, angle, 'angular-acceleration': angularAcceleration, area, + capacitance, + 'catalytic-activity': catalyticActivity, + 'catalytic-concentration': catalyticConcentration, charge, + 'current-density': currentDensity, + 'data-transfer-rate': dataTransferRate, + density, digital, + 'dimension-ratio': dimensionRatio, + 'dynamic-viscosity': dynamicViscosity, + 'earthquake-magnitude': earthquakeMagnitude, 'electric-current': electricCurrent, + 'electric-dipole-moment': electricDipoleMoment, + 'electric-field-strength': electricFieldStrength, + 'electric-flux': electricFlux, + 'electric-permittivity': electricPermittivity, + 'electrical-conductance': electricalConductance, + 'electrical-conductivity': electricalConductivity, energy, force, frequency, + 'fuel-efficiency': fuelEfficiency, + 'heat-capacity': heatCapacity, illuminance, + inductance, + 'kinematic-viscosity': kinematicViscosity, length, + 'light-exposure': lightExposure, + 'linear-charge-density': linerChargeDensity, + 'logarithmic-ratio': logarithmicRatio, + 'luminous-efficacy': luminousEfficacy, + 'luminous-flux': luminousFlux, + 'luminous-intensity': luminousIntensity, + 'magnetic-field-gradient': magneticFieldGradient, + 'magnetic-flux': magneticFlux, + 'magnetic-flux-density': magneticFluxDensity, + 'magnetic-moment': magneticMoment, + 'magnetic-permeability': magneticPermeability, mass, + 'mass-fraction': massFraction, + 'molar-concentration': molarConcentration, + 'molar-energy': molarEnergy, + 'molar-heat-capacity': molarHeatCapacity, + 'molar-mass': molarMass, 'parts-per': partsPer, power, pressure, + 'specific-humidity': specificHumidity, speed, temperature, time, @@ -143,7 +295,7 @@ export const UnitSystems = Object.values(UnitSystem); export interface Unit { name: string; - tags: string[]; + tags?: string[]; to_anchor: number; anchor_shift?: number; transform?: (value: number) => number; @@ -407,6 +559,9 @@ function buildUnitCache(measures: Record[]) { unit.name = translate.instant(unit.name); + const measureNameTranslation = translate.instant('unit.measures.' + measureName); + unit.tags = unit.tags ?? []; + unit.tags.push(testAbbr, unit.name, measureNameTranslation); unitCache.set(testAbbr, { measure: measureName, system: systemName, diff --git a/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts b/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts deleted file mode 100644 index be120c4b15..0000000000 --- a/ui-ngx/src/app/shared/models/units/Magnetic-permeability.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type MagneticPermeabilityMetricUnits = 'H/m'; -export type MagneticPermeabilityImperialUnits = 'G/Oe'; - -export type MagneticPermeabilityUnits = - | MagneticPermeabilityMetricUnits - | MagneticPermeabilityImperialUnits; - -const METRIC: TbMeasureUnits = { - transform: (Hm) => Hm * 795774.715, - units: { - 'H/m': { - name: 'unit.henry-per-meter', - tags: ['magnetic permeability', 'henry per meter', 'H/m'], - to_anchor: 1, - }, - }, -}; - -const IMPERIAL: TbMeasureUnits = { - transform: (GOe) => GOe / 795774.715, - units: { - 'G/Oe': { - name: 'unit.gauss-per-oersted', - tags: ['magnetic field', 'Gauss per Oersted', 'G/Oe'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, - IMPERIAL, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/acceleration.ts b/ui-ngx/src/app/shared/models/units/acceleration.ts index 1a39b0b814..a1643020a9 100644 --- a/ui-ngx/src/app/shared/models/units/acceleration.ts +++ b/ui-ngx/src/app/shared/models/units/acceleration.ts @@ -26,22 +26,22 @@ const METRIC: TbMeasureUnits = { units: { 'G': { name: 'unit.g-force', - tags: ['acceleration', 'gravity', 'g-force', 'load', 'G'], + tags: ['gravity', 'g-force', 'load'], to_anchor: 9.80665, }, 'm/s²': { name: 'unit.meters-per-second-squared', - tags: ['peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'meters per second squared', 'm/s²'], + tags: ['peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'meters per second squared'], to_anchor: 1, }, 'Gal': { name: 'unit.gal', - tags: ['acceleration', 'gravity', 'g-force', 'Gal'], + tags: ['gravity', 'g-force'], to_anchor: 1, }, 'km/h²': { name: 'unit.kilometer-per-hour-squared', - tags: ['acceleration', 'rate of change of velocity', 'kilometer per hour squared', 'km/h²'], + tags: ['rate of change of velocity', 'kilometer per hour squared'], to_anchor: 1 / 12960, } } diff --git a/ui-ngx/src/app/shared/models/units/air-quality-index.ts b/ui-ngx/src/app/shared/models/units/air-quality-index.ts index 907071b594..9047f86d94 100644 --- a/ui-ngx/src/app/shared/models/units/air-quality-index.ts +++ b/ui-ngx/src/app/shared/models/units/air-quality-index.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type AirQualityIndexUnits = 'aqi'; @@ -6,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { aqi: { name: 'unit.aqi', - tags: ['AQI', 'air quality index'], + tags: ['pollutant concentration'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts index 47f798c388..1601f5d80a 100644 --- a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts +++ b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts @@ -1,34 +1,48 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type AmountOfSubstanceMetricUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AmountOfSubstanceUnits = AmountOfSubstanceMetricUnits; +export type AmountOfSubstanceUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'mol': { name: 'unit.mole', - tags: ['amount of substance', 'chemical amount', 'mole', 'mol'], + tags: ['chemical amount'], to_anchor: 1, }, 'nmol': { name: 'unit.nanomole', - tags: ['amount of substance', 'nanomole', 'nmol'], + tags: ['chemical amount'], to_anchor: 0.000000001, }, 'μmol': { name: 'unit.micromole', - tags: ['amount of substance', 'micromole', 'μmol'], + tags: ['chemical amount'], to_anchor: 0.000001, }, 'mmol': { name: 'unit.millimole', - tags: ['amount of substance', 'millimole', 'mmol'], + tags: ['chemical amount'], to_anchor: 0.001, }, 'kmol': { name: 'unit.kilomole', - tags: ['amount of substance', 'kilomole', 'kmol'], + tags: ['chemical amount'], to_anchor: 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/angle.ts b/ui-ngx/src/app/shared/models/units/angle.ts index 7082e5a986..a92c4cb7a5 100644 --- a/ui-ngx/src/app/shared/models/units/angle.ts +++ b/ui-ngx/src/app/shared/models/units/angle.ts @@ -16,45 +16,43 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AngleUnits = AngleMetricUnits; +export type AngleUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mrad' | 'rev'; -export type AngleMetricUnits = 'rad' | 'deg' | 'grad' | 'arcmin' | 'arcsec' | 'mrad' | 'rev'; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { rad: { name: 'unit.rad', - tags: ['angle', 'radian', 'radians', 'rad'], + tags: ['radians'], to_anchor: 180 / Math.PI, }, deg: { name: 'unit.degree', - tags: ['angle', 'degree', 'degrees', 'deg'], + tags: ['degrees'], to_anchor: 1, }, grad: { name: 'unit.gradian', - tags: ['angle', 'gradian', 'grades', 'grad'], + tags: ['grades'], to_anchor: 9 / 10, }, arcmin: { name: 'unit.arcminute', - tags: ['angle', 'arcminute', 'arcminutes', 'arcmin'], + tags: ['arcminutes'], to_anchor: 1 / 60 }, arcsec: { name: 'unit.arcsecond', - tags: ['angle', 'arcsecond', 'arcseconds', 'arcsec'], + tags: ['arcseconds'], to_anchor: 1 / 3600 }, mrad: { name: 'unit.milliradian', - tags: ['angle', 'military angle', 'angular mil', 'mil'], + tags: ['military angle', 'angular mil', 'mil'], to_anchor: 9 / (50 * Math.PI), }, rev: { name: 'unit.revolution', - tags: ['angle', 'revolution', 'full circle', 'complete turn', 'rev'], + tags: ['full circle', 'complete turn'], to_anchor: 360, }, } diff --git a/ui-ngx/src/app/shared/models/units/angular-acceleration.ts b/ui-ngx/src/app/shared/models/units/angular-acceleration.ts index 4e5085d7bf..d61957ac27 100644 --- a/ui-ngx/src/app/shared/models/units/angular-acceleration.ts +++ b/ui-ngx/src/app/shared/models/units/angular-acceleration.ts @@ -26,7 +26,7 @@ const METRIC: TbMeasureUnits = { units: { 'rad/s²': { name: 'unit.radian-per-second-squared', - tags: ['angular acceleration', 'rotation rate of change', 'rad/s²'], + tags: ['rotation rate of change'], to_anchor: 1, } } @@ -37,7 +37,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'rpm/s': { name: 'unit.revolutions-per-minute-per-second', - tags: ['angular acceleration', 'rotation rate of change', 'rpm/s'], + tags: ['rotation rate of change'], to_anchor: 1 } } diff --git a/ui-ngx/src/app/shared/models/units/area.ts b/ui-ngx/src/app/shared/models/units/area.ts index 6c36d6d976..b1df9295e2 100644 --- a/ui-ngx/src/app/shared/models/units/area.ts +++ b/ui-ngx/src/app/shared/models/units/area.ts @@ -26,37 +26,37 @@ const METRIC: TbMeasureUnits = { units: { 'mm²': { name: 'unit.square-millimeter', - tags: ['area','lot','zone','space','region','square millimeter','square millimeters','mm²','sq-mm'], + tags: ['lot', 'zone', 'space', 'region', 'square millimeters', 'sq-mm'], to_anchor: 1 / 1000000, }, 'cm²': { name: 'unit.square-centimeter', - tags: ['area','lot','zone','space','region','square centimeter','square centimeters','cm²','sq-cm'], + tags: ['lot', 'zone', 'space', 'region', 'square centimeters', 'sq-cm'], to_anchor: 1 / 10000, }, 'm²': { name: 'unit.square-meter', - tags: ['area','lot','zone','space','region','square meter','square meters','m²','sq-m'], + tags: ['lot', 'zone', 'space', 'region', 'square meters', 'sq-m'], to_anchor: 1, }, a: { name: 'unit.are', - tags: ['area','land measurement','are'], + tags: ['land measurement'], to_anchor: 100, }, ha: { name: 'unit.hectare', - tags: ['area','lot','zone','space','region','hectare','hectares','ha'], + tags: ['lot', 'zone', 'space', 'region', 'hectares'], to_anchor: 10000, }, 'km²': { name: 'unit.square-kilometer', - tags: ['area','lot','zone','space','region','square kilometer','square kilometers','km²','sq-km'], + tags: ['lot', 'zone', 'space', 'region', 'square kilometers', 'sq-km'], to_anchor: 1000000, }, barn: { name: 'unit.barn', - tags: ['cross-sectional area', 'particle physics', 'nuclear physics', 'barn'], + tags: ['cross-sectional area', 'particle physics', 'nuclear physics'], to_anchor: 1e-28, }, } @@ -67,32 +67,32 @@ const IMPERIAL: TbMeasureUnits = { units: { 'in²': { name: 'unit.square-inch', - tags: ['area','lot','zone','space','region','square inch','square inches','in²','sq-in'], + tags: ['lot', 'zone', 'space', 'region', 'square inches', 'sq-in'], to_anchor: 1 / 144, }, 'yd²': { name: 'unit.square-yard', - tags: ['area','lot','zone','space','region','square yard','square yards','yd²','sq-yd'], + tags: ['lot', 'zone', 'space', 'region', 'square yards', 'sq-yd'], to_anchor: 9, }, 'ft²': { name: 'unit.square-foot', - tags: ['area','lot','zone','space','region','square foot','square feet','ft²','sq-ft'], + tags: ['lot', 'zone', 'space', 'region', 'square feet', 'sq-ft'], to_anchor: 1, }, ac: { name: 'unit.acre', - tags: ['area','lot','zone','space','region','acre','acres','a'], + tags: ['lot', 'zone', 'space', 'region', 'acres', 'a'], to_anchor: 43560, }, 'ml²': { name: 'unit.square-mile', - tags: ['area','lot','zone','space','region','square mile','square miles','ml²','sq-mi'], + tags: ['lot', 'zone', 'space', 'region', 'square mile', 'sq-mi'], to_anchor: 27878400, }, cin: { name: 'unit.circular-inch', - tags: ['area','circular measurement','circular inch','circin'], + tags: ['circular measurement', 'circin'], to_anchor: Math.PI / 576 } } diff --git a/ui-ngx/src/app/shared/models/units/blood-glucose.ts b/ui-ngx/src/app/shared/models/units/blood-glucose.ts deleted file mode 100644 index 18b0902591..0000000000 --- a/ui-ngx/src/app/shared/models/units/blood-glucose.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type BloodGlucoseUnits = BloodGlucoseMetricUnits; -export type BloodGlucoseMetricUnits = 'mg/dL'; - -const METRIC: TbMeasureUnits = { - units: { - 'mg/dL': { - name: 'unit.milligrams-per-deciliter', - tags: ['glucose', 'blood sugar', 'glucose level', 'mg/dL'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/capacitance.ts b/ui-ngx/src/app/shared/models/units/capacitance.ts index 4999afd536..f4dc13dc97 100644 --- a/ui-ngx/src/app/shared/models/units/capacitance.ts +++ b/ui-ngx/src/app/shared/models/units/capacitance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type CapacitanceUnits = 'F' | 'mF' | 'μF' | 'nF' | 'pF' | 'kF' | 'MF' | 'GF' | 'TF'; @@ -6,47 +22,47 @@ const METRIC: TbMeasureUnits = { units: { 'F': { name: 'unit.farad', - tags: ['electric capacitance', 'capacitance', 'farad', 'F'], + tags: ['electric capacitance'], to_anchor: 1, }, 'mF': { name: 'unit.millifarad', - tags: ['electric capacitance', 'capacitance', 'millifarad', 'mF'], + tags: ['electric capacitance'], to_anchor: 1e-3, }, 'μF': { name: 'unit.microfarad', - tags: ['electric capacitance', 'capacitance', 'microfarad', 'μF'], + tags: ['electric capacitance'], to_anchor: 1e-6, }, 'nF': { name: 'unit.nanofarad', - tags: ['electric capacitance', 'capacitance', 'nanofarad', 'nF'], + tags: ['electric capacitance'], to_anchor: 1e-9, }, 'pF': { name: 'unit.picofarad', - tags: ['electric capacitance', 'capacitance', 'picofarad', 'pF'], + tags: ['electric capacitance'], to_anchor: 1e-12, }, 'kF': { name: 'unit.kilofarad', - tags: ['electric capacitance', 'capacitance', 'kilofarad', 'kF'], + tags: ['electric capacitance'], to_anchor: 1e3, }, 'MF': { name: 'unit.megafarad', - tags: ['electric capacitance', 'capacitance', 'megafarad', 'MF'], + tags: ['electric capacitance'], to_anchor: 1e6, }, 'GF': { name: 'unit.gigafarad', - tags: ['electric capacitance', 'capacitance', 'gigafarad', 'GF'], + tags: ['electric capacitance'], to_anchor: 1e9, }, 'TF': { name: 'unit.terafarad', - tags: ['electric capacitance', 'capacitance', 'terafarad', 'TF'], + tags: ['electric capacitance'], to_anchor: 1e12, }, }, diff --git a/ui-ngx/src/app/shared/models/units/catalytic-activity.ts b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts index 7198bf4d9c..c0dc61c2f5 100644 --- a/ui-ngx/src/app/shared/models/units/catalytic-activity.ts +++ b/ui-ngx/src/app/shared/models/units/catalytic-activity.ts @@ -1,18 +1,34 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type AirQualityIndexUnits = 'kat'; +export type CatalyticActivityUnits = 'kat'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { kat: { name: 'unit.katal', - tags: ['catalytic activity', 'enzyme activity', 'kat'], + tags: ['catalytic activity', 'enzyme activity'], to_anchor: 1, }, }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, }; diff --git a/ui-ngx/src/app/shared/models/units/catalytic-concentration.ts b/ui-ngx/src/app/shared/models/units/catalytic-concentration.ts new file mode 100644 index 0000000000..805f4cfc40 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/catalytic-concentration.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type CatalyticConcentrationUnits = 'kat/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'kat/m³': { + name: 'unit.katal-per-cubic-metre', + tags: ['enzyme concentration'], + to_anchor: 1, + } + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/charge.ts b/ui-ngx/src/app/shared/models/units/charge.ts index b06a0223d5..2abce39f18 100644 --- a/ui-ngx/src/app/shared/models/units/charge.ts +++ b/ui-ngx/src/app/shared/models/units/charge.ts @@ -24,42 +24,42 @@ const METRIC: TbMeasureUnits = { units: { c: { name: 'unit.coulomb', - tags: ['charge', 'electricity', 'electrostatics', 'Coulomb', 'C'], + tags: ['electricity', 'electrostatics'], to_anchor: 1, }, mC: { name: 'unit.millicoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'millicoulombs', 'mC'], + tags: ['electricity', 'electrostatics'], to_anchor: 1 / 1000, }, μC: { name: 'unit.microcoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'microcoulomb', 'µC'], + tags: [ 'electricity', 'electrostatics'], to_anchor: 1 / 1000000, }, nC: { name: 'unit.nanocoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'nanocoulomb', 'nC'], + tags: ['electricity', 'electrostatics',], to_anchor: 1e-9, }, pC: { name: 'unit.picocoulomb', - tags: ['charge', 'electricity', 'electrostatics', 'picocoulomb', 'pC'], + tags: ['electricity', 'electrostatics'], to_anchor: 1e-12, }, mAh: { name: 'unit.milliampere-hour', - tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'milliampere-hour', 'milliampere-hours', 'mAh'], + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'milliampere-hours'], to_anchor: 3.6, }, Ah: { name: 'unit.ampere-hours', - tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'ampere', 'ampere-hours', 'Ah'], + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'ampere'], to_anchor: 3600, }, kAh: { name: 'unit.kiloampere-hours', - tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'kiloampere-hours', 'kiloampere-hour', 'kAh'], + tags: ['electric current', 'current flow', 'electric charge', 'current capacity', 'flow of electricity', 'electrical flow', 'kiloampere-hours'], to_anchor: 3600000, }, } diff --git a/ui-ngx/src/app/shared/models/units/concentration-gradient.ts b/ui-ngx/src/app/shared/models/units/concentration-gradient.ts deleted file mode 100644 index ef406445d2..0000000000 --- a/ui-ngx/src/app/shared/models/units/concentration-gradient.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ConcentrationMetricUnits = 'mol/m³' | 'mg/mL' | 'mg/m³' | 'µg/m³' | 'particles/mL'; -export type ConcentrationUnits = ConcentrationMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'mol/m³': { - name: 'unit.mole-per-cubic-meter', - tags: ['concentration', 'amount of substance per unit volume', 'mole per cubic meter', 'mol/m³'], - to_anchor: 1, - }, - 'mg/mL': { - name: 'unit.milligram-per-milliliter', - tags: ['concentration', 'mass per unit volume', 'milligram per milliliter', 'mg/mL'], - to_anchor: 1, - }, - 'mg/m³': { - name: 'unit.milligram-per-cubic-meter', - tags: ['concentration', 'mass per unit volume', 'milligram per cubic meter', 'mg/m³'], - to_anchor: 1, - }, - 'µg/m³': { - name: 'unit.micrograms-per-cubic-meter', - tags: ['concentration', 'air quality', 'particulate matter', 'PM2.5', 'PM10', 'micrograms per cubic meter', 'µg/m³'], - to_anchor: 1, - }, - 'particles/mL': { - name: 'unit.particle-density', - tags: ['concentration', 'particle density', 'particles per milliliter', 'particles/mL'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/concentration-volume.ts b/ui-ngx/src/app/shared/models/units/concentration-volume.ts deleted file mode 100644 index 94c8050fee..0000000000 --- a/ui-ngx/src/app/shared/models/units/concentration-volume.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ConcentrationVolumeMetricUnits = - 'mol/m³' - | 'µg/m³' - | 'mg/m³' - | 'g/m³' - | 'mg/L' - | 'mg/mL' - | 'kat/m³' - | '°Bx'; -export type ConcentrationVolumeUnits = ConcentrationVolumeMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'mol/m³': { - name: 'unit.mole-per-cubic-meter', - tags: ['concentration', 'amount of substance', 'mole per cubic meter', 'mol/m³'], - to_anchor: 1, - }, - 'µg/m³': { - name: 'unit.micrograms-per-cubic-meter', - tags: ['coarse particulate matter', 'pm10', 'fine particulate matter', 'pm2.5', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'micrograms per cubic meter', 'µg/m³'], - to_anchor: 1e-9, - }, - 'mg/m³': { - name: 'unit.milligram-per-cubic-meter', - tags: ['concentration', 'mass per volume', 'mg/m³'], - to_anchor: 1e-6, - }, - 'g/m³': { - name: 'unit.gram-per-cubic-meter', - tags: ['humidity', 'moisture', 'absolute humidity', 'g/m³'], - to_anchor: 1 / 1000, - }, - 'mg/L': { - name: 'unit.mg-per-liter', - tags: ['dissolved oxygen', 'water quality', 'mg/L'], - to_anchor: 1e-6, - }, - 'mg/mL': { - name: 'unit.milligram-per-milliliter', - tags: ['concentration', 'mass per volume', 'mg/mL'], - to_anchor: 1 / 1000, - }, - 'kat/m³': { - name: 'unit.katal-per-cubic-metre', - tags: ['catalytic activity concentration', 'enzyme concentration', 'kat/m³'], - to_anchor: 1, - }, - '°Bx': { - name: 'unit.degrees-brix', - tags: ['sugar content', 'fruit ripeness', 'Bx'], - to_anchor: 10.04 * 1e-3, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/current-density.ts b/ui-ngx/src/app/shared/models/units/current-density.ts index 7b332c57f4..aa5c88b0e9 100644 --- a/ui-ngx/src/app/shared/models/units/current-density.ts +++ b/ui-ngx/src/app/shared/models/units/current-density.ts @@ -1,19 +1,33 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type CurrentDensityMetricUnits = 'µA/cm²' | 'A/m²'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type CurrentDensityUnits = CurrentDensityMetricUnits; +export type CurrentDensityUnits = 'µA/cm²' | 'A/m²'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'µA/cm²': { name: 'unit.microampere-per-square-centimeter', - tags: ['current density', 'microampere per square centimeter', 'µA/cm²'], + tags: ['current per unit area'], to_anchor: 10000, }, 'A/m²': { name: 'unit.ampere-per-square-meter', - tags: ['current density', 'current per unit area', 'ampere per square meter', 'A/m²'], + tags: ['current per unit area'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts index e3b9095700..d952eaf727 100644 --- a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts +++ b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type DataTransferRateUnits = 'bps' | 'kbps' | 'Mbps' | 'Gbps' | 'Tbps' | 'B/s' | 'KB/s' | 'MB/s' | 'GB/s'; @@ -6,47 +22,38 @@ const METRIC: TbMeasureUnits = { units: { 'bps': { name: 'unit.bit-per-second', - tags: ['data transfer rate', 'bps'], to_anchor: 1, }, 'kbps': { name: 'unit.kilobit-per-second', - tags: ['data transfer rate', 'kbps'], to_anchor: 1e3, }, 'Mbps': { name: 'unit.megabit-per-second', - tags: ['data transfer rate', 'Mbps'], to_anchor: 1e6, }, 'Gbps': { name: 'unit.gigabit-per-second', - tags: ['data transfer rate', 'Gbps'], to_anchor: 1e9, }, 'Tbps': { name: 'unit.terabit-per-second', - tags: ['data transfer rate', 'Tbps'], to_anchor: 1e12, }, 'B/s': { name: 'unit.byte-per-second', - tags: ['data transfer rate', 'B/s'], to_anchor: 8, }, 'KB/s': { name: 'unit.kilobyte-per-second', - tags: ['data transfer rate', 'KB/s'], to_anchor: 8e3, }, 'MB/s': { name: 'unit.megabyte-per-second', - tags: ['data transfer rate', 'MB/s'], to_anchor: 8e6, }, 'GB/s': { name: 'unit.gigabyte-per-second', - tags: ['data transfer rate', 'GB/s'], to_anchor: 8e9, }, }, diff --git a/ui-ngx/src/app/shared/models/units/density.ts b/ui-ngx/src/app/shared/models/units/density.ts index 8603de1976..8a501d1638 100644 --- a/ui-ngx/src/app/shared/models/units/density.ts +++ b/ui-ngx/src/app/shared/models/units/density.ts @@ -1,6 +1,22 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type DensityMetricUnits = 'kg/m³' | 'g/cm³'; +export type DensityMetricUnits = 'kg/m³' | 'g/cm³' | 'mg/dL' | 'g/m³' | 'mg/mL' | 'mg/L' | 'mg/m³' | 'µg/m³'; export type DensityImperialUnits = 'lb/ft³' | 'oz/in³' | 'ton/yd³'; export type DensityUnits = DensityMetricUnits | DensityImperialUnits; @@ -10,13 +26,43 @@ const METRIC: TbMeasureUnits = { units: { 'kg/m³': { name: 'unit.kilogram-per-cubic-meter', - tags: ['density', 'mass per unit volume', 'kg/m³'], - to_anchor: 1, // Base unit: kg/m³ + tags: ['mass per unit volume'], + to_anchor: 1, }, 'g/cm³': { name: 'unit.gram-per-cubic-centimeter', - tags: ['density', 'mass per unit volume', 'g/cm³'], - to_anchor: 1000, // 1 g/cm³ = 10³ kg/m³ + tags: ['mass per unit volume'], + to_anchor: 1000, + }, + 'mg/dL': { + name: 'unit.milligrams-per-deciliter', + tags: ['glucose', 'blood sugar', 'glucose level', 'concentration'], + to_anchor: 0.01, + }, + 'g/m³': { + name: 'unit.gram-per-cubic-meter', + tags: ['humidity', 'moisture', 'absolute humidity', 'concentration'], + to_anchor: 0.001, + }, + 'mg/L': { + name: 'unit.mg-per-liter', + tags: ['dissolved oxygen', 'water quality', 'mg/L', 'concentration'], + to_anchor: 0.001, + }, + 'mg/mL': { + name: 'unit.milligram-per-milliliter', + tags: ['mass per unit volume', 'concentration'], + to_anchor: 1, + }, + 'mg/m³': { + name: 'unit.milligram-per-cubic-meter', + tags: ['mass per unit volume', 'concentration'], + to_anchor: 1e-6, + }, + 'µg/m³': { + name: 'unit.micrograms-per-cubic-meter', + tags: ['coarse particulate matter', 'pm10', 'fine particulate matter', 'pm2.5', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'concentration'], + to_anchor: 1e-9, }, }, }; @@ -26,17 +72,17 @@ const IMPERIAL: TbMeasureUnits = { units: { 'lb/ft³': { name: 'unit.pound-per-cubic-foot', - tags: ['density', 'mass per unit volume', 'lb/ft³'], + tags: ['mass per unit volume'], to_anchor: 1, }, 'oz/in³': { name: 'unit.ounces-per-cubic-inch', - tags: ['density', 'mass per unit volume', 'oz/in³'], + tags: ['mass per unit volume'], to_anchor: 1728, }, 'ton/yd³': { name: 'unit.tons-per-cubic-yard', - tags: ['density', 'mass per unit volume', 'ton/yd³'], + tags: ['mass per unit volume'], to_anchor: 74.074, }, }, diff --git a/ui-ngx/src/app/shared/models/units/digital.ts b/ui-ngx/src/app/shared/models/units/digital.ts index 54bcceea2f..32f2eed2a7 100644 --- a/ui-ngx/src/app/shared/models/units/digital.ts +++ b/ui-ngx/src/app/shared/models/units/digital.ts @@ -16,60 +16,58 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type DigitalUnits = DigitalMetricUnits; +export type DigitalUnits = 'bit' | 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB' | 'EB' | 'ZB' | 'YB'; -export type DigitalMetricUnits = 'bit' | 'B' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB' | 'EB' | 'ZB' | 'YB'; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { bit: { name: 'unit.bit', - tags: ['data', 'binary digit', 'information', 'bit'], + tags: ['data', 'binary digit', 'information'], to_anchor: 1.25e-1, }, B: { name: 'unit.byte', - tags: ['data', 'byte', 'information', 'storage', 'memory', 'B'], + tags: ['data', 'information', 'storage', 'memory'], to_anchor: 1 }, KB: { name: 'unit.kilobyte', - tags: ['data', 'kilobyte', 'KB'], + tags: ['data'], to_anchor: 1024, }, MB: { name: 'unit.megabyte', - tags: ['data', 'megabyte', 'MB'], + tags: ['data'], to_anchor: 1024 ** 2, }, GB: { name: 'unit.gigabyte', - tags: ['data', 'gigabyte', 'GB'], + tags: ['data'], to_anchor: 1024 ** 3, }, TB: { name: 'unit.terabyte', - tags: ['data', 'terabyte', 'TB'], + tags: ['data'], to_anchor: 1024 ** 4, }, PB: { name: 'unit.petabyte', - tags: ['data', 'petabyte', 'PB'], + tags: ['data'], to_anchor: 1024 ** 5, }, EB: { name: 'unit.exabyte', - tags: ['data', 'exabyte', 'EB'], + tags: ['data'], to_anchor: 1024 ** 6, }, ZB: { name: 'unit.zettabyte', - tags: ['data', 'zettabyte', 'EB'], + tags: ['data'], to_anchor: 1024 ** 7, }, YB: { name: 'unit.yottabyte', - tags: ['data', 'yottabyte', 'EB'], + tags: ['data'], to_anchor: 1024 ** 8, }, } diff --git a/ui-ngx/src/app/shared/models/units/dimension-ratio.ts b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts new file mode 100644 index 0000000000..3deb60a38f --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type DimensionRatioUnits = 'm/m'; + +const METRIC: TbMeasureUnits = { + units: { + 'm/m': { + name: 'unit.meter-per-meter', + tags: ['ratio of length to length'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts b/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts deleted file mode 100644 index 71972aa6e8..0000000000 --- a/ui-ngx/src/app/shared/models/units/dimensionless-ratio.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type DimensionlessRatioMetricUnits = 'm/m'; - -export type DimensionRatioUnits = DimensionlessRatioMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'm/m': { - name: 'unit.meter-per-meter', - tags: ['ratio of length to length', 'meter per meter', 'm/m'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts index 272892f9df..e3fe8602b3 100644 --- a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type DynamicViscosityMetricUnits = 'Pa·s' | 'cP' | 'P' | 'N·s/m²' | 'dyn·s/cm²' | 'kg/(m·s)'; @@ -10,32 +26,32 @@ const METRIC: TbMeasureUnits = { units: { 'Pa·s': { name: 'unit.pascal-second', - tags: ['dynamic viscosity', 'viscosity', 'fluid mechanics', 'Pa·s'], + tags: ['fluid mechanics'], to_anchor: 1, }, 'cP': { name: 'unit.centipoise', - tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'centipoise', 'cP'], + tags: ['fluid mechanics'], to_anchor: 0.001, }, 'P': { name: 'unit.poise', - tags: ['viscosity', 'dynamic viscosity', 'fluid viscosity', 'poise', 'P'], + tags: ['fluid mechanics'], to_anchor: 0.1, }, 'N·s/m²': { name: 'unit.newton-second-per-square-meter', - tags: ['newton second per square meter', 'N·s/m²'], + tags: ['fluid mechanics'], to_anchor: 1, }, 'dyn·s/cm²': { name: 'unit.dyne-second-per-square-centimeter', - tags: ['dyne second per square centimeter', 'dyn·s/cm²'], + tags: ['fluid mechanics'], to_anchor: 0.1, }, 'kg/(m·s)': { name: 'unit.kilogram-per-meter-second', - tags: ['kilogram per meter-second', 'kg/(m·s)'], + tags: ['fluid mechanics'], to_anchor: 1, }, }, @@ -46,7 +62,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'lb/(ft·h)': { name: 'unit.pound-per-foot-hour', - tags: ['pound per foot-hour', 'lb/(ft·h)'], + tags: ['fluid mechanics'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts index 02658073b5..60b40ec01c 100644 --- a/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts +++ b/ui-ngx/src/app/shared/models/units/earthquake-magnitude.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type EarthquakeMagnitudeUnits = 'richter'; @@ -6,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { richter: { name: 'unit.richter-scale', - tags: ['earthquake', 'seismic activity', 'richter'], + tags: ['seismic activity'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electric-current.ts b/ui-ngx/src/app/shared/models/units/electric-current.ts index 9025e71c15..fbfb9b888c 100644 --- a/ui-ngx/src/app/shared/models/units/electric-current.ts +++ b/ui-ngx/src/app/shared/models/units/electric-current.ts @@ -16,50 +16,48 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricCurrentUnits = ElectricCurrentMetricalUnits; +export type ElectricCurrentUnits = 'A' | 'pA' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; -export type ElectricCurrentMetricalUnits = 'A' | 'pA' | 'nA' | 'μA' | 'mA' | 'kA' | 'MA' | 'GA'; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { A: { name: 'unit.ampere', - tags: ['electric current', 'current flow', 'flow of electricity', 'electrical flow', 'ampere', 'amperes', 'amperage', 'A'], + tags: ['current flow', 'flow of electricity', 'electrical flow', 'amperes', 'amperage'], to_anchor: 1, }, pA: { name: 'unit.picoampere', - tags: ['current', 'amperes', 'picoampere', 'pA'], + tags: ['picoamperes'], to_anchor: 1e-12, }, nA: { name: 'unit.nanoampere', - tags: ['electric current', 'amperes', 'nanoampere', 'nA'], + tags: ['nanoamperes'], to_anchor: 1e-9, }, μA: { name: 'unit.microampere', - tags: ['electric current', 'microampere', 'microamperes', 'μA'], + tags: ['microamperes'], to_anchor: 1e-6, }, mA: { name: 'unit.milliampere', - tags: ['electric current', 'milliampere', 'milliamperes', 'mA'], + tags: ['milliamperes'], to_anchor: 0.001, }, kA: { name: 'unit.kiloampere', - tags: ['electric current', 'kiloampere', 'kiloamperes', 'kA'], + tags: ['kiloamperes'], to_anchor: 1000, }, MA: { name: 'unit.megaampere', - tags: ['electric current', 'megaampere', 'megaamperes', 'MA'], + tags: ['megaamperes'], to_anchor: 1e6, }, GA: { name: 'unit.gigaampere', - tags: ['electric current', 'gigaampere', 'gigaamperes', 'GA'], + tags: ['gigaamperes'], to_anchor: 1e9, }, } diff --git a/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts new file mode 100644 index 0000000000..b43e08b857 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts @@ -0,0 +1,39 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricDipoleMomentUnits = 'C·m' | 'D'; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m': { + name: 'unit.electric-dipole-moment', + to_anchor: 1, + }, + 'D': { + name: 'unit.debye', + tags: ['polarization'], + to_anchor: 3.33564e-30 + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-field-strength.ts b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts index f8d753c401..16a35b963d 100644 --- a/ui-ngx/src/app/shared/models/units/electric-field-strength.ts +++ b/ui-ngx/src/app/shared/models/units/electric-field-strength.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ElectricFieldStrengthUnits = 'V/m' | 'mV/m' | 'kV/m'; @@ -6,17 +22,14 @@ const METRIC: TbMeasureUnits = { units: { 'V/m': { name: 'unit.volts-per-meter', - tags: ['electric field strength', 'volts per meter', 'V/m'], to_anchor: 1, }, 'mV/m': { name: 'unit.millivolts-per-meter', - tags: ['electric field strength', 'millivolts per meter', 'mV/m'], to_anchor: 1e-3, }, 'kV/m': { name: 'unit.kilovolts-per-meter', - tags: ['electric field strength', 'kilovolts per meter', 'kV/m'], to_anchor: 1e3, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electric-flux.ts b/ui-ngx/src/app/shared/models/units/electric-flux.ts index ac40a58293..ca4e6c2fe7 100644 --- a/ui-ngx/src/app/shared/models/units/electric-flux.ts +++ b/ui-ngx/src/app/shared/models/units/electric-flux.ts @@ -1,39 +1,47 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type ElectricFluxMetricUnits = 'V·m' | 'kV·m' | 'MV·m' | 'µV·m' | 'mV·m' | 'nV·m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricFluxUnits = ElectricFluxMetricUnits; +export type ElectricFluxUnits = 'V·m' | 'kV·m' | 'MV·m' | 'µV·m' | 'mV·m' | 'nV·m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'V·m': { name: 'unit.volt-meter', - tags: ['electric flux', 'volt-meter', 'V·m'], to_anchor: 1, }, 'kV·m': { name: 'unit.kilovolt-meter', - tags: ['electric flux', 'kilovolt-meter', 'kV·m'], to_anchor: 1000, }, 'MV·m': { name: 'unit.megavolt-meter', - tags: ['electric flux', 'megavolt-meter', 'MV·m'], to_anchor: 1000000, }, 'µV·m': { name: 'unit.microvolt-meter', - tags: ['electric flux', 'microvolt-meter', 'µV·m'], to_anchor: 0.000001, }, 'mV·m': { name: 'unit.millivolt-meter', - tags: ['electric flux', 'millivolt-meter', 'mV·m'], to_anchor: 0.001, }, 'nV·m': { name: 'unit.nanovolt-meter', - tags: ['electric flux', 'nanovolt-meter', 'nV·m'], to_anchor: 0.000000001, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electric-moment.ts b/ui-ngx/src/app/shared/models/units/electric-moment.ts deleted file mode 100644 index 47a8e55830..0000000000 --- a/ui-ngx/src/app/shared/models/units/electric-moment.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ElectricMomentMetricUnits = 'C·m' | 'D'; - -export type ElectricMomentUnits = ElectricMomentMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'C·m': { - name: 'unit.electric-dipole-moment', - tags: ['electric dipole', 'dipole moment', 'coulomb meter', 'C·m'], - to_anchor: 1, - }, - 'D': { - name: 'unit.debye', - tags: ['polarization', 'electric dipole moment', 'debye', 'D'], - to_anchor: 3.33564e-30 - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-permittivity.ts b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts index d9bea899a7..e116d9e08a 100644 --- a/ui-ngx/src/app/shared/models/units/electric-permittivity.ts +++ b/ui-ngx/src/app/shared/models/units/electric-permittivity.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type ElectricPermittivityMetricUnits = 'F/m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricPermittivityUnits = ElectricPermittivityMetricUnits; +export type ElectricPermittivityUnits = 'F/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'F/m': { name: 'unit.farad-per-meter', - tags: ['electric permittivity', 'farad per meter', 'F/m'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts index 0a1fe6bebe..62241d059f 100644 --- a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts +++ b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type ElectricalConductanceUnits = 'S' | 'mS' | 'μS' | 'kS' | 'MS' | 'GS'; @@ -6,32 +22,26 @@ const METRIC: TbMeasureUnits = { units: { 'S': { name: 'unit.siemens', - tags: ['electrical conductance', 'conductance', 'siemens', 'S'], to_anchor: 1, }, 'mS': { name: 'unit.millisiemens', - tags: ['electrical conductance', 'conductance', 'millisiemens', 'mS'], to_anchor: 1e-3, }, 'μS': { name: 'unit.microsiemens', - tags: ['electrical conductance', 'conductance', 'microsiemens', 'μS'], to_anchor: 1e-6, }, 'kS': { name: 'unit.kilosiemens', - tags: ['electrical conductance', 'conductance', 'kilosiemens', 'kS'], to_anchor: 1e3, }, 'MS': { name: 'unit.megasiemens', - tags: ['electrical conductance', 'conductance', 'megasiemens', 'MS'], to_anchor: 1e6, }, 'GS': { name: 'unit.gigasiemens', - tags: ['electrical conductance', 'conductance', 'gigasiemens', 'GS'], to_anchor: 1e9, }, }, diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts index 9bf7245ff1..aa9aab858f 100644 --- a/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts +++ b/ui-ngx/src/app/shared/models/units/electrical-conductivity.ts @@ -1,23 +1,38 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ElectricalConductivityMetricUnits = 'µS/cm' | 'mS/m' | 'S/m'; -export type ElectricalConductivityUnits = ElectricalConductivityMetricUnits; +export type ElectricalConductivityUnits = 'µS/cm' | 'mS/m' | 'S/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'S/m': { name: 'unit.siemens-per-meter', - tags: ['Electrical conductivity', 'water quality', 'soil quality', 'siemens per meter', 'S/m'], + tags: [ 'water quality', 'soil quality'], to_anchor: 1, }, 'µS/cm': { name: 'unit.microsiemens-per-centimeter', - tags: ['Electrical conductivity', 'water quality', 'soil quality', 'microsiemens per centimeter', 'µS/cm'], + tags: ['water quality', 'soil quality'], to_anchor: 0.0001, }, 'mS/m': { name: 'unit.millisiemens-per-meter', - tags: ['Electrical conductivity', 'water quality', 'soil quality', 'millisiemens per meter', 'mS/m'], + tags: ['water quality', 'soil quality'], to_anchor: 0.001, }, }, diff --git a/ui-ngx/src/app/shared/models/units/energy.ts b/ui-ngx/src/app/shared/models/units/energy.ts index 2f74091c10..8b81101574 100644 --- a/ui-ngx/src/app/shared/models/units/energy.ts +++ b/ui-ngx/src/app/shared/models/units/energy.ts @@ -40,42 +40,36 @@ const METRIC: TbMeasureUnits = { units: { Wm: { name: 'unit.watt-minute', - tags: ['energy', 'watt-minute', 'watt-minutes', 'Wm'], to_anchor: 60, }, Wh: { name: 'unit.watt-hour', - tags: ['energy', 'watt-hour', 'watt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + tags: ['energy usage', 'power consumption', 'energy consumption', 'electricity usage'], to_anchor: 3600, }, mWh: { name: 'unit.milliwatt-hour', - tags: ['energy', 'milliwatt-hour', 'milliwatt-hours', 'mWh'], to_anchor: 3.6, }, kWh: { name: 'unit.kilowatt-hour', - tags: ['energy', 'kilowatt-hour', 'kilowatt-hours', 'energy usage', 'power consumption', 'energy consumption', 'electricity usage'], + tags: ['energy usage', 'power consumption', 'energy consumption', 'electricity usage'], to_anchor: 3600000, }, MWh: { name: 'unit.megawatt-hour', - tags: ['energy', 'megawatt-hour', 'megawatt-hours', 'MWh'], to_anchor: 3600000000, }, GWh: { name: 'unit.gigawatt-hour', - tags: ['energy', 'gigawatt-hour', 'gigawatt-hours', 'GWh'], to_anchor: 3600000000000, }, μJ: { name: 'unit.microjoule', - tags: ['energy', 'microjoule', 'microjoules', 'μJ'], to_anchor: 0.000001, }, mJ: { name: 'unit.millijoule', - tags: ['energy', 'millijoule', 'millijoules', 'mJ'], to_anchor: 0.001, }, J: { @@ -85,22 +79,19 @@ const METRIC: TbMeasureUnits = { }, kJ: { name: 'unit.kilojoule', - tags: ['energy', 'kilojoule', 'kilojoules', 'kJ'], to_anchor: 1000, }, MJ: { name: 'unit.megajoule', - tags: ['energy', 'megajoule', 'megajoules', 'MJ'], to_anchor: 1000000, }, GJ: { name: 'unit.gigajoule', - tags: ['energy', 'gigajoule', 'gigajoules', 'GJ'], to_anchor: 1000000000, }, eV: { name: 'unit.electron-volts', - tags: ['energy', 'subatomic particles', 'radiation'], + tags: ['subatomic particles', 'radiation'], to_anchor: 1.602176634e-19, }, }, @@ -111,32 +102,31 @@ const IMPERIAL: TbMeasureUnits = { units: { cal: { name: 'unit.small-calorie', - tags: ['energy', 'small calorie', 'calories', 'cal'], to_anchor: 1, }, Cal: { name: 'unit.calorie', - tags: ['energy', 'food energy', 'Calorie', 'Calories', 'Cal'], + tags: ['food energy'], to_anchor: 1000, }, kcal: { name: 'unit.kilocalorie', - tags: ['energy', 'small calorie', 'kilocalories', 'kcal'], + tags: ['small calorie'], to_anchor: 1000, }, BTU: { name: 'unit.british-thermal-unit', - tags: ['energy', 'heat', 'work done', 'british thermal unit', 'british thermal units', 'BTU'], + tags: ['heat', 'work done'], to_anchor: 252.164401, }, 'ft·lb': { name: 'unit.foot-pound', - tags: ['energy', 'foot-pound', 'foot-pounds', 'ft·lb', 'ft⋅lbf'], + tags: ['ft⋅lbf'], to_anchor: 0.32404875717017, }, thm: { name: 'unit.therm', - tags: ['energy', 'natural gas consumption', 'BTU', 'therm', 'thm'], + tags: ['natural gas consumption', 'BTU'], to_anchor: 25219021.687207, }, }, diff --git a/ui-ngx/src/app/shared/models/units/force.ts b/ui-ngx/src/app/shared/models/units/force.ts index 46ee193829..afc98f626d 100644 --- a/ui-ngx/src/app/shared/models/units/force.ts +++ b/ui-ngx/src/app/shared/models/units/force.ts @@ -26,17 +26,15 @@ const METRIC: TbMeasureUnits = { units: { N: { name: 'unit.newton', - tags: ['force', 'pressure', 'newton', 'newtons', 'N', 'push', 'pull', 'weight', 'gravity', 'N'], + tags: ['pressure', 'push', 'pull', 'weight'], to_anchor: 1, }, kN: { name: 'unit.kilonewton', - tags: ['force', 'kN'], to_anchor: 1000, }, dyn: { name: 'unit.dyne', - tags: ['force', 'dyn'], to_anchor: 0.00001, }, }, @@ -47,27 +45,22 @@ const IMPERIAL: TbMeasureUnits = { units: { lbf: { name: 'unit.pound-force', - tags: ['force', 'lbf'], to_anchor: 1, }, kgf: { name: 'unit.kilogram-force', - tags: ['force', 'kgf'], to_anchor: 2.20462, }, klbf: { name: 'unit.kilopound-force', - tags: ['force', 'klbf'], to_anchor: 1000, }, pdl: { name: 'unit.poundal', - tags: ['force', 'pdl'], to_anchor: 0.031081, }, kip: { name: 'unit.kip', - tags: ['force', 'kip'], to_anchor: 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/frequency.ts b/ui-ngx/src/app/shared/models/units/frequency.ts index f677803fe8..4baf86405e 100644 --- a/ui-ngx/src/app/shared/models/units/frequency.ts +++ b/ui-ngx/src/app/shared/models/units/frequency.ts @@ -23,62 +23,61 @@ const METRIC: TbMeasureUnits = { units: { mHz: { name: 'unit.millihertz', - tags: ['frequency', 'cycles per second', 'millihertz', 'mHz'], + tags: ['cycles per second'], to_anchor: 1 / 1000, }, Hz: { name: 'unit.hertz', - tags: ['frequency', 'cycles per second', 'hertz', 'Hz'], + tags: ['cycles per second'], to_anchor: 1, }, kHz: { name: 'unit.kilohertz', - tags: ['frequency', 'cycles per second', 'kilohertz', 'kHz'], + tags: ['cycles per second'], to_anchor: 1000, }, MHz: { name: 'unit.megahertz', - tags: ['frequency', 'cycles per second', 'megahertz', 'MHz'], + tags: ['cycles per second'], to_anchor: 1000 * 1000, }, GHz: { name: 'unit.gigahertz', - tags: ['frequency', 'cycles per second', 'gigahertz', 'GHz'], + tags: ['cycles per second'], to_anchor: 1000 * 1000 * 1000, }, THz: { name: 'unit.terahertz', - tags: ['frequency', 'terahertz', 'THz'], to_anchor: 1000 * 1000 * 1000 * 1000, }, rpm: { name: 'unit.rotation-per-minute', - tags: ['frequency', 'rotation per minute', 'rotations per minute', 'rpm', 'angular velocity'], + tags: ['rotations per minute', 'angular velocity'], to_anchor: 1 / 60, }, RPM: { name: 'unit.rpm', - tags: ['rotational speed', 'angular velocity', 'revolutions per minute', 'RPM'], + tags: ['rotational speed', 'angular velocity'], to_anchor: 1 / 60, }, 'λ': { name: 'unit.lambda', - tags: ['wavelength', 'lambda', 'λ'], + tags: ['wavelength'], to_anchor: 299792458, }, bpm: { name: 'unit.beats-per-minute', - tags: ['heart rate', 'pulse', 'bpm'], + tags: ['heart rate', 'pulse'], to_anchor: 0.0167 }, 'deg/s': { name: 'unit.deg-per-second', - tags: ['angular velocity', 'degrees per second', 'deg/s'], - to_anchor: 1 / 360, // 1 deg/s = 1/360 Hz + tags: ['angular velocity'], + to_anchor: 1 / 360, }, 'rad/s': { name: 'unit.radian-per-second', - tags: ['angular velocity', 'rotation speed', 'rad/s'], + tags: ['angular velocity'], to_anchor: 1 / (Math.PI * 2), }, }, diff --git a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts index f097382089..64eb49cfb2 100644 --- a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts +++ b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type FuelEfficiencyMetricUnits = 'km/L' | 'L/100km'; @@ -10,12 +26,10 @@ const METRIC: TbMeasureUnits = { units: { 'km/L': { name: 'unit.kilometers-per-liter', - tags: ['fuel efficiency', 'km/L'], to_anchor: 1, }, 'L/100km': { name: 'unit.liters-per-100-km', - tags: ['fuel efficiency', 'L/100km'], to_anchor: 1, transform: (value) => 100 / value, }, @@ -27,12 +41,10 @@ const IMPERIAL: TbMeasureUnits = { units: { 'mpg': { name: 'unit.miles-per-gallon', - tags: ['fuel efficiency', 'mpg'], to_anchor: 0.425144, }, 'gal/mi': { name: 'unit.gallons-per-mile', - tags: ['fuel efficiency', 'gal/mi'], to_anchor: 2.35214583, }, }, diff --git a/ui-ngx/src/app/shared/models/units/heat-capacity.ts b/ui-ngx/src/app/shared/models/units/heat-capacity.ts index 05b56cd0a7..7c42926bd0 100644 --- a/ui-ngx/src/app/shared/models/units/heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/heat-capacity.ts @@ -1,15 +1,29 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type HeatCapacityMetricUnits = 'J/K'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type HeatCapacityUnits = HeatCapacityMetricUnits; +export type HeatCapacityUnits = 'J/K'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/K': { name: 'unit.joule-per-kelvin', - tags: ['specific heat capacity', 'heat capacity per unit temperature', 'joule per kelvin', 'J/K'], - to_anchor: 1, // Base unit: J/K + tags: ['heat capacity per unit temperature'], + to_anchor: 1, }, }, }; diff --git a/ui-ngx/src/app/shared/models/units/humidity.ts b/ui-ngx/src/app/shared/models/units/humidity.ts deleted file mode 100644 index 22bf1d6902..0000000000 --- a/ui-ngx/src/app/shared/models/units/humidity.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type HumidityUnits = HumidityMetricUnits; -export type HumidityMetricUnits = 'g/kg'; - -const METRIC: TbMeasureUnits = { - units: { - 'g/kg': { - name: 'unit.gram-per-kilogram', - tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/illuminance.ts b/ui-ngx/src/app/shared/models/units/illuminance.ts index 6c1ae4d43d..286cd4923d 100644 --- a/ui-ngx/src/app/shared/models/units/illuminance.ts +++ b/ui-ngx/src/app/shared/models/units/illuminance.ts @@ -26,17 +26,17 @@ const METRIC: TbMeasureUnits = { units: { lx: { name: 'unit.lux', - tags: ['illumination', 'light level on a surface', 'illuminance', 'Lux', 'lx'], + tags: ['light level on a surface', 'illuminance', 'Lux', 'lx'], to_anchor: 1, }, 'cd/m²': { name: 'unit.candela-per-square-meter', - tags: ['brightness', 'light level', 'Luminance', 'Candela per square meter', 'cd/m²'], + tags: ['brightness', 'light level', 'Luminance'], to_anchor: 1, }, 'lm/m²': { name: 'unit.lumen-per-square-meter', - tags: ['illumination', 'light level', 'lumen per square meter', 'lm/m²'], + tags: ['light level'], to_anchor: 1, }, }, @@ -47,7 +47,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'fc': { name: 'unit.foot-candle', - tags: ['illuminance', 'light level', 'foot-candle', 'fc'], + tags: ['illuminance', 'light level'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/inductance.ts b/ui-ngx/src/app/shared/models/units/inductance.ts index 9f0d0bb3eb..0acbab4774 100644 --- a/ui-ngx/src/app/shared/models/units/inductance.ts +++ b/ui-ngx/src/app/shared/models/units/inductance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type InductanceMetricUnits = 'H' | 'mH' | 'µH' | 'nH' | 'T·m/A'; diff --git a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts index 6a9104ff9f..8d2201d456 100644 --- a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type KinematicViscosityMetricUnits = 'm²/s' | 'cm²/s' | 'St' | 'cSt'; @@ -10,23 +26,19 @@ const METRIC: TbMeasureUnits = { units: { 'm²/s': { name: 'unit.square-meter-per-second', - tags: ['kinematic viscosity', 'm²/s'], to_anchor: 1, }, 'cm²/s': { name: 'unit.square-centimeter-per-second', - tags: ['kinematic viscosity', 'cm²/s'], to_anchor: 1e-4, }, 'St': { name: 'unit.stoke', - tags: ['kinematic viscosity', 'stokes', 'St'], - to_anchor: 1e-4, // St to m²/s + to_anchor: 1e-4, }, 'cSt': { name: 'unit.centistokes', - tags: ['kinematic viscosity', 'centistokes', 'cSt'], - to_anchor: 1e-6, // cSt to m²/s + to_anchor: 1e-6, }, }, }; @@ -36,12 +48,10 @@ const IMPERIAL: TbMeasureUnits = { units: { 'ft²/s': { name: 'unit.square-foot-per-second', - tags: ['kinematic viscosity', 'ft²/s'], to_anchor: 0.09290304, }, 'in²/s': { name: 'unit.square-inch-per-second', - tags: ['kinematic viscosity', 'in²/s'], to_anchor: 0.00064516, }, }, diff --git a/ui-ngx/src/app/shared/models/units/length.ts b/ui-ngx/src/app/shared/models/units/length.ts index 4a13c73bb1..549a66c3e6 100644 --- a/ui-ngx/src/app/shared/models/units/length.ts +++ b/ui-ngx/src/app/shared/models/units/length.ts @@ -43,42 +43,42 @@ const METRIC: TbMeasureUnits = { units: { nm: { name: 'unit.nanometer', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nanoscale', 'atomic scale', 'molecular scale', 'nanometer', 'nanometers', 'nm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nanoscale', 'atomic scale', 'molecular scale'], to_anchor: 1e-9, }, μm: { name: 'unit.micrometer', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'microns', 'micrometer', 'micrometers', 'µm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'microns'], to_anchor: 1e-6, }, mm: { name: 'unit.millimeter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'millimeter', 'millimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'mm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition'], to_anchor: 1e-3, }, cm: { name: 'unit.centimeter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'centimeter', 'centimeters', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition', 'cm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'rainfall', 'precipitation', 'displacement', 'position', 'movement', 'transition'], to_anchor: 1e-2, }, dm: { name: 'unit.decimeter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'decimeter', 'decimeters', 'dm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 1e-1, }, m: { name: 'unit.meter', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'meter', 'meters', 'm'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 1, }, km: { name: 'unit.kilometer', - tags: ['distance', 'height', 'length', 'width', 'gap', 'depth', 'kilometer', 'kilometers', 'km'], + tags: ['distance', 'height', 'width', 'gap', 'depth'], to_anchor: 1e3, }, angstrom: { name: 'unit.angstrom', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'atomic scale', 'atomic distance', 'nanoscale', 'angstrom', 'angstroms', 'Å'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'atomic scale', 'atomic distance', 'nanoscale'], to_anchor: 1e-10, }, }, @@ -89,87 +89,87 @@ const IMPERIAL: TbMeasureUnits = { units: { in: { name: 'unit.inch', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'inch', 'inches', 'in'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 1 / 12, }, yd: { name: 'unit.yard', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'yard', 'yards', 'yd'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 3, }, 'ft-us': { name: 'unit.foot-us', - tags: ['length', 'us survey foot', 'us survey feet', 'ft-us', 'surveying'], + tags: ['us survey foot', 'us survey feet', 'ft-us', 'surveying'], to_anchor: 1.000002, }, ft: { name: 'unit.foot', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'foot', 'feet', 'ft'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'feet'], to_anchor: 1, }, fathom: { name: 'unit.fathom', - tags: ['depth', 'nautical measurement', 'fathom'], + tags: ['depth', 'nautical measurement'], to_anchor: 6, }, mi: { name: 'unit.mile', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'mile', 'miles', 'mi'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth'], to_anchor: 5280, }, nmi: { name: 'unit.nautical-mile', - tags: ['level', 'height', 'distance', 'length', 'width', 'gap', 'depth', 'nautical mile', 'nmi'], + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nautical mile'], to_anchor: 6076.12, }, thou: { name: 'unit.thou', - tags: ['length', 'measurement', 'thou'], + tags: ['measurement'], to_anchor: 0.001 / 12, }, barleycorn: { name: 'unit.barleycorn', - tags: ['length', 'shoe size', 'barleycorn'], + tags: ['shoe size'], to_anchor: 1 / 36, }, hand: { name: 'unit.hand', - tags: ['length', 'horse measurement', 'hand'], + tags: ['horse measurement'], to_anchor: 4 / 12, }, ch: { name: 'unit.chain', - tags: ['length', 'land surveying', 'ch'], + tags: ['land surveying'], to_anchor: 66, }, fur: { name: 'unit.furlong', - tags: ['length', 'land surveying', 'fur'], + tags: ['land surveying'], to_anchor: 660, }, league: { name: 'unit.league', - tags: ['length', 'historical measurement', 'league'], + tags: ['historical measurement'], to_anchor: 3 * 5280, }, link: { name: 'unit.link', - tags: ['length', 'land surveying', 'link'], + tags: ['land surveying'], to_anchor: 0.66, }, rod: { name: 'unit.rod', - tags: ['length', 'land surveying', 'rod'], + tags: ['land surveying'], to_anchor: 16.5, }, cable: { name: 'unit.cable', - tags: ['distance', 'nautical measurement', 'cable'], + tags: ['distance', 'nautical measurement'], to_anchor: 600, }, AU: { name: 'unit.astronomical-unit', - tags: ['distance', 'celestial bodies', 'solar system', 'AU'], + tags: ['distance', 'celestial bodies', 'solar system'], to_anchor: 149597870700 * 3.28084, }, }, diff --git a/ui-ngx/src/app/shared/models/units/light-exposure.ts b/ui-ngx/src/app/shared/models/units/light-exposure.ts index 353cf57a37..72dd8a920b 100644 --- a/ui-ngx/src/app/shared/models/units/light-exposure.ts +++ b/ui-ngx/src/app/shared/models/units/light-exposure.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type LightExposureMetricUnits = 'lx·s'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LightExposureUnits = LightExposureMetricUnits; +export type LightExposureUnits = 'lx·s'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'lx·s': { name: 'unit.lux-second', - tags: ['light exposure', 'illuminance over time', 'lux-second', 'lx·s'], + tags: ['illuminance over time'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/liner-charge-density.ts b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts index 4a2239b1d7..904257e328 100644 --- a/ui-ngx/src/app/shared/models/units/liner-charge-density.ts +++ b/ui-ngx/src/app/shared/models/units/liner-charge-density.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type LinerChargeDensityMetricUnits = 'C/m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LinerChargeDensityUnits = LinerChargeDensityMetricUnits; +export type LinerChargeDensityUnits = 'C/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'C/m': { name: 'unit.coulomb-per-meter', - tags: ['electric displacement field per length', 'coulomb per meter', 'C/m'], + tags: ['electric displacement field per length'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts new file mode 100644 index 0000000000..cb34632711 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts @@ -0,0 +1,45 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type LogarithmicRatioUnits = 'dB' | 'B' | 'Np'; + +const METRIC: TbMeasureUnits = { + units: { + 'dB': { + name: 'unit.decibel', + tags: ['noise level', 'sound level', 'volume', 'acoustics'], + to_anchor: 1, + }, + 'B': { + name: 'unit.bel', + tags: ['power ratio', 'intensity ratio'], + to_anchor: 10, + }, + 'Np': { + name: 'unit.neper', + tags: ['gain', 'loss', 'attenuation'], + to_anchor: 8.685889638, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-units.ts b/ui-ngx/src/app/shared/models/units/logarithmic-units.ts deleted file mode 100644 index ccbfd9af8e..0000000000 --- a/ui-ngx/src/app/shared/models/units/logarithmic-units.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type LogarithmicUnits = 'dB' | 'B' | 'Np'; - -const METRIC: TbMeasureUnits = { - units: { - 'dB': { - name: 'unit.decibel', - tags: ['noise level', 'sound level', 'volume', 'acoustics', 'decibel', 'dB'], - to_anchor: 1, - }, - 'B': { - name: 'unit.bel', - tags: ['logarithmic unit', 'power ratio', 'intensity ratio', 'bel', 'B'], - to_anchor: 10, - }, - 'Np': { - name: 'unit.neper', - tags: ['logarithmic unit', 'ratio', 'gain', 'loss', 'attenuation', 'neper', 'Np'], - to_anchor: 8.685889638, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts index 1aa5c496fb..cacc51132c 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-efficacy.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type LuminousEfficacyMetricUnits = 'lm/W'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LuminousEfficacyUnits = LuminousEfficacyMetricUnits; +export type LuminousEfficacyUnits = 'lm/W'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'lm/W': { name: 'unit.lumens-per-watt', - tags: ['luminous efficacy', 'lighting efficiency', 'lumens per watt', 'lm/W'], + tags: ['lighting efficiency'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/luminous-flux.ts b/ui-ngx/src/app/shared/models/units/luminous-flux.ts index ff1b052919..0d91e372e8 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-flux.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-flux.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type LuminousFluxMetricUnits = 'lm'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LuminousFluxUnits = LuminousFluxMetricUnits; +export type LuminousFluxUnits = 'lm'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'lm': { name: 'unit.lumen', - tags: ['luminous flux', 'total light output', 'lumen', 'lm'], + tags: ['total light output'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts index 7168aab088..da85631822 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type LuminousIntensityMetricUnits = 'cd'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LuminousIntensityUnits = LuminousIntensityMetricUnits; +export type LuminousIntensityUnits = 'cd'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'cd': { name: 'unit.candela', - tags: ['luminous intensity', 'light intensity', 'candela', 'cd'], + tags: ['light intensity'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts index 41e8fade91..c5136698da 100644 --- a/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts +++ b/ui-ngx/src/app/shared/models/units/magnetic-field-gradient.ts @@ -1,18 +1,31 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MagneticFieldGradientUnits = MagneticFieldGradientMetricUnits; -export type MagneticFieldGradientMetricUnits = 'T/m' | 'G/cm'; +export type MagneticFieldGradientUnits = 'T/m' | 'G/cm'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'T/m': { name: 'unit.tesla-per-meter', - tags: ['magnetic field', 'tesla per meter', 'T/m'], to_anchor: 1, }, 'G/cm': { name: 'unit.gauss-per-centimeter', - tags: ['magnetic field', 'gauss per centimeter', 'G/cm'], to_anchor: 0.01, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-field.ts b/ui-ngx/src/app/shared/models/units/magnetic-field.ts deleted file mode 100644 index 8996c7911b..0000000000 --- a/ui-ngx/src/app/shared/models/units/magnetic-field.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type MagneticFieldMetricUnits = 'T' | 'mT' | 'μT' | 'nT' | 'kT' | 'MT' | 'G' | 'kG' | 'γ' | 'A/m' | 'Oe'; - -export type MagneticFieldUnits = MagneticFieldMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'T': { - name: 'unit.tesla', - tags: ['magnetic field', 'magnetic field strength', 'tesla', 'T', 'magnetic flux density'], - to_anchor: 1, - }, - 'mT': { - name: 'unit.millitesla', - tags: ['magnetic field', 'magnetic field strength', 'millitesla', 'mT'], - to_anchor: 0.001, - }, - 'μT': { - name: 'unit.microtesla', - tags: ['magnetic field', 'magnetic field strength', 'microtesla', 'μT'], - to_anchor: 0.000001, - }, - 'nT': { - name: 'unit.nanotesla', - tags: ['magnetic field', 'magnetic field strength', 'nanotesla', 'nT'], - to_anchor: 0.000000001, - }, - 'kT': { - name: 'unit.kilotesla', - tags: ['magnetic field', 'magnetic field strength', 'kilotesla', 'kT'], - to_anchor: 1000, - }, - 'MT': { - name: 'unit.megatesla', - tags: ['magnetic field', 'magnetic field strength', 'megatesla', 'MT'], - to_anchor: 1000000, - }, - 'G': { - name: 'unit.gauss', - tags: ['magnetic field', 'magnetic field strength', 'gauss', 'G', 'magnetic flux density'], - to_anchor: 0.0001, - }, - 'kG': { - name: 'unit.kilogauss', - tags: ['magnetic field', 'magnetic field strength', 'kilogauss', 'kG', 'magnetic flux density'], - to_anchor: 0.1, - }, - 'γ': { - name: 'unit.gamma', - tags: ['magnetic flux density', 'gamma', 'γ'], - to_anchor: 0.000000001, - }, - 'A/m': { - name: 'unit.ampere-per-meter', - tags: ['magnetic field strength', 'magnetic field intensity', 'ampere per meter', 'A/m'], - to_anchor: 0.00000125663706143591, - }, - 'Oe': { - name: 'unit.oersted', - tags: ['magnetic field', 'oersted', 'Oe'], - to_anchor: 0.0001, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts b/ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts new file mode 100644 index 0000000000..4f93ead1be --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-flux-density.ts @@ -0,0 +1,84 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticFluxDensityUnits = 'T' | 'mT' | 'μT' | 'nT' | 'kT' | 'MT' | 'G' | 'kG' | 'γ' | 'A/m' | 'Oe'; + +const METRIC: TbMeasureUnits = { + units: { + T: { + name: 'unit.tesla', + tags: ['magnetic field strength'], + to_anchor: 1, + }, + mT: { + name: 'unit.millitesla', + tags: ['magnetic field strength'], + to_anchor: 0.001, + }, + μT: { + name: 'unit.microtesla', + tags: ['magnetic field strength'], + to_anchor: 0.000001, + }, + nT: { + name: 'unit.nanotesla', + tags: ['magnetic field strength'], + to_anchor: 0.000000001, + }, + kT: { + name: 'unit.kilotesla', + tags: ['magnetic field strength'], + to_anchor: 1000, + }, + MT: { + name: 'unit.megatesla', + tags: ['magnetic field strength'], + to_anchor: 1000000, + }, + G: { + name: 'unit.gauss', + tags: ['magnetic field strength'], + to_anchor: 0.0001, + }, + kG: { + name: 'unit.kilogauss', + tags: ['magnetic field strength'], + to_anchor: 0.1, + }, + γ: { + name: 'unit.gamma', + to_anchor: 0.000000001, + }, + 'A/m': { + name: 'unit.ampere-per-meter', + tags: ['magnetic field strength', 'magnetic field intensity'], + to_anchor: 0.00000125663706143591, + }, + Oe: { + name: 'unit.oersted', + tags: ['magnetic field strength'], + to_anchor: 0.0001, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/magnetic-flux.ts b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts index 467d317d7a..d503f92627 100644 --- a/ui-ngx/src/app/shared/models/units/magnetic-flux.ts +++ b/ui-ngx/src/app/shared/models/units/magnetic-flux.ts @@ -1,37 +1,48 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type MagneticFluxUnits = 'Wb' | 'µWb' | 'mWb' | 'Mx' | 'G·cm²' | 'kG·cm²'; const METRIC: TbMeasureUnits = { units: { - 'Wb': { + Wb: { name: 'unit.weber', - tags: ['magnetic flux', 'weber', 'Wb'], to_anchor: 1, }, - 'µWb': { + µWb: { name: 'unit.microweber', - tags: ['magnetic flux', 'microweber', 'µWb'], to_anchor: 1e-6, }, - 'mWb': { + mWb: { name: 'unit.milliweber', - tags: ['magnetic flux', 'milliweber', 'mWb'], to_anchor: 1e-3, }, - 'Mx': { + Mx: { name: 'unit.maxwell', - tags: ['magnetic flux', 'magnetic field', 'maxwell', 'Mx'], + tags: ['magnetic field'], to_anchor: 1e-8, }, 'G·cm²': { name: 'unit.gauss-square-centimeter', - tags: ['magnetic flux', 'gauss-square centimeter', 'G·cm²'], to_anchor: 1e-8, }, 'kG·cm²': { name: 'unit.kilogauss-square-centimeter', - tags: ['magnetic flux', 'kilogauss-square centimeter', 'kG·cm²'], to_anchor: 1e-5, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-moment.ts b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts index d7771bdf33..670a88a400 100644 --- a/ui-ngx/src/app/shared/models/units/magnetic-moment.ts +++ b/ui-ngx/src/app/shared/models/units/magnetic-moment.ts @@ -1,19 +1,33 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type MagneticMomentMetricUnits = 'A·m²' | 'μB'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MagneticMomentUnits = MagneticMomentMetricUnits; +export type MagneticMomentUnits = 'A·m²' | 'μB'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'A·m²': { name: 'unit.magnetic-dipole-moment', - tags: ['magnetic dipole', 'dipole moment', 'ampere square meter', 'A·m²'], + tags: ['magnetic dipole moment'], to_anchor: 1, }, - 'μB': { + μB: { name: 'unit.bohr-magneton', - tags: ['atomic physics', 'magnetic moment', 'bohr magneton', 'μB'], + tags: ['atomic physics'], to_anchor: 9.274e-24, }, }, diff --git a/ui-ngx/src/app/shared/models/units/magnetic-permeability.ts b/ui-ngx/src/app/shared/models/units/magnetic-permeability.ts new file mode 100644 index 0000000000..73052db8d9 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/magnetic-permeability.ts @@ -0,0 +1,40 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MagneticPermeabilityUnits = 'H/m' | 'G/Oe'; + +const METRIC: TbMeasureUnits = { + units: { + 'H/m': { + name: 'unit.henry-per-meter', + to_anchor: 1, + }, + 'G/Oe': { + name: 'unit.gauss-per-oersted', + tags: ['magnetic field'], + to_anchor: 1/ 795774.715, + }, + }, +}; + + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/mass-fraction.ts b/ui-ngx/src/app/shared/models/units/mass-fraction.ts new file mode 100644 index 0000000000..23136c2447 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/mass-fraction.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MassFractionUnits = '°Bx'; + +const METRIC: TbMeasureUnits = { + units: { + '°Bx': { + name: 'unit.degrees-brix', + tags: ['sugar content', 'fruit ripeness'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/mass.ts b/ui-ngx/src/app/shared/models/units/mass.ts index fbed798da3..75d1d578a6 100644 --- a/ui-ngx/src/app/shared/models/units/mass.ts +++ b/ui-ngx/src/app/shared/models/units/mass.ts @@ -26,37 +26,37 @@ const METRIC: TbMeasureUnits = { units: { ng: { name: 'unit.nanogram', - tags: ['mass', 'weight', 'heaviness', 'load', 'nanogram', 'nanograms', 'ng'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1e-9, }, mcg: { name: 'unit.microgram', - tags: ['mass', 'weight', 'heaviness', 'load', 'μg', 'microgram'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1e-6, }, mg: { name: 'unit.milligram', - tags: ['mass', 'weight', 'heaviness', 'load', 'milligram', 'miligrams', 'mg'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1e-3, }, g: { name: 'unit.gram', - tags: ['mass', 'weight', 'heaviness', 'load', 'gram', 'grams', 'g'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1, }, kg: { name: 'unit.kilogram', - tags: ['mass', 'weight', 'heaviness', 'load', 'kilogram', 'kilograms', 'kg'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1000, }, t: { name: 'unit.tonne', - tags: ['mass', 'weight', 'heaviness', 'load', 'tonne', 'tons', 't'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1000000, }, Da: { name: 'unit.dalton', - tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit', 'dalton', 'Da'], + tags: ['atomic mass unit', 'AMU', 'unified atomic mass unit'], to_anchor: 1.66053906660e-24, }, ct: { @@ -72,47 +72,47 @@ const IMPERIAL: TbMeasureUnits = { units: { oz: { name: 'unit.ounce', - tags: ['mass', 'weight', 'heaviness', 'load', 'ounce', 'ounces', 'oz'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1 / 16, }, lb: { name: 'unit.pound', - tags: ['mass', 'weight', 'heaviness', 'load', 'pound', 'pounds', 'lb'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 1, }, st: { name: 'unit.stone', - tags: ['mass', 'weight', 'heaviness', 'load', 'stone', 'stones', 'st'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 14, }, 'short tons': { name: 'unit.short-tons', - tags: ['mass', 'weight', 'heaviness', 'load', 'short ton', 'short tons'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 2000, }, gr: { name: 'unit.grain', - tags: ['mass', 'measurement', 'grain', 'gr'], + tags: ['measurement'], to_anchor: 1 / 7000, }, dr: { name: 'unit.drachm', - tags: ['mass', 'measurement', 'drachm', 'dr'], + tags: ['measurement'], to_anchor: 1 / 256, }, qr: { name: 'unit.quarter', - tags: ['mass', 'measurement', 'quarter', 'qr'], + tags: ['measurement'], to_anchor: 28, }, cwt: { name: 'unit.hundredweight-count', - tags: ['mass', 'weight', 'heaviness', 'load', 'hundredweight count', 'cwt'], + tags: ['weight', 'heaviness', 'load'], to_anchor: 100, }, slug: { name: 'unit.slug', - tags: ['mass', 'measurement', 'slug'], + tags: ['measurement'], to_anchor: 32.174, }, }, diff --git a/ui-ngx/src/app/shared/models/units/molar-concentration.ts b/ui-ngx/src/app/shared/models/units/molar-concentration.ts new file mode 100644 index 0000000000..e0d3c1ef30 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/molar-concentration.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type MolarConcentrationUnits = 'mol/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'mol/m³': { + name: 'unit.mole-per-cubic-meter', + tags: ['amount of substance per unit volume'], + to_anchor: 1, + } + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/molar-energy.ts b/ui-ngx/src/app/shared/models/units/molar-energy.ts index 41e7b5a75e..99162d314f 100644 --- a/ui-ngx/src/app/shared/models/units/molar-energy.ts +++ b/ui-ngx/src/app/shared/models/units/molar-energy.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type MolarEnergyMetricUnits = 'J/mol'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MolarEnergyUnits = MolarEnergyMetricUnits; +export type MolarEnergyUnits = 'J/mol'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/mol': { name: 'unit.joule-per-mole', - tags: ['molar energy', 'joule per mole', 'J/mol'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts index 104a44b3de..ee2b4b1752 100644 --- a/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/molar-heat-capacity.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type MolarHeatCapacityMetricUnits = 'J/(mol·K)'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type MolarHeatCapacityUnits = MolarHeatCapacityMetricUnits; +export type MolarHeatCapacityUnits = 'J/(mol·K)'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/(mol·K)': { name: 'unit.joule-per-mole-kelvin', - tags: ['molar heat capacity', 'joule per mole-kelvin', 'J/(mol·K)'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/molar-mass.ts b/ui-ngx/src/app/shared/models/units/molar-mass.ts index c9a6b89639..9bbb4ab9e3 100644 --- a/ui-ngx/src/app/shared/models/units/molar-mass.ts +++ b/ui-ngx/src/app/shared/models/units/molar-mass.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type MolarMassUnits = 'kg/mol' | 'g/mol' | 'mg/mol'; @@ -6,17 +22,14 @@ const METRIC: TbMeasureUnits = { units: { 'g/mol': { name: 'unit.gram-per-mole', - tags: ['molar mass', 'gram per mole', 'g/mol'], to_anchor: 1, }, 'kg/mol': { name: 'unit.kilogram-per-mole', - tags: ['molar mass', 'kilogram per mole', 'kg/mol'], to_anchor: 1e3, }, 'mg/mol': { name: 'unit.milligram-per-mole', - tags: ['molar mass', 'milligram per mole', 'mg/mol'], to_anchor: 1e-3, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-humidity.ts b/ui-ngx/src/app/shared/models/units/specific-humidity.ts new file mode 100644 index 0000000000..82f12b26e2 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/specific-humidity.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SpecificHumidityUnits = 'g/kg'; + +const METRIC: TbMeasureUnits = { + units: { + 'g/kg': { + name: 'unit.gram-per-kilogram', + tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c685b6171e..c6e2d31cbc 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5866,18 +5866,54 @@ }, "measures": { "acceleration": "Acceleration", + "air-quality-index": "Air quality index", + "amount-of-substance": "Amount of substance", "angle": "Angle", "angular-acceleration": "Angular acceleration", "area": "Area", + "capacitance": "Capacitance", + "catalytic-activity": "Catalytic activity", + "catalytic-concetration": "Catalytic concetration", "charge": "Charge", + "current-density": "Current density", + "data-transfer-rate": "Data transfer rate", "digital": "Digital", + "dimension-ratio": "Dimension ratio", + "dynamic-viscosity": "Dynamic viscosity", + "earthquake-magnitude": "Earthquake magnitude", "electric-current": "Electric current", + "electric-dipole-moment": "Electric dipole moment", + "electric-field-strength": "Electric field strength", + "electric-flux": "Electric flux", + "electric-permittivity": "Electric permittivity", + "electrical-conductance": "Electrical conductance", + "electrical-conductivity": "Electrical conductivity", "energy": "Energy", "force": "Force", "frequency": "Frequency", + "fuel-efficiency": "fuel efficiency", + "heat-capacity": "Heat capacity", "illuminance": "Illuminance", + "inductance": "Inductance", + "kinematic-viscosity": "Kinematic viscosity", "length": "Length", + "light-exposure": "Light exposure", + "logarithmic-ratio": "Logarithmic ratio", + "luminous-efficacy": "Luminous efficacy", + "luminous-flux": "Luminous flux", + "luminous-intensity": "Luminous intensity", + "magnetic-field-gradient": "Magnetic field gradient", + "magnetic-flux": "Magnetic flux", + "magnetic-flux-density": "Magnetic flux density", + "magnetic-moment": "Magnetic moment", + "magnetic-permeability": "Magnetic permeability", "mass": "Mass", + "mass-fraction": "Mass fraction", + "molar-concetration": "Molar concetration", + "molar-energy": "Molar energy", + "molar-heat-capacity": "Molar heat capacity", + "molar-mass": "Molar mass", + "specific-humidity": "Specific humidity", "temperature": "Temperature", "time": "Time" }, From a34616dd4125ce57009747fa40c5e714cfc1c116 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 6 May 2025 11:13:53 +0300 Subject: [PATCH 14/27] UI: Updated unit definition --- ui-ngx/src/app/shared/models/unit.models.ts | 121 ++++++++++++++++-- .../shared/models/units/absorbed-dose-rate.ts | 35 +++++ .../app/shared/models/units/acceleration.ts | 12 +- ui-ngx/src/app/shared/models/units/acidity.ts | 35 +++++ .../models/units/amount-of-substance.ts | 10 +- .../app/shared/models/units/area-density.ts | 35 +++++ .../app/shared/models/units/capacitance.ts | 20 +-- .../shared/models/units/data-transfer-rate.ts | 10 +- .../shared/models/units/dimension-ratio.ts | 7 +- .../shared/models/units/dynamic-viscosity.ts | 4 +- .../models/units/electric-charge-density.ts | 34 +++++ .../models/units/electric-dipole-moment.ts | 2 +- .../models/units/electric-polarizability.ts | 35 +++++ .../models/units/electrical-conductance.ts | 12 +- .../app/shared/models/units/energy-density.ts | 34 +++++ .../shared/models/units/fuel-efficiency.ts | 2 +- .../app/shared/models/units/illuminance.ts | 2 +- .../models/units/kinematic-viscosity.ts | 4 +- .../shared/models/units/logarithmic-ratio.ts | 6 +- .../app/shared/models/units/luminous-flux.ts | 2 +- .../shared/models/units/luminous-intensity.ts | 2 +- .../models/units/number-concentration.ts | 35 +++++ .../models/units/particle-concentration.ts | 19 --- .../{parts-per.ts => parts-per-million.ts} | 11 +- .../src/app/shared/models/units/percentage.ts | 20 --- ui-ngx/src/app/shared/models/units/ph.ts | 19 --- .../app/shared/models/units/polarization.ts | 21 --- .../app/shared/models/units/power-density.ts | 32 +++-- ui-ngx/src/app/shared/models/units/power.ts | 22 ++-- .../src/app/shared/models/units/pressure.ts | 60 ++++----- .../src/app/shared/models/units/radiance.ts | 18 ++- .../shared/models/units/radiant-intensity.ts | 23 +++- .../app/shared/models/units/radiation-dose.ts | 49 ++++--- .../models/units/radioactive-decay-rate.ts | 25 ---- .../shared/models/units/radioactive-decay.ts | 38 ++++++ .../units/radioactivity-concentration.ts | 25 +++- .../app/shared/models/units/radioactivity.ts | 41 +++--- .../shared/models/units/reciprocal-length.ts | 35 +++++ .../src/app/shared/models/units/resistance.ts | 46 ++++--- .../shared/models/units/reynolds-number.ts | 24 +++- .../app/shared/models/units/signal-level.ts | 45 +++++++ .../shared/models/units/signal-strength.ts | 31 ----- .../app/shared/models/units/solid-angle.ts | 26 +++- .../shared/models/units/specific-energy.ts | 23 +++- .../models/units/specific-heat-capacity.ts | 8 +- .../shared/models/units/specific-humidity.ts | 2 +- .../shared/models/units/specific-volume.ts | 24 +++- ui-ngx/src/app/shared/models/units/speed.ts | 20 +-- .../app/shared/models/units/sugar-content.ts | 19 --- .../models/units/surface-charge-density.ts | 24 +++- .../shared/models/units/surface-tension.ts | 26 +++- .../app/shared/models/units/temperature.ts | 8 +- .../models/units/thermal-conductivity.ts | 23 +++- ui-ngx/src/app/shared/models/units/time.ts | 22 ++-- ui-ngx/src/app/shared/models/units/torque.ts | 6 +- .../src/app/shared/models/units/turbidity.ts | 23 +++- ui-ngx/src/app/shared/models/units/voltage.ts | 16 +-- .../models/units/volume-charge-density.ts | 21 --- .../{volume-flow-rate.ts => volume-flow.ts} | 44 +++---- ui-ngx/src/app/shared/models/units/volume.ts | 42 +++--- .../src/app/shared/models/units/wavenumber.ts | 21 --- .../assets/locale/locale.constant-en_US.json | 43 ++++++- 62 files changed, 1007 insertions(+), 497 deletions(-) create mode 100644 ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/acidity.ts create mode 100644 ui-ngx/src/app/shared/models/units/area-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-charge-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/electric-polarizability.ts create mode 100644 ui-ngx/src/app/shared/models/units/energy-density.ts create mode 100644 ui-ngx/src/app/shared/models/units/number-concentration.ts delete mode 100644 ui-ngx/src/app/shared/models/units/particle-concentration.ts rename ui-ngx/src/app/shared/models/units/{parts-per.ts => parts-per-million.ts} (80%) delete mode 100644 ui-ngx/src/app/shared/models/units/percentage.ts delete mode 100644 ui-ngx/src/app/shared/models/units/ph.ts delete mode 100644 ui-ngx/src/app/shared/models/units/polarization.ts delete mode 100644 ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts create mode 100644 ui-ngx/src/app/shared/models/units/radioactive-decay.ts create mode 100644 ui-ngx/src/app/shared/models/units/reciprocal-length.ts create mode 100644 ui-ngx/src/app/shared/models/units/signal-level.ts delete mode 100644 ui-ngx/src/app/shared/models/units/signal-strength.ts delete mode 100644 ui-ngx/src/app/shared/models/units/sugar-content.ts delete mode 100644 ui-ngx/src/app/shared/models/units/volume-charge-density.ts rename ui-ngx/src/app/shared/models/units/{volume-flow-rate.ts => volume-flow.ts} (56%) delete mode 100644 ui-ngx/src/app/shared/models/units/wavenumber.ts diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index c2763ff632..3fcf742cb0 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -14,12 +14,15 @@ /// limitations under the License. /// +import absorbedDoseRate, { AbsorbedDoseRateUnits } from '@shared/models/units/absorbed-dose-rate'; import acceleration, { AccelerationUnits } from '@shared/models/units/acceleration'; +import acidity, { AcidityUnits } from '@shared/models/units/acidity'; import airQualityIndex, { AirQualityIndexUnits } from '@shared/models/units/air-quality-index'; import amountOfSubstance, { AmountOfSubstanceUnits } from '@shared/models/units/amount-of-substance'; import angle, { AngleUnits } from '@shared/models/units/angle'; import angularAcceleration, { AngularAccelerationUnits } from '@shared/models/units/angular-acceleration'; import area, { AreaUnits } from '@shared/models/units/area'; +import areaDensity, { AreaDensityUnits } from '@shared/models/units/area-density'; import capacitance, { CapacitanceUnits } from '@shared/models/units/capacitance'; import catalyticActivity, { CatalyticActivityUnits } from '@shared/models/units/catalytic-activity'; import catalyticConcentration, { CatalyticConcentrationUnits } from '@shared/models/units/catalytic-concentration'; @@ -31,14 +34,17 @@ import digital, { DigitalUnits } from '@shared/models/units/digital'; import dimensionRatio, { DimensionRatioUnits } from '@shared/models/units/dimension-ratio'; import dynamicViscosity, { DynamicViscosityUnits } from '@shared/models/units/dynamic-viscosity'; import earthquakeMagnitude, { EarthquakeMagnitudeUnits } from '@shared/models/units/earthquake-magnitude'; +import electricChargeDensity, { ElectricChargeDensityUnits } from '@shared/models/units/electric-charge-density'; import electricCurrent, { ElectricCurrentUnits } from '@shared/models/units/electric-current'; import electricDipoleMoment, { ElectricDipoleMomentUnits } from '@shared/models/units/electric-dipole-moment'; import electricFieldStrength, { ElectricFieldStrengthUnits } from '@shared/models/units/electric-field-strength'; import electricFlux, { ElectricFluxUnits } from '@shared/models/units/electric-flux'; import electricPermittivity, { ElectricPermittivityUnits } from '@shared/models/units/electric-permittivity'; +import electricPolarizability, { ElectricPolarizabilityUnits } from '@shared/models/units/electric-polarizability'; import electricalConductance, { ElectricalConductanceUnits } from '@shared/models/units/electrical-conductance'; import electricalConductivity, { ElectricalConductivityUnits } from '@shared/models/units/electrical-conductivity'; import energy, { EnergyUnits } from '@shared/models/units/energy'; +import energyDensity, { EnergyDensityUnits } from '@shared/models/units/energy-density'; import force, { ForceUnits } from '@shared/models/units/force'; import fuelEfficiency, { FuelEfficiencyUnits } from '@shared/models/units/fuel-efficiency'; import frequency, { FrequencyUnits } from '@shared/models/units/frequency'; @@ -64,26 +70,51 @@ import molarConcentration, { MolarConcentrationUnits } from '@shared/models/unit import molarEnergy, { MolarEnergyUnits } from '@shared/models/units/molar-energy'; import molarHeatCapacity, { MolarHeatCapacityUnits } from '@shared/models/units/molar-heat-capacity'; import molarMass, { MolarMassUnits } from '@shared/models/units/molar-mass'; -import partsPer, { PartsPerUnits } from '@shared/models/units/parts-per'; +import numberConcentration, { NumberConcentrationUnits } from '@shared/models/units/number-concentration'; +import partsPerMillion, { PartsPerMillionUnits } from '@shared/models/units/parts-per-million'; import power, { PowerUnits } from '@shared/models/units/power'; +import powerDensity, { PowerDensityUnits } from '@shared/models/units/power-density'; import pressure, { PressureUnits } from '@shared/models/units/pressure'; +import radiance, { RadianceUnits } from '@shared/models/units/radiance'; +import radiantIntensity, { RadiantIntensityUnits } from '@shared/models/units/radiant-intensity'; +import radiationDose, { RadiationDoseUnits } from '@shared/models/units/radiation-dose'; +import radioactiveDecay, { RadioactiveDecayUnits } from '@shared/models/units/radioactive-decay'; +import radioactivity, { RadioactivityUnits } from '@shared/models/units/radioactivity'; +import radioactivityConcentration, { + RadioactivityConcentrationUnits +} from '@shared/models/units/radioactivity-concentration'; +import resistance, { ResistanceUnits } from '@shared/models/units/resistance'; +import reynoldsNumber, { ReynoldsNumberUnits } from '@shared/models/units/reynolds-number'; +import signalLevel, { SignalLevelUnits } from '@shared/models/units/signal-level'; +import solidAngle, { SolidAngleUnits } from '@shared/models/units/solid-angle'; +import specificEnergy, { SpecificEnergyUnits } from '@shared/models/units/specific-energy'; +import specificHeatCapacity, { SpecificHeatCapacityUnits } from '@shared/models/units/specific-heat-capacity'; import specificHumidity, { SpecificHumidityUnits } from '@shared/models/units/specific-humidity'; +import specificVolume, { SpecificVolumeUnits } from '@shared/models/units/specific-volume'; import speed, { SpeedUnits } from '@shared/models/units/speed'; +import surfaceChargeDensity, { SurfaceChargeDensityUnits } from '@shared/models/units/surface-charge-density'; +import surfaceTension, { SurfaceTensionUnits } from '@shared/models/units/surface-tension'; import temperature, { TemperatureUnits } from '@shared/models/units/temperature'; +import thermalConductivity, { ThermalConductivityUnits } from '@shared/models/units/thermal-conductivity'; import time, { TimeUnits } from '@shared/models/units/time'; import torque, { TorqueUnits } from '@shared/models/units/torque'; +import turbidity, { TurbidityUnits } from '@shared/models/units/turbidity'; import voltage, { VoltageUnits } from '@shared/models/units/voltage'; import volume, { VolumeUnits } from '@shared/models/units/volume'; -import volumeFlowRate, { VolumeFlowRateUnits } from '@shared/models/units/volume-flow-rate'; +import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow'; import { TranslateService } from '@ngx-translate/core'; +import reciprocalLength, { ReciprocalLengthUnits } from '@shared/models/units/reciprocal-length'; export type AllMeasuresUnits = + | AbsorbedDoseRateUnits | AccelerationUnits + | AcidityUnits | AirQualityIndexUnits | AmountOfSubstanceUnits | AngleUnits | AngularAccelerationUnits | AreaUnits + | AreaDensityUnits | CapacitanceUnits | CatalyticActivityUnits | CatalyticConcentrationUnits @@ -95,14 +126,17 @@ export type AllMeasuresUnits = | DimensionRatioUnits | DynamicViscosityUnits | EarthquakeMagnitudeUnits + | ElectricChargeDensityUnits | ElectricCurrentUnits | ElectricDipoleMomentUnits | ElectricFieldStrengthUnits | ElectricFluxUnits | ElectricPermittivityUnits + | ElectricPolarizabilityUnits | ElectricalConductanceUnits | ElectricalConductivityUnits | EnergyUnits + | EnergyDensityUnits | ForceUnits | FrequencyUnits | FuelEfficiencyUnits @@ -128,20 +162,42 @@ export type AllMeasuresUnits = | MolarEnergyUnits | MolarHeatCapacityUnits | MolarMassUnits - | PartsPerUnits + | NumberConcentrationUnits + | PartsPerMillionUnits | PowerUnits + | PowerDensityUnits | PressureUnits + | RadianceUnits + | RadiantIntensityUnits + | RadiationDoseUnits + | RadioactiveDecayUnits + | RadioactivityUnits + | RadioactivityConcentrationUnits + | ReciprocalLengthUnits + | ResistanceUnits + | ReynoldsNumberUnits + | SignalLevelUnits + | SolidAngleUnits + | SpecificEnergyUnits + | SpecificHeatCapacityUnits | SpecificHumidityUnits + | SpecificVolumeUnits | SpeedUnits + | SurfaceChargeDensityUnits + | SurfaceTensionUnits | TemperatureUnits + | ThermalConductivityUnits | TimeUnits | TorqueUnits + | TurbidityUnits | VoltageUnits | VolumeUnits - | VolumeFlowRateUnits; + | VolumeFlowUnits; export type AllMeasures = + | 'absorbed-dose-rate' | 'acceleration' + | 'acidity' | 'air-quality-index' | 'amount-of-substance' | 'angle' @@ -158,14 +214,17 @@ export type AllMeasures = | 'dimension-ratio' | 'dynamic-viscosity' | 'earthquake-magnitude' + | 'electric-charge-density' | 'electric-current' | 'electric-dipole-moment' | 'electric-field-strength' | 'electric-flux' | 'electric-permittivity' + | 'electric-polarizability' | 'electrical-conductance' | 'electrical-conductivity' | 'energy' + | 'energy-density' | 'force' | 'frequency' | 'fuel-efficiency' @@ -191,28 +250,51 @@ export type AllMeasures = | 'molar-energy' | 'molar-heat-capacity' | 'molar-mass' - | 'parts-per' + | 'number-concentration' + | 'parts-per-million' | 'power' + | 'power-density' | 'pressure' + | 'radiance' + | 'radiant-intensity' + | 'radiation-dose' + | 'radioactive-decay' + | 'radioactivity' + | 'radioactivity-concentration' + | 'reciprocal-length' + | 'resistance' + | 'reynolds-number' + | 'signal-level' + | 'solid-angle' + | 'specific-energy' + | 'specific-heat-capacity' | 'specific-humidity' + | 'specific-volume' + | 'surface-charge-density' + | 'surface-tension' | 'speed' | 'temperature' + | 'thermal-conductivity' | 'time' | 'torque' + | 'turbidity' | 'voltage' | 'volume' - | 'volume-flow-rate'; + | 'volume-flow'; const allMeasures: Record< AllMeasures, TbMeasure > = Object.freeze({ + 'absorbed-dose-rate': absorbedDoseRate, acceleration, + acidity, 'air-quality-index': airQualityIndex, 'amount-of-substance': amountOfSubstance, angle, 'angular-acceleration': angularAcceleration, area, + 'area-density': areaDensity, capacitance, 'catalytic-activity': catalyticActivity, 'catalytic-concentration': catalyticConcentration, @@ -224,14 +306,17 @@ const allMeasures: Record< 'dimension-ratio': dimensionRatio, 'dynamic-viscosity': dynamicViscosity, 'earthquake-magnitude': earthquakeMagnitude, + 'electric-charge-density': electricChargeDensity, 'electric-current': electricCurrent, 'electric-dipole-moment': electricDipoleMoment, 'electric-field-strength': electricFieldStrength, 'electric-flux': electricFlux, 'electric-permittivity': electricPermittivity, + 'electric-polarizability': electricPolarizability, 'electrical-conductance': electricalConductance, 'electrical-conductivity': electricalConductivity, energy, + 'energy-density': energyDensity, force, frequency, 'fuel-efficiency': fuelEfficiency, @@ -246,6 +331,7 @@ const allMeasures: Record< 'luminous-efficacy': luminousEfficacy, 'luminous-flux': luminousFlux, 'luminous-intensity': luminousIntensity, + 'number-concentration': numberConcentration, 'magnetic-field-gradient': magneticFieldGradient, 'magnetic-flux': magneticFlux, 'magnetic-flux-density': magneticFluxDensity, @@ -257,17 +343,36 @@ const allMeasures: Record< 'molar-energy': molarEnergy, 'molar-heat-capacity': molarHeatCapacity, 'molar-mass': molarMass, - 'parts-per': partsPer, + 'parts-per-million': partsPerMillion, power, + 'power-density': powerDensity, pressure, + radiance, + 'radiant-intensity': radiantIntensity, + 'radiation-dose': radiationDose, + 'radioactive-decay': radioactiveDecay, + radioactivity, + 'radioactivity-concentration': radioactivityConcentration, + 'reciprocal-length': reciprocalLength, + resistance, + 'reynolds-number': reynoldsNumber, + 'signal-level': signalLevel, + 'solid-angle': solidAngle, + 'specific-energy': specificEnergy, + 'specific-heat-capacity': specificHeatCapacity, 'specific-humidity': specificHumidity, + 'specific-volume': specificVolume, speed, + 'surface-charge-density': surfaceChargeDensity, + 'surface-tension': surfaceTension, temperature, + 'thermal-conductivity': thermalConductivity, time, torque, + turbidity, voltage, volume, - 'volume-flow-rate': volumeFlowRate, + 'volume-flow': volumeFlow, }); export enum UnitsType { diff --git a/ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts b/ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts new file mode 100644 index 0000000000..46f52272a1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/absorbed-dose-rate.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AbsorbedDoseRateUnits = 'Gy/s'; + +const METRIC: TbMeasureUnits = { + units: { + 'Gy/s': { + name: 'unit.gy-per-second', + tags: ['radiation dose rate'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/acceleration.ts b/ui-ngx/src/app/shared/models/units/acceleration.ts index a1643020a9..cef91df465 100644 --- a/ui-ngx/src/app/shared/models/units/acceleration.ts +++ b/ui-ngx/src/app/shared/models/units/acceleration.ts @@ -24,24 +24,24 @@ export type AccelerationUnits = AccelerationMetricUnits | AccelerationImperialUn const METRIC: TbMeasureUnits = { ratio: 3.28084, units: { - 'G': { + G: { name: 'unit.g-force', - tags: ['gravity', 'g-force', 'load'], + tags: ['gravity', 'load'], to_anchor: 9.80665, }, 'm/s²': { name: 'unit.meters-per-second-squared', - tags: ['peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'meters per second squared'], + tags: ['peak to peak', 'root mean square (RMS)', 'vibration'], to_anchor: 1, }, - 'Gal': { + Gal: { name: 'unit.gal', tags: ['gravity', 'g-force'], to_anchor: 1, }, 'km/h²': { name: 'unit.kilometer-per-hour-squared', - tags: ['rate of change of velocity', 'kilometer per hour squared'], + tags: ['rate of change of velocity'], to_anchor: 1 / 12960, } } @@ -52,7 +52,7 @@ const IMPERIAL: TbMeasureUnits = { units: { 'ft/s²': { name: 'unit.foot-per-second-squared', - tags: ['acceleration', 'rate of change of velocity', 'foot per second squared', 'ft/s²'], + tags: ['rate of change of velocity'], to_anchor: 1 } } diff --git a/ui-ngx/src/app/shared/models/units/acidity.ts b/ui-ngx/src/app/shared/models/units/acidity.ts new file mode 100644 index 0000000000..9681084a99 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/acidity.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AcidityUnits = 'pH'; + +const METRIC: TbMeasureUnits = { + units: { + pH: { + name: 'unit.ph-level', + tags: [ 'alkalinity', 'neutral', 'acid', 'base', 'soil pH', 'water quality', 'water pH'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts index 1601f5d80a..6dc38ccad6 100644 --- a/ui-ngx/src/app/shared/models/units/amount-of-substance.ts +++ b/ui-ngx/src/app/shared/models/units/amount-of-substance.ts @@ -20,27 +20,27 @@ export type AmountOfSubstanceUnits = 'mol' | 'nmol' | 'μmol' | 'mmol' | 'kmol'; const METRIC: TbMeasureUnits = { units: { - 'mol': { + mol: { name: 'unit.mole', tags: ['chemical amount'], to_anchor: 1, }, - 'nmol': { + nmol: { name: 'unit.nanomole', tags: ['chemical amount'], to_anchor: 0.000000001, }, - 'μmol': { + μmol: { name: 'unit.micromole', tags: ['chemical amount'], to_anchor: 0.000001, }, - 'mmol': { + mmol: { name: 'unit.millimole', tags: ['chemical amount'], to_anchor: 0.001, }, - 'kmol': { + kmol: { name: 'unit.kilomole', tags: ['chemical amount'], to_anchor: 1000, diff --git a/ui-ngx/src/app/shared/models/units/area-density.ts b/ui-ngx/src/app/shared/models/units/area-density.ts new file mode 100644 index 0000000000..3fd0757a45 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/area-density.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type AreaDensityUnits = 'kg/m²'; + +const METRIC: TbMeasureUnits = { + units: { + 'kg/m²': { + name: 'unit.kilogram-per-square-meter', + tags: ['surface density', 'mass per unit area'], + to_anchor: 1 + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/capacitance.ts b/ui-ngx/src/app/shared/models/units/capacitance.ts index f4dc13dc97..8f65fd97b2 100644 --- a/ui-ngx/src/app/shared/models/units/capacitance.ts +++ b/ui-ngx/src/app/shared/models/units/capacitance.ts @@ -20,48 +20,48 @@ export type CapacitanceUnits = 'F' | 'mF' | 'μF' | 'nF' | 'pF' | 'kF' | 'MF' | const METRIC: TbMeasureUnits = { units: { - 'F': { + F: { name: 'unit.farad', tags: ['electric capacitance'], to_anchor: 1, }, - 'mF': { + mF: { name: 'unit.millifarad', tags: ['electric capacitance'], to_anchor: 1e-3, }, - 'μF': { + μF: { name: 'unit.microfarad', tags: ['electric capacitance'], to_anchor: 1e-6, }, - 'nF': { + nF: { name: 'unit.nanofarad', tags: ['electric capacitance'], to_anchor: 1e-9, }, - 'pF': { + pF: { name: 'unit.picofarad', tags: ['electric capacitance'], to_anchor: 1e-12, }, - 'kF': { + kF: { name: 'unit.kilofarad', tags: ['electric capacitance'], to_anchor: 1e3, }, - 'MF': { + MF: { name: 'unit.megafarad', tags: ['electric capacitance'], to_anchor: 1e6, }, - 'GF': { + GF: { name: 'unit.gigafarad', tags: ['electric capacitance'], to_anchor: 1e9, }, - 'TF': { - name: 'unit.terafarad', + TF: { + name: 'unit.terfarad', tags: ['electric capacitance'], to_anchor: 1e12, }, diff --git a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts index d952eaf727..e77576dce2 100644 --- a/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts +++ b/ui-ngx/src/app/shared/models/units/data-transfer-rate.ts @@ -20,23 +20,23 @@ export type DataTransferRateUnits = 'bps' | 'kbps' | 'Mbps' | 'Gbps' | 'Tbps' | const METRIC: TbMeasureUnits = { units: { - 'bps': { + bps: { name: 'unit.bit-per-second', to_anchor: 1, }, - 'kbps': { + kbps: { name: 'unit.kilobit-per-second', to_anchor: 1e3, }, - 'Mbps': { + Mbps: { name: 'unit.megabit-per-second', to_anchor: 1e6, }, - 'Gbps': { + Gbps: { name: 'unit.gigabit-per-second', to_anchor: 1e9, }, - 'Tbps': { + Tbps: { name: 'unit.terabit-per-second', to_anchor: 1e12, }, diff --git a/ui-ngx/src/app/shared/models/units/dimension-ratio.ts b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts index 3deb60a38f..1df77e13ad 100644 --- a/ui-ngx/src/app/shared/models/units/dimension-ratio.ts +++ b/ui-ngx/src/app/shared/models/units/dimension-ratio.ts @@ -16,10 +16,15 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type DimensionRatioUnits = 'm/m'; +export type DimensionRatioUnits = 'm/m' | '%'; const METRIC: TbMeasureUnits = { units: { + '%': { + name: 'unit.percent', + tags: ['power source', 'state of charge (SoC)', 'battery', 'battery level', 'level', 'humidity', 'moisture', 'relative humidity', 'water content', 'soil moisture', 'irrigation', 'water in soil', 'soil water content', 'VWC', 'Volumetric Water Content', 'Total Harmonic Distortion', 'THD', 'power quality', 'UV Transmittance', 'capacity'], + to_anchor: 1, + }, 'm/m': { name: 'unit.meter-per-meter', tags: ['ratio of length to length'], diff --git a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts index e3fe8602b3..bb8a479931 100644 --- a/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/dynamic-viscosity.ts @@ -29,12 +29,12 @@ const METRIC: TbMeasureUnits = { tags: ['fluid mechanics'], to_anchor: 1, }, - 'cP': { + cP: { name: 'unit.centipoise', tags: ['fluid mechanics'], to_anchor: 0.001, }, - 'P': { + P: { name: 'unit.poise', tags: ['fluid mechanics'], to_anchor: 0.1, diff --git a/ui-ngx/src/app/shared/models/units/electric-charge-density.ts b/ui-ngx/src/app/shared/models/units/electric-charge-density.ts new file mode 100644 index 0000000000..0e2e8dfe08 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-charge-density.ts @@ -0,0 +1,34 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricChargeDensityUnits = 'C/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'C/m³': { + name: 'unit.coulomb-per-cubic-meter', + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts index b43e08b857..8a7ecc7379 100644 --- a/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts +++ b/ui-ngx/src/app/shared/models/units/electric-dipole-moment.ts @@ -24,7 +24,7 @@ const METRIC: TbMeasureUnits = { name: 'unit.electric-dipole-moment', to_anchor: 1, }, - 'D': { + D: { name: 'unit.debye', tags: ['polarization'], to_anchor: 3.33564e-30 diff --git a/ui-ngx/src/app/shared/models/units/electric-polarizability.ts b/ui-ngx/src/app/shared/models/units/electric-polarizability.ts new file mode 100644 index 0000000000..2d92ad4cb1 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/electric-polarizability.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ElectricPolarizabilityUnits = 'C·m²/V'; + +const METRIC: TbMeasureUnits = { + units: { + 'C·m²/V': { + name: 'unit.coulomb-per-square-meter-per-volt', + tags: ['electric field'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts index 62241d059f..bbad5d3f5c 100644 --- a/ui-ngx/src/app/shared/models/units/electrical-conductance.ts +++ b/ui-ngx/src/app/shared/models/units/electrical-conductance.ts @@ -20,27 +20,27 @@ export type ElectricalConductanceUnits = 'S' | 'mS' | 'μS' | 'kS' | 'MS' | 'GS' const METRIC: TbMeasureUnits = { units: { - 'S': { + S: { name: 'unit.siemens', to_anchor: 1, }, - 'mS': { + mS: { name: 'unit.millisiemens', to_anchor: 1e-3, }, - 'μS': { + μS: { name: 'unit.microsiemens', to_anchor: 1e-6, }, - 'kS': { + kS: { name: 'unit.kilosiemens', to_anchor: 1e3, }, - 'MS': { + MS: { name: 'unit.megasiemens', to_anchor: 1e6, }, - 'GS': { + GS: { name: 'unit.gigasiemens', to_anchor: 1e9, }, diff --git a/ui-ngx/src/app/shared/models/units/energy-density.ts b/ui-ngx/src/app/shared/models/units/energy-density.ts new file mode 100644 index 0000000000..9b0e2e4a1e --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/energy-density.ts @@ -0,0 +1,34 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type EnergyDensityUnits = 'J/m³'; + +const METRIC: TbMeasureUnits = { + units: { + 'J/m³': { + name: 'unit.joule-per-cubic-meter', + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts index 64eb49cfb2..360b35ed6e 100644 --- a/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts +++ b/ui-ngx/src/app/shared/models/units/fuel-efficiency.ts @@ -39,7 +39,7 @@ const METRIC: TbMeasureUnits = { const IMPERIAL: TbMeasureUnits = { ratio: 0.425144, units: { - 'mpg': { + mpg: { name: 'unit.miles-per-gallon', to_anchor: 0.425144, }, diff --git a/ui-ngx/src/app/shared/models/units/illuminance.ts b/ui-ngx/src/app/shared/models/units/illuminance.ts index 286cd4923d..4063b61fc5 100644 --- a/ui-ngx/src/app/shared/models/units/illuminance.ts +++ b/ui-ngx/src/app/shared/models/units/illuminance.ts @@ -45,7 +45,7 @@ const METRIC: TbMeasureUnits = { const IMPERIAL: TbMeasureUnits = { ratio: 10.76391, units: { - 'fc': { + fc: { name: 'unit.foot-candle', tags: ['illuminance', 'light level'], to_anchor: 1, diff --git a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts index 8d2201d456..c1203ff75b 100644 --- a/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts +++ b/ui-ngx/src/app/shared/models/units/kinematic-viscosity.ts @@ -32,11 +32,11 @@ const METRIC: TbMeasureUnits = { name: 'unit.square-centimeter-per-second', to_anchor: 1e-4, }, - 'St': { + St: { name: 'unit.stoke', to_anchor: 1e-4, }, - 'cSt': { + cSt: { name: 'unit.centistokes', to_anchor: 1e-6, }, diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts index cb34632711..83a26c3030 100644 --- a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts +++ b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts @@ -20,17 +20,17 @@ export type LogarithmicRatioUnits = 'dB' | 'B' | 'Np'; const METRIC: TbMeasureUnits = { units: { - 'dB': { + dB: { name: 'unit.decibel', tags: ['noise level', 'sound level', 'volume', 'acoustics'], to_anchor: 1, }, - 'B': { + B: { name: 'unit.bel', tags: ['power ratio', 'intensity ratio'], to_anchor: 10, }, - 'Np': { + Np: { name: 'unit.neper', tags: ['gain', 'loss', 'attenuation'], to_anchor: 8.685889638, diff --git a/ui-ngx/src/app/shared/models/units/luminous-flux.ts b/ui-ngx/src/app/shared/models/units/luminous-flux.ts index 0d91e372e8..db9c3bc2b6 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-flux.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-flux.ts @@ -20,7 +20,7 @@ export type LuminousFluxUnits = 'lm'; const METRIC: TbMeasureUnits = { units: { - 'lm': { + lm: { name: 'unit.lumen', tags: ['total light output'], to_anchor: 1, diff --git a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts index da85631822..259e64de6f 100644 --- a/ui-ngx/src/app/shared/models/units/luminous-intensity.ts +++ b/ui-ngx/src/app/shared/models/units/luminous-intensity.ts @@ -20,7 +20,7 @@ export type LuminousIntensityUnits = 'cd'; const METRIC: TbMeasureUnits = { units: { - 'cd': { + cd: { name: 'unit.candela', tags: ['light intensity'], to_anchor: 1, diff --git a/ui-ngx/src/app/shared/models/units/number-concentration.ts b/ui-ngx/src/app/shared/models/units/number-concentration.ts new file mode 100644 index 0000000000..b5ad4692bc --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/number-concentration.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type NumberConcentrationUnits = 'particles/mL'; + +const METRIC: TbMeasureUnits = { + units: { + 'particles/mL': { + name: 'unit.particle-density', + tags: ['particle concentration', 'count'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/particle-concentration.ts b/ui-ngx/src/app/shared/models/units/particle-concentration.ts deleted file mode 100644 index 991d5a9f78..0000000000 --- a/ui-ngx/src/app/shared/models/units/particle-concentration.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type ParticleConcentrationUnits = 'particles/mL'; - -const METRIC: TbMeasureUnits = { - units: { - 'particles/mL': { - name: 'unit.particle-density', - tags: ['particle concentration', 'count', 'particles/mL'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/parts-per.ts b/ui-ngx/src/app/shared/models/units/parts-per-million.ts similarity index 80% rename from ui-ngx/src/app/shared/models/units/parts-per.ts rename to ui-ngx/src/app/shared/models/units/parts-per-million.ts index 83f72447df..ca3de46a52 100644 --- a/ui-ngx/src/app/shared/models/units/parts-per.ts +++ b/ui-ngx/src/app/shared/models/units/parts-per-million.ts @@ -16,25 +16,24 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type PartsPerUnits = PartsPerMetricUnits; -export type PartsPerMetricUnits = 'ppm' | 'ppb'; +export type PartsPerMillionUnits = 'ppm' | 'ppb'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { ppm: { name: 'unit.ppm', - tags: ['carbon dioxide', 'co²', 'carbon monoxide', 'co', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc', 'ppm'], + tags: ['carbon dioxide', 'co²', 'carbon monoxide', 'co', 'aqi', 'air quality', 'total volatile organic compounds', 'tvoc'], to_anchor: 1, }, ppb: { name: 'unit.ppb', - tags: ['ozone', 'o³', 'nitrogen dioxide', 'no²', 'sulfur dioxide', 'so²', 'aqi', 'air quality', 'tvoc', 'ppb'], + tags: ['ozone', 'o³', 'nitrogen dioxide', 'no²', 'sulfur dioxide', 'so²', 'aqi', 'air quality', 'tvoc'], to_anchor: 0.001, } }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, }; diff --git a/ui-ngx/src/app/shared/models/units/percentage.ts b/ui-ngx/src/app/shared/models/units/percentage.ts deleted file mode 100644 index 1bd796b0f5..0000000000 --- a/ui-ngx/src/app/shared/models/units/percentage.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type PercentageMetricUnits = '%'; -export type PercentageUnits = PercentageMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - '%': { - name: 'unit.percent', - tags: ['power source', 'state of charge (SoC)', 'battery', 'battery level', 'level', 'humidity', 'moisture', 'percentage', 'relative humidity', 'water content', 'soil moisture', 'irrigation', 'water in soil', 'soil water content', 'VWC', 'Volumetric Water Content', 'Total Harmonic Distortion', 'THD', 'power quality', 'UV Transmittance', '%', 'capacity'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/ph.ts b/ui-ngx/src/app/shared/models/units/ph.ts deleted file mode 100644 index d394d0f8ec..0000000000 --- a/ui-ngx/src/app/shared/models/units/ph.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type PHUnits = 'pH'; - -const METRIC: TbMeasureUnits = { - units: { - pH: { - name: 'unit.ph-level', - tags: ['acidity', 'alkalinity', 'neutral', 'acid', 'base', 'pH', 'soil pH', 'water quality', 'water pH'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/polarization.ts b/ui-ngx/src/app/shared/models/units/polarization.ts deleted file mode 100644 index 09528228a0..0000000000 --- a/ui-ngx/src/app/shared/models/units/polarization.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type PolarizationMetricUnits = 'C·m²/V'; - -export type PolarizationUnits = PolarizationMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'C·m²/V': { - name: 'unit.coulomb-per-square-meter-per-volt', - tags: ['polarization', 'electric field', 'coulomb per square meter per volt', 'C·m²/V'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/power-density.ts b/ui-ngx/src/app/shared/models/units/power-density.ts index 38ec33fd6e..ed3ac37a6c 100644 --- a/ui-ngx/src/app/shared/models/units/power-density.ts +++ b/ui-ngx/src/app/shared/models/units/power-density.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type PowerDensityMetricUnits = 'mW/cm²' | 'W/cm²' | 'kW/cm²' | 'mW/m²' | 'W/m²' | 'kW/m²'; @@ -10,32 +26,32 @@ const METRIC: TbMeasureUnits = { units: { 'mW/cm²': { name: 'unit.milliwatt-per-square-centimeter', - tags: ['power density', 'radiation intensity', 'sunlight intensity', 'signal power', 'intensity', 'milliwatts per square centimeter', 'UV Intensity', 'mW/cm²'], + tags: ['radiation intensity', 'sunlight intensity', 'signal power', 'intensity', 'UV Intensity'], to_anchor: 10000, }, 'W/cm²': { name: 'unit.watt-per-square-centimeter', - tags: ['power density', 'intensity of power', 'watts per square centimeter', 'W/cm²'], + tags: ['intensity of power'], to_anchor: 10000, }, 'kW/cm²': { name: 'unit.kilowatt-per-square-centimeter', - tags: ['power density', 'intensity of power', 'kilowatts per square centimeter', 'kW/cm²'], + tags: ['intensity of power'], to_anchor: 10000000, }, 'mW/m²': { name: 'unit.milliwatt-per-square-meter', - tags: ['power density', 'intensity of power', 'milliwatts per square meter', 'mW/m²'], + tags: ['intensity of power'], to_anchor: 0.001, }, 'W/m²': { name: 'unit.watt-per-square-meter', - tags: ['power density', 'intensity of power', 'watts per square meter', 'W/m²'], + tags: ['intensity of power'], to_anchor: 1, }, 'kW/m²': { name: 'unit.kilowatt-per-square-meter', - tags: ['power density', 'intensity of power', 'kilowatts per square meter', 'kW/m²'], + tags: ['intensity of power'], to_anchor: 1000, }, }, @@ -46,12 +62,12 @@ const IMPERIAL: TbMeasureUnits = { units: { 'W/in²': { name: 'unit.watt-per-square-inch', - tags: ['power density', 'intensity of power', 'watts per square inch', 'W/in²'], + tags: ['intensity of power'], to_anchor: 1, }, 'kW/in²': { name: 'unit.kilowatt-per-square-inch', - tags: ['power density', 'intensity of power', 'kilowatts per square inch', 'kW/in²'], + tags: ['intensity of power'], to_anchor: 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/power.ts b/ui-ngx/src/app/shared/models/units/power.ts index 8b9c5eda81..937ddde675 100644 --- a/ui-ngx/src/app/shared/models/units/power.ts +++ b/ui-ngx/src/app/shared/models/units/power.ts @@ -26,37 +26,37 @@ const METRIC: TbMeasureUnits = { units: { W: { name: 'unit.watt', - tags: ['power', 'horsepower', 'performance', 'watt', 'watts', 'electricity', 'W'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 1, }, μW: { name: 'unit.microwatt', - tags: ['power', 'horsepower', 'performance', 'microwatt', 'microwatts', 'electricity', 'μW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 0.000001, }, mW: { name: 'unit.milliwatt', - tags: ['power', 'horsepower', 'performance', 'milliwatt', 'milliwatts', 'electricity', 'mW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 0.001, }, kW: { name: 'unit.kilowatt', - tags: ['power', 'horsepower', 'performance', 'kilowatt', 'kilowatts', 'electricity', 'kW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 1000, }, MW: { name: 'unit.megawatt', - tags: ['power', 'horsepower', 'performance', 'megawatt', 'megawatts', 'electricity', 'MW'], + tags: [ 'horsepower', 'performance', 'electricity'], to_anchor: 1000000, }, GW: { name: 'unit.gigawatt', - tags: ['power', 'horsepower', 'performance', 'gigawatt', 'gigawatts', 'electricity', 'GW'], + tags: ['horsepower', 'performance', 'electricity'], to_anchor: 1000000000, }, PS: { name: 'unit.metric-horsepower', - tags: ['power', 'performance', 'metric horsepower', 'PS'], + tags: ['performance'], to_anchor: 735.49875, }, }, @@ -67,22 +67,22 @@ const IMPERIAL: TbMeasureUnits = { units: { 'BTU/s': { name: 'unit.btu-per-second', - tags: ['power', 'heat transfer', 'thermal energy', 'british thermal unit per second', 'Btu/s'], + tags: ['heat transfer', 'thermal energy'], to_anchor: 778.16937, }, 'ft-lb/s': { name: 'unit.foot-pound-per-second', - tags: ['power', 'foot-pound per second', 'foot-pounds per second', 'ft-lb/s', 'mechanical power'], + tags: ['mechanical power'], to_anchor: 1, }, hp: { name: 'unit.horsepower', - tags: ['power', 'horsepower', 'performance', 'electricity', 'horsepowers', 'hp'], + tags: ['performance', 'electricity'], to_anchor: 550, }, 'BTU/h': { name: 'unit.btu-per-hour', - tags: ['power', 'heat transfer', 'thermal energy', 'HVAC', 'BTU/h'], + tags: ['heat transfer', 'thermal energy', 'HVAC'], to_anchor: 0.216158, }, }, diff --git a/ui-ngx/src/app/shared/models/units/pressure.ts b/ui-ngx/src/app/shared/models/units/pressure.ts index 7443763754..e57bcd6e78 100644 --- a/ui-ngx/src/app/shared/models/units/pressure.ts +++ b/ui-ngx/src/app/shared/models/units/pressure.ts @@ -36,9 +36,7 @@ export type PressureMetricUnits = | 'N/m²' | 'kN/m²' | 'kgf/m²' - | 'Pa/cm²' - | 'J/m³' - | 'kg/m²'; + | 'Pa/cm²'; export type PressureImperialUnits = 'psi' | 'ksi' | 'inHg' | 'psi/in²' | 'tonf/in²'; @@ -47,104 +45,94 @@ const METRIC: TbMeasureUnits = { units: { Pa: { name: 'unit.pascal', - tags: ['pressure', 'force', 'compression', 'tension', 'pascal', 'pascals', 'Pa', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], + tags: ['force', 'compression', 'tension', 'atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], to_anchor: 0.001, }, kPa: { name: 'unit.kilopascal', - tags: ['pressure', 'force', 'compression', 'tension', 'kilopascal', 'kilopascals', 'kPa'], + tags: ['force', 'compression', 'tension'], to_anchor: 1, }, MPa: { name: 'unit.megapascal', - tags: ['pressure', 'force', 'compression', 'tension', 'megapascal', 'megapascals', 'MPa'], + tags: ['force', 'compression', 'tension'], to_anchor: 1000, }, GPa: { name: 'unit.gigapascal', - tags: ['pressure', 'force', 'compression', 'tension', 'gigapascal', 'gigapascals', 'GPa'], + tags: ['force', 'compression', 'tension'], to_anchor: 1000000, }, hPa: { name: 'unit.hectopascal', - tags: ['pressure', 'force', 'compression', 'tension', 'hectopascal', 'hectopascals', 'hPa', 'atmospheric pressure'], + tags: ['force', 'compression', 'tension', 'atmospheric pressure'], to_anchor: 0.1, }, mbar: { name: 'unit.millibar', - tags: ['pressure', 'force', 'compression', 'tension', 'millibar', 'millibars', 'mbar'], + tags: ['force', 'compression', 'tension'], to_anchor: 0.1, }, mb: { name: 'unit.millibar', - tags: ['atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight', 'mb'], + tags: ['atmospheric pressure', 'air pressure', 'weather', 'altitude', 'flight'], to_anchor: 0.1, }, bar: { name: 'unit.bar', - tags: ['pressure', 'force', 'compression', 'tension', 'bar', 'bars'], + tags: ['force', 'compression', 'tension'], to_anchor: 100, }, kbar: { name: 'unit.kilobar', - tags: ['pressure', 'force', 'compression', 'tension', 'kilobar', 'kilobars', 'kbar'], + tags: ['force', 'compression', 'tension'], to_anchor: 100000, }, Torr: { name: 'unit.torr', - tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'torr'], + tags: ['force', 'compression', 'tension', 'vacuum pressure'], to_anchor: 101325 / 760000, }, mmHg: { name: 'unit.millimeters-of-mercury', - tags: ['pressure', 'force', 'compression', 'tension', 'millimeter of mercury', 'millimeters of mercury', 'mmHg', 'vacuum pressure'], + tags: ['force', 'compression', 'tension', 'vacuum pressure'], to_anchor: 0.133322, }, atm: { name: 'unit.atmospheres', - tags: ['pressure', 'force', 'compression', 'tension', 'atmosphere', 'atmospheres', 'atmospheric pressure', 'atm'], + tags: ['force', 'compression', 'tension', 'atmospheric pressure'], to_anchor: 101.325, }, 'Pa/m²': { name: 'unit.pascal-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square meter', 'Pa/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.001, }, 'N/mm²': { name: 'unit.newton-per-square-millimeter', - tags: ['pressure', 'stress', 'mechanical strength', 'newton per square millimeter', 'N/mm²'], + tags: ['stress', 'mechanical strength'], to_anchor: 1000, }, 'N/m²': { name: 'unit.newton-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'newton per square meter', 'N/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.001, }, 'kN/m²': { name: 'unit.kilonewton-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'kilonewton per square meter', 'kN/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 1, }, 'kgf/m²': { name: 'unit.kilogram-force-per-square-meter', - tags: ['pressure', 'stress', 'mechanical strength', 'kilogram-force per square meter', 'kgf/m²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.00980665, }, 'Pa/cm²': { name: 'unit.pascal-per-square-centimeter', - tags: ['pressure', 'stress', 'mechanical strength', 'pascal per square centimeter', 'Pa/cm²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.1, }, - 'J/m³': { - name: 'unit.joule-per-cubic-meter', - tags: ['energy density', 'joule per cubic meter', 'J/m³'], - to_anchor: 0.001, - }, - 'kg/m²': { - name: 'unit.kilogram-per-square-meter', - tags: ['density','surface density','areal density','mass per unit area','kg/m²'], - to_anchor: 0.00980665 - } }, }; @@ -153,27 +141,27 @@ const IMPERIAL: TbMeasureUnits = { units: { psi: { name: 'unit.pounds-per-square-inch', - tags: ['pressure', 'force', 'compression', 'tension', 'pounds per square inch', 'psi'], + tags: ['force', 'compression', 'tension'], to_anchor: 0.001, }, ksi: { name: 'unit.kilopound-per-square-inch', - tags: ['pressure', 'force', 'compression', 'tension', 'kilopound per square inch', 'kilopounds per square inch', 'ksi'], + tags: ['force', 'compression', 'tension'], to_anchor: 1, }, inHg: { name: 'unit.inch-of-mercury', - tags: ['pressure', 'force', 'compression', 'tension', 'vacuum pressure', 'inHg', 'atmospheric pressure', 'barometric pressure'], + tags: ['force', 'compression', 'tension', 'vacuum pressure','atmospheric pressure', 'barometric pressure'], to_anchor: 0.000491154, }, 'psi/in²': { name: 'unit.pound-per-square-inch', - tags: ['pressure', 'stress', 'mechanical strength', 'pound per square inch', 'psi/in²'], + tags: ['stress', 'mechanical strength'], to_anchor: 0.001, }, 'tonf/in²': { name: 'unit.ton-force-per-square-inch', - tags: ['pressure', 'stress', 'mechanical strength', 'ton-force per square inch', 'tonf/in²'], + tags: ['stress', 'mechanical strength'], to_anchor: 2, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radiance.ts b/ui-ngx/src/app/shared/models/units/radiance.ts index 38fb646fab..878cc86064 100644 --- a/ui-ngx/src/app/shared/models/units/radiance.ts +++ b/ui-ngx/src/app/shared/models/units/radiance.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type RadianceUnits = 'W/(m²·sr)'; @@ -6,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { 'W/(m²·sr)': { name: 'unit.watt-per-square-metre-steradian', - tags: ['radiance', 'radiant flux density', 'wTape per square metre-steradian', 'W/(m²·sr)'], + tags: ['radiant flux density'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radiant-intensity.ts b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts index 78ee45ee5d..3b4958b892 100644 --- a/ui-ngx/src/app/shared/models/units/radiant-intensity.ts +++ b/ui-ngx/src/app/shared/models/units/radiant-intensity.ts @@ -1,13 +1,28 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadiantIntensityUnits = RadiantIntensityMetricUnits; -export type RadiantIntensityMetricUnits = 'W/sr'; +export type RadiantIntensityUnits = 'W/sr'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'W/sr': { name: 'unit.watt-per-steradian', - tags: ['radiant intensity', 'power per unit solid angle', 'watt per steradian', 'W/sr'], + tags: ['power per unit solid angle'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radiation-dose.ts b/ui-ngx/src/app/shared/models/units/radiation-dose.ts index 1619a48780..c7adc05f57 100644 --- a/ui-ngx/src/app/shared/models/units/radiation-dose.ts +++ b/ui-ngx/src/app/shared/models/units/radiation-dose.ts @@ -1,43 +1,58 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadiationDoseMetricUnits = 'Gy' | 'Sv' | 'Rad' | 'Rem' | 'R' | 'C/kg' | 'Gy/s'; -export type RadiationDoseUnits = RadiationDoseMetricUnits; +export type RadiationDoseUnits = 'Gy' | 'Sv' | 'Rad' | 'Rem' | 'R' | 'C/kg' | 'cps'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { - 'Sv': { + Sv: { name: 'unit.sievert', - tags: ['radiation dose', 'sievert', 'radiation dose equivalent', 'Sv'], + tags: ['sievert', 'radiation dose equivalent', 'Sv'], to_anchor: 1, }, - 'Gy': { + Gy: { name: 'unit.gray', - tags: ['radiation dose', 'absorbed dose', 'gray', 'Gy'], + tags: ['absorbed dose', 'gray', 'Gy'], to_anchor: 1, }, - 'Rad': { + Rad: { name: 'unit.rad', - tags: ['radiation dose', 'rad'], + tags: ['rad'], to_anchor: 0.01, }, - 'Rem': { + Rem: { name: 'unit.rem', - tags: ['radiation dose equivalent', 'rem'], + tags: ['radiation dose equivalent'], to_anchor: 0.01, }, - 'R': { + R: { name: 'unit.roentgen', - tags: ['radiation exposure', 'roentgen', 'R'], + tags: ['radiation exposure'], to_anchor: 0.0093, }, 'C/kg': { name: 'unit.coulombs-per-kilogram', - tags: ['radiation exposure', 'dose', 'coulombs per kilogram', 'electric charge-to-mass ratio', 'C/kg'], + tags: ['radiation exposure', 'electric charge-to-mass ratio'], to_anchor: 34, }, - 'Gy/s': { - name: 'unit.gy-per-second', - tags: ['absorbed dose rate', 'radiation dose rate', 'gray per second', 'Gy/s'], + cps: { + name: 'unit.cps', + tags: ['radiation detection'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts b/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts deleted file mode 100644 index d357c11b72..0000000000 --- a/ui-ngx/src/app/shared/models/units/radioactive-decay-rate.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type RadioactiveDecayRateUnits = 'Bq/s' | 'Ci/s'; - -const METRIC: TbMeasureUnits = { - transform: (Bq_s) => Bq_s / 3.7e10, // Convert Bq/s to Ci/s - units: { - 'Bq/s': { - name: 'unit.becquerels-per-second', - tags: ['radioactive decay rate', 'becquerels per second', 'Bq/s'], - to_anchor: 1, - }, - 'Ci/s': { - name: 'unit.curies-per-second', - tags: ['radioactive decay rate', 'curies per second', 'Ci/s'], - to_anchor: 3.7e10, - }, - } -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactive-decay.ts b/ui-ngx/src/app/shared/models/units/radioactive-decay.ts new file mode 100644 index 0000000000..520b81dab6 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/radioactive-decay.ts @@ -0,0 +1,38 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type RadioactiveDecayUnits = 'Bq/s' | 'Ci/s'; + +const METRIC: TbMeasureUnits = { + units: { + 'Bq/s': { + name: 'unit.becquerels-per-second', + to_anchor: 1, + }, + 'Ci/s': { + name: 'unit.curies-per-second', + to_anchor: 3.7e10, + }, + } +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts index 8036332f13..4d6c99bf1e 100644 --- a/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts +++ b/ui-ngx/src/app/shared/models/units/radioactivity-concentration.ts @@ -1,18 +1,33 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadioactivityConcentrationMetricUnits = 'Bq/m³' | 'Ci/L'; -export type RadioactivityConcentrationUnits = RadioactivityConcentrationMetricUnits; +export type RadioactivityConcentrationUnits = 'Bq/m³' | 'Ci/L'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'Bq/m³': { name: 'unit.becquerels-per-cubic-meter', - tags: ['radioactivity', 'radiation', 'becquerels per cubic meter', 'Bq/m³'], + tags: ['radiation'], to_anchor: 1, }, 'Ci/L': { name: 'unit.curies-per-liter', - tags: ['radioactivity', 'radiation', 'curies per liter', 'Ci/L'], + tags: ['radiation'], to_anchor: 3.7e10 * 1000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/radioactivity.ts b/ui-ngx/src/app/shared/models/units/radioactivity.ts index 8db76da7af..ed456ca96a 100644 --- a/ui-ngx/src/app/shared/models/units/radioactivity.ts +++ b/ui-ngx/src/app/shared/models/units/radioactivity.ts @@ -1,33 +1,44 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type RadioactivityMetricUnits = 'Bq' | 'Ci' | 'Rd' | 'dps' | 'cps'; +export type RadioactivityMetricUnits = 'Bq' | 'Ci' | 'Rd' | 'dps'; export type RadioactivityUnits = RadioactivityMetricUnits; const METRIC: TbMeasureUnits = { units: { - 'Bq': { + Bq: { name: 'unit.becquerel', - tags: ['radioactivity', 'decay rate', 'becquerel', 'Bq'], + tags: ['radioactivity', 'decay rate'], to_anchor: 1, }, - 'Ci': { + Ci: { name: 'unit.curie', - tags: ['radioactivity', 'radiation', 'curie', 'Ci'], + tags: ['radiation'], to_anchor: 3.7e10, }, - 'Rd': { + Rd: { name: 'unit.rutherford', - tags: ['radioactive decay', 'radioactivity', 'rutherford', 'Rd'], - to_anchor: 1e6, + tags: ['radioactive decay'], + to_anchor: 3.7e4, }, - 'dps': { + dps: { name: 'unit.dps', - tags: ['radioactive decay', 'radioactivity', 'disintegrations per second', 'dps'], - to_anchor: 1, - }, - cps: { - name: 'unit.cps', - tags: ['radiation detection', 'counts per second', 'cps'], + tags: ['radioactive decay'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/reciprocal-length.ts b/ui-ngx/src/app/shared/models/units/reciprocal-length.ts new file mode 100644 index 0000000000..bfa26f57ec --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/reciprocal-length.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type ReciprocalLengthUnits = 'm⁻¹'; + +const METRIC: TbMeasureUnits = { + units: { + 'm⁻¹': { + name: 'unit.reciprocal-metre', + tags: ['wavenumber', 'wave density', 'wave frequency'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/resistance.ts b/ui-ngx/src/app/shared/models/units/resistance.ts index 6f14faf4c4..0efef06a0a 100644 --- a/ui-ngx/src/app/shared/models/units/resistance.ts +++ b/ui-ngx/src/app/shared/models/units/resistance.ts @@ -1,39 +1,53 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type ResistanceMetricUnits = 'Ω' | 'μΩ' | 'mΩ' | 'kΩ' | 'MΩ' | 'GΩ'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ResistanceUnits = ResistanceMetricUnits; +export type ResistanceUnits = 'Ω' | 'μΩ' | 'mΩ' | 'kΩ' | 'MΩ' | 'GΩ'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { - 'Ω': { + Ω: { name: 'unit.ohm', - tags: ['electrical resistance', 'resistance', 'impedance', 'ohm'], + tags: ['electrical resistance', 'impedance'], to_anchor: 1, }, - 'μΩ': { + μΩ: { name: 'unit.microohm', - tags: ['electrical resistance', 'resistance', 'microohm', 'μΩ'], + tags: ['electrical resistance'], to_anchor: 0.000001, }, - 'mΩ': { + mΩ: { name: 'unit.milliohm', - tags: ['electrical resistance', 'resistance', 'milliohm', 'mΩ'], + tags: ['electrical resistance'], to_anchor: 0.001, }, - 'kΩ': { + kΩ: { name: 'unit.kilohm', - tags: ['electrical resistance', 'resistance', 'kilohm', 'kΩ'], + tags: ['electrical resistance'], to_anchor: 1000, }, - 'MΩ': { + MΩ: { name: 'unit.megohm', - tags: ['electrical resistance', 'resistance', 'megohm', 'MΩ'], + tags: ['electrical resistance'], to_anchor: 1000000, }, - 'GΩ': { + GΩ: { name: 'unit.gigohm', - tags: ['electrical resistance', 'resistance', 'gigohm', 'GΩ'], + tags: ['electrical resistance'], to_anchor: 1000000000, }, }, diff --git a/ui-ngx/src/app/shared/models/units/reynolds-number.ts b/ui-ngx/src/app/shared/models/units/reynolds-number.ts index a9e8b8c01e..6f3b9a9df3 100644 --- a/ui-ngx/src/app/shared/models/units/reynolds-number.ts +++ b/ui-ngx/src/app/shared/models/units/reynolds-number.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type ReynoldsNumberMetricUnits = 'Re'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ReynoldsNumberUnits = ReynoldsNumberMetricUnits; +export type ReynoldsNumberUnits = 'Re'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { Re: { name: 'unit.reynolds', - tags: ['fluid flow regime', 'fluid mechanics', 'reynolds', 'Re'], + tags: ['fluid flow regime', 'fluid mechanics'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/signal-level.ts b/ui-ngx/src/app/shared/models/units/signal-level.ts new file mode 100644 index 0000000000..0ef0c5ce54 --- /dev/null +++ b/ui-ngx/src/app/shared/models/units/signal-level.ts @@ -0,0 +1,45 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; + +export type SignalLevelUnits = 'dBmV' | 'dBm' | 'rssi'; + +const METRIC: TbMeasureUnits = { + units: { + dBmV: { + name: 'unit.dbmV', + tags: ['decibels millivolt', 'voltage level'], + to_anchor: 1, + }, + dBm: { + name: 'unit.dbm', + tags: ['decibel milliwatts', 'output power'], + to_anchor: 1, + }, + rssi: { + name: 'unit.rssi', + tags: ['signal strength', 'received signal strength indicator'], + to_anchor: 1, + }, + }, +}; + +const measure: TbMeasure = { + METRIC, +}; + +export default measure; diff --git a/ui-ngx/src/app/shared/models/units/signal-strength.ts b/ui-ngx/src/app/shared/models/units/signal-strength.ts deleted file mode 100644 index ef6a595ea3..0000000000 --- a/ui-ngx/src/app/shared/models/units/signal-strength.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type SignalStrengthMetricUnits = 'dBmV' | 'dBm' | 'rssi'; - -export type SignalStrengthUnits = SignalStrengthMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'dBmV': { - name: 'unit.dbmV', - tags: ['decibels millivolt', 'voltage level', 'signal', 'dBmV'], - to_anchor: 1, - }, - 'dBm': { - name: 'unit.dbm', - tags: ['decibel milliwatts', 'output power', 'signal', 'dBm'], - to_anchor: 1, - }, - 'rssi': { - name: 'unit.rssi', - tags: ['signal strength', 'signal level', 'received signal strength indicator', 'rssi', 'dBm'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/solid-angle.ts b/ui-ngx/src/app/shared/models/units/solid-angle.ts index c339884b65..c220c41098 100644 --- a/ui-ngx/src/app/shared/models/units/solid-angle.ts +++ b/ui-ngx/src/app/shared/models/units/solid-angle.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type SolidAngleMetricUnits = 'sr'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SolidAngleUnits = SolidAngleMetricUnits; +export type SolidAngleUnits = 'sr'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { - 'sr': { + sr: { name: 'unit.steradian', - tags: ['solid angle', 'spatial extent', 'steradian', 'sr'], + tags: ['spatial extent'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-energy.ts b/ui-ngx/src/app/shared/models/units/specific-energy.ts index 3ca9d44221..3787f1fb7a 100644 --- a/ui-ngx/src/app/shared/models/units/specific-energy.ts +++ b/ui-ngx/src/app/shared/models/units/specific-energy.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type SpecificEnergyMetricUnits = 'J/kg'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SpecificEnergyUnits = SpecificEnergyMetricUnits; +export type SpecificEnergyUnits = 'J/kg'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/kg': { name: 'unit.joule-per-kilogram', - tags: ['specific energy', 'specific energy capacity', 'joule per kilogram', 'J/kg'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts index 9bdf38e80c..595d1ca7ff 100644 --- a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts @@ -1,14 +1,12 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SpecificHeatCapacityMetricUnits = 'J/(kg·K)'; +export type SpecificHeatCapacityUnits = 'J/(kg·K)'; -export type SpecificHeatCapacityUnits = SpecificHeatCapacityMetricUnits; - -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'J/(kg·K)': { name: 'unit.joule-per-kilogram-kelvin', - tags: ['specific heat capacity', 'heat capacity per unit mass and temperature', 'joule per kilogram-kelvin', 'J/(kg·K)'], + tags: ['heat capacity per unit mass and temperature'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-humidity.ts b/ui-ngx/src/app/shared/models/units/specific-humidity.ts index 82f12b26e2..9a93f513da 100644 --- a/ui-ngx/src/app/shared/models/units/specific-humidity.ts +++ b/ui-ngx/src/app/shared/models/units/specific-humidity.ts @@ -22,7 +22,7 @@ const METRIC: TbMeasureUnits = { units: { 'g/kg': { name: 'unit.gram-per-kilogram', - tags: ['humidity', 'moisture', 'specific humidity', 'g/kg'], + tags: ['humidity', 'moisture'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/specific-volume.ts b/ui-ngx/src/app/shared/models/units/specific-volume.ts index 9968454e41..adef7b4b00 100644 --- a/ui-ngx/src/app/shared/models/units/specific-volume.ts +++ b/ui-ngx/src/app/shared/models/units/specific-volume.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type SpecificVolumeMetricUnits = 'm³/kg'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SpecificVolumeUnits = SpecificVolumeMetricUnits; +export type SpecificVolumeUnits = 'm³/kg'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'm³/kg': { name: 'unit.cubic-meter-per-kilogram', - tags: ['specific volume', 'volume per unit mass', 'cubic meter per kilogram', 'm³/kg'], + tags: ['volume per unit mass'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/speed.ts b/ui-ngx/src/app/shared/models/units/speed.ts index f44553a7a7..b930b0739c 100644 --- a/ui-ngx/src/app/shared/models/units/speed.ts +++ b/ui-ngx/src/app/shared/models/units/speed.ts @@ -26,22 +26,22 @@ const METRIC: TbMeasureUnits = { units: { 'm/s': { name: 'unit.meter-per-second', - tags: ['speed', 'velocity', 'pace', 'meter per second', 'm/s', 'peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'wind speed', 'weather'], + tags: ['velocity', 'pace', 'peak', 'peak to peak', 'root mean square (RMS)', 'vibration', 'wind speed', 'weather'], to_anchor: 3.6, }, 'km/h': { name: 'unit.kilometer-per-hour', - tags: ['speed', 'velocity', 'pace', 'kilometer per hour', 'km/h'], + tags: ['velocity', 'pace'], to_anchor: 1, }, 'mm/min': { name: 'unit.millimeters-per-minute', - tags: ['feed rate', 'cutting feed rate', 'millimeters per minute', 'mm/min'], + tags: ['feed rate', 'cutting feed rate'], to_anchor: 0.06, }, 'mm/s': { name: 'unit.millimeters-per-second', - tags: ['velocity', 'speed', 'vibration rate', 'millimeters per second', 'mm/s'], + tags: ['velocity', 'vibration rate'], to_anchor: 0.0036, }, }, @@ -52,27 +52,27 @@ const IMPERIAL: TbMeasureUnits = { units: { mph: { name: 'unit.mile-per-hour', - tags: ['speed', 'velocity', 'pace', 'mile per hour', 'mph'], + tags: ['velocity', 'pace'], to_anchor: 1, }, kt: { name: 'unit.knot', - tags: ['speed', 'velocity', 'pace', 'knot', 'knots', 'kt'], + tags: ['velocity', 'pace'], to_anchor: 1.150779, }, 'ft/s': { name: 'unit.foot-per-second', - tags: ['speed', 'velocity', 'pace', 'foot per second', 'ft/s'], - to_anchor: 0.681818, // 1 ft/s ≈ 0.681818 mph + tags: ['velocity', 'pace'], + to_anchor: 0.681818, }, 'ft/min': { name: 'unit.foot-per-minute', - tags: ['speed', 'velocity', 'pace', 'foot per minute', 'ft/min'], + tags: ['velocity', 'pace'], to_anchor: 0.0113636, }, 'in/h': { name: 'unit.inch-per-hour', - tags: ['speed', 'velocity', 'pace', 'inch per hour', 'in/h'], + tags: ['velocity', 'pace'], to_anchor: 0.00001578, }, }, diff --git a/ui-ngx/src/app/shared/models/units/sugar-content.ts b/ui-ngx/src/app/shared/models/units/sugar-content.ts deleted file mode 100644 index 1badaf3ce0..0000000000 --- a/ui-ngx/src/app/shared/models/units/sugar-content.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type SugarContentUnits = '°Bx'; - -const METRIC: TbMeasureUnits = { - units: { - '°Bx': { - name: 'unit.degrees-brix', - tags: ['sugar content', 'fruit ripeness', 'Bx'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/surface-charge-density.ts b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts index 306649db3c..d0640bcf4e 100644 --- a/ui-ngx/src/app/shared/models/units/surface-charge-density.ts +++ b/ui-ngx/src/app/shared/models/units/surface-charge-density.ts @@ -1,14 +1,28 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type SurfaceChargeDensityMetricUnits = 'C/m²'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SurfaceChargeDensityUnits = SurfaceChargeDensityMetricUnits; +export type SurfaceChargeDensityUnits = 'C/m²'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'C/m²': { name: 'unit.coulomb-per-square-meter', - tags: ['electric surface charge density', 'coulomb per square meter', 'C/m²'], + tags: ['electric surface charge density'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/surface-tension.ts b/ui-ngx/src/app/shared/models/units/surface-tension.ts index b1c5593354..718b8113c1 100644 --- a/ui-ngx/src/app/shared/models/units/surface-tension.ts +++ b/ui-ngx/src/app/shared/models/units/surface-tension.ts @@ -1,20 +1,34 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type SurfaceTensionMetricUnits = 'N/m'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type SurfaceTensionhUnits = SurfaceTensionMetricUnits; +export type SurfaceTensionUnits = 'N/m'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'N/m': { name: 'unit.newton-per-meter', - tags: ['linear density', 'force per unit length', 'newton per meter', 'N/m'], + tags: ['linear density', 'force per unit length'], to_anchor: 1, }, }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, }; diff --git a/ui-ngx/src/app/shared/models/units/temperature.ts b/ui-ngx/src/app/shared/models/units/temperature.ts index 67f17f0a4b..59d11f1eed 100644 --- a/ui-ngx/src/app/shared/models/units/temperature.ts +++ b/ui-ngx/src/app/shared/models/units/temperature.ts @@ -28,12 +28,12 @@ const METRIC: TbMeasureUnits = { units: { '°C': { name: 'unit.celsius', - tags: ['temperature', 'heat', 'cold', 'warmth', 'degrees', 'celsius', 'shipment condition', '°C'], + tags: ['heat', 'cold', 'warmth', 'degrees', 'shipment condition'], to_anchor: 1, }, K: { name: 'unit.kelvin', - tags: ['temperature', 'heat', 'cold', 'warmth', 'degrees', 'kelvin', 'K', 'color quality', 'white balance', 'color temperature'], + tags: ['heat', 'cold', 'warmth', 'degrees', 'color quality', 'white balance', 'color temperature'], to_anchor: 1, anchor_shift: 273.15, }, @@ -45,12 +45,12 @@ const IMPERIAL: TbMeasureUnits = { units: { '°F': { name: 'unit.fahrenheit', - tags: ['temperature', 'heat', 'cold', 'warmth', 'degrees', 'fahrenheit', '°F'], + tags: ['heat', 'cold', 'warmth', 'degrees'], to_anchor: 1, }, '°R': { name: 'unit.rankine', - tags: ['temperature', 'heat', 'cold', 'warmth', 'Rankine', '°R'], + tags: ['heat', 'cold', 'warmth'], to_anchor: 1, anchor_shift: 459.67, }, diff --git a/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts index 3c27727919..5407614164 100644 --- a/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts +++ b/ui-ngx/src/app/shared/models/units/thermal-conductivity.ts @@ -1,14 +1,27 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// -export type ThermalConductivityMetricUnits = 'W/(m·K)'; +import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type ThermalConductivityUnits = ThermalConductivityMetricUnits; +export type ThermalConductivityUnits = 'W/(m·K)'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { 'W/(m·K)': { name: 'unit.watt-per-meter-kelvin', - tags: ['thermal conductivity', 'watt per meter-kelvin', 'W/(m·K)'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/time.ts b/ui-ngx/src/app/shared/models/units/time.ts index 944bfafbe7..2833080230 100644 --- a/ui-ngx/src/app/shared/models/units/time.ts +++ b/ui-ngx/src/app/shared/models/units/time.ts @@ -36,52 +36,52 @@ const METRIC: TbMeasureUnits = { units: { ns: { name: 'unit.nanosecond', - tags: ['time', 'duration', 'interval', 'ns'], + tags: ['duration', 'interval'], to_anchor: 1 / 1000000000 }, - 'μs': { + μs: { name: 'unit.microsecond', - tags: ['time', 'duration', 'interval', 'h'], + tags: ['duration', 'interval'], to_anchor: 1 / 1000000 }, ms: { name: 'unit.millisecond', - tags: ['time', 'duration', 'interval', 'ms'], + tags: ['duration', 'interval'], to_anchor: 1 / 1000 }, s: { name: 'unit.second', - tags: ['time', 'duration', 'interval', 'second', 'sec'], + tags: ['duration', 'interval'], to_anchor: 1, }, min: { name: 'unit.minute', - tags: ['time', 'duration', 'interval', 'minute', 'min'], + tags: ['duration', 'interval'], to_anchor: 60, }, h: { name: 'unit.hour', - tags: ['time', 'duration', 'interval', 'h'], + tags: ['duration', 'interval'], to_anchor: 60 * 60, }, d: { name: 'unit.day', - tags: ['time', 'duration', 'interval', 'd'], + tags: ['duration', 'interval'], to_anchor: 60 * 60 * 24, }, wk: { name: 'unit.week', - tags: ['time', 'duration', 'interval', 'wk'], + tags: ['duration', 'interval'], to_anchor: 60 * 60 * 24 * 7, }, mo: { name: 'unit.month', - tags: ['time', 'duration', 'interval', 'mo'], + tags: ['duration', 'interval'], to_anchor: (60 * 60 * 24 * daysInYear) / 12, }, yr: { name: 'unit.year', - tags: ['time', 'duration', 'interval', 'yr'], + tags: ['duration', 'interval'], to_anchor: 60 * 60 * 24 * daysInYear, }, } diff --git a/ui-ngx/src/app/shared/models/units/torque.ts b/ui-ngx/src/app/shared/models/units/torque.ts index fa830a0a4a..03ef7e7208 100644 --- a/ui-ngx/src/app/shared/models/units/torque.ts +++ b/ui-ngx/src/app/shared/models/units/torque.ts @@ -26,7 +26,7 @@ const METRIC: TbMeasureUnits = { units: { Nm: { name: 'unit.newton-meter', - tags: ['torque', 'rotational force', 'newton meter', 'Nm'], + tags: ['rotational force', 'newton meter', 'Nm'], to_anchor: 1, }, }, @@ -37,12 +37,12 @@ const IMPERIAL: TbMeasureUnits = { units: { 'lbf-ft': { name: 'unit.foot-pounds', - tags: ['torque', 'rotational force', 'foot-pound', 'foot-pounds', 'ft·lbf'], + tags: ['rotational force'], to_anchor: 1, }, 'in·lbf': { name: 'unit.inch-pounds', - tags: ['torque', 'rotational force', 'inch-pounds', 'inch-pound', 'in·lbf'], + tags: ['rotational force'], to_anchor: 1 / 12, }, }, diff --git a/ui-ngx/src/app/shared/models/units/turbidity.ts b/ui-ngx/src/app/shared/models/units/turbidity.ts index 92b1122437..3d5e752ef7 100644 --- a/ui-ngx/src/app/shared/models/units/turbidity.ts +++ b/ui-ngx/src/app/shared/models/units/turbidity.ts @@ -1,13 +1,28 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type TurbidityUnits = TurbidityMetricUnits; -export type TurbidityMetricUnits = 'NTU'; +export type TurbidityUnits = 'NTU'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { units: { NTU: { name: 'unit.turbidity', - tags: ['water turbidity', 'water clarity', 'Nephelometric Turbidity Units', 'NTU'], + tags: ['water turbidity', 'water clarity', 'Nephelometric Turbidity Units'], to_anchor: 1, }, }, diff --git a/ui-ngx/src/app/shared/models/units/voltage.ts b/ui-ngx/src/app/shared/models/units/voltage.ts index 54f0ec2c20..10c079551c 100644 --- a/ui-ngx/src/app/shared/models/units/voltage.ts +++ b/ui-ngx/src/app/shared/models/units/voltage.ts @@ -23,37 +23,37 @@ const METRIC: TbMeasureUnits = { units: { pV: { name: 'unit.picovolt', - tags: ['voltage', 'volts', 'picovolt', 'pV'], + tags: ['volts'], to_anchor: 1e-12, }, nV: { name: 'unit.nanovolt', - tags: ['voltage', 'volts', 'nanovolt', 'nV'], + tags: ['volts'], to_anchor: 1e-9, }, μV: { name: 'unit.microvolt', - tags: ['electric potential', 'electric tension', 'voltage', 'microvolt', 'microvolts', 'μV'], + tags: ['electric potential', 'electric tension'], to_anchor: 1e-6, }, mV: { name: 'unit.millivolt', - tags: ['electric potential', 'electric tension', 'voltage', 'millivolt', 'millivolts', 'mV'], - to_anchor: 0.001, // 1 mV = 1e-3 V + tags: ['electric potential', 'electric tension'], + to_anchor: 0.001, }, V: { name: 'unit.volt', - tags: ['electric potential', 'electric tension', 'voltage', 'volt', 'volts', 'V', 'power source', 'battery', 'battery level'], + tags: ['electric potential', 'electric tension', 'power source', 'battery', 'battery level'], to_anchor: 1, }, kV: { name: 'unit.kilovolt', - tags: ['electric potential', 'electric tension', 'voltage', 'kilovolt', 'kilovolts', 'kV'], + tags: ['electric potential', 'electric tension'], to_anchor: 1000, }, MV: { name: 'unit.megavolt', - tags: ['electric potential', 'electric tension', 'voltage', 'megavolt', 'megavolts', 'MV'], + tags: ['electric potential', 'electric tension'], to_anchor: 1e6, }, }, diff --git a/ui-ngx/src/app/shared/models/units/volume-charge-density.ts b/ui-ngx/src/app/shared/models/units/volume-charge-density.ts deleted file mode 100644 index 0b172962fd..0000000000 --- a/ui-ngx/src/app/shared/models/units/volume-charge-density.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type VolumeChargeDensityMetricUnits = 'C/m³'; - -export type VolumeChargeDensityUnits = VolumeChargeDensityMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'C/m³': { - name: 'unit.coulomb-per-cubic-meter', - tags: ['electric charge density', 'coulomb per cubic meter', 'C/m³'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/app/shared/models/units/volume-flow-rate.ts b/ui-ngx/src/app/shared/models/units/volume-flow.ts similarity index 56% rename from ui-ngx/src/app/shared/models/units/volume-flow-rate.ts rename to ui-ngx/src/app/shared/models/units/volume-flow.ts index fd1f25c1f4..0f9fa44520 100644 --- a/ui-ngx/src/app/shared/models/units/volume-flow-rate.ts +++ b/ui-ngx/src/app/shared/models/units/volume-flow.ts @@ -16,9 +16,9 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type VolumeFlowRateUnits = VolumeFlowRateMetricUnits | VolumeFlowRateImperialUnits; +export type VolumeFlowUnits = VolumeFlowMetricUnits | VolumeFlowImperialUnits; -export type VolumeFlowRateMetricUnits = +export type VolumeFlowMetricUnits = | 'dm³/s' | 'mL/min' | 'L/s' @@ -27,86 +27,86 @@ export type VolumeFlowRateMetricUnits = | 'm³/s' | 'm³/hr'; -export type VolumeFlowRateImperialUnits = +export type VolumeFlowImperialUnits = | 'fl-oz/s' | 'ft³/s' | 'ft³/min' | 'gal/hr' | 'GPM'; -const METRIC: TbMeasureUnits = { +const METRIC: TbMeasureUnits = { ratio: 33.8140227, units: { + 'L/s': { + name: 'unit.liter-per-second', + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], + to_anchor: 1, + }, 'dm³/s': { name: 'unit.cubic-decimeter-per-second', - tags: ['volume flow', 'cubic decimeter per second', 'dm3/s'], + tags: ['cubic decimeter per second'], to_anchor: 1, }, 'mL/min': { name: 'unit.milliliters-per-minute', - tags: ['volume flow', 'flow rate', 'fluid dynamics', 'milliliters per minute', 'mL/min'], + tags: ['flow rate', 'fluid dynamics'], to_anchor: 1 / 60000, }, - 'L/s': { - name: 'unit.liter-per-second', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per second', 'L/s'], - to_anchor: 1, - }, 'L/min': { name: 'unit.liter-per-minute', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'liter per minute', 'L/min'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 1 / 60, }, 'L/hr': { name: 'unit.liters-per-hour', - tags: ['volume flow', 'fuel consumption', 'liter per hour', 'L/hr'], + tags: ['fuel consumption'], to_anchor: 1 / 3600, }, 'm³/s': { name: 'unit.cubic-meters-per-second', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per second', 'm³/s'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 1000, }, 'm³/hr': { name: 'unit.cubic-meters-per-hour', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'cubic meters per hour', 'm³/hr'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 5 / 18, }, }, }; -const IMPERIAL: TbMeasureUnits = { +const IMPERIAL: TbMeasureUnits = { ratio: 1 / 33.8140227, units: { 'fl-oz/s': { name: 'unit.fluid-ounce-per-second', - tags: ['volume flow', 'fluid ounce per second', 'fl-oz/s'], + tags: ['fluid ounce per second', 'fl-oz/s'], to_anchor: 1, }, 'ft³/s': { name: 'unit.cubic-foot-per-second', - tags: ['volume flow', 'flow rate', 'fluid flow', 'cubic foot per second', 'cubic feet per second', 'ft³/s'], + tags: ['flow rate', 'fluid flow'], to_anchor: 957.506, }, 'ft³/min': { name: 'unit.cubic-foot-per-minute', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'CFM', 'flow rate', 'fluid flow', 'cubic foot per minute', 'ft³/min'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate', 'CFM', 'flow rate', 'fluid flow'], to_anchor: 957.506 / 60, }, 'gal/hr': { name: 'unit.gallons-per-hour', - tags: ['volume flow', 'fuel consumption', 'gallons per hour', 'gal/hr'], + tags: ['fuel consumption'], to_anchor: 128 / 3600, }, 'GPM': { name: 'unit.gallons-per-minute', - tags: ['volume flow', 'airflow', 'ventilation', 'HVAC', 'gas flow rate', 'gallons per minute', 'GPM'], + tags: ['airflow', 'ventilation', 'HVAC', 'gas flow rate'], to_anchor: 128 / 60, }, }, }; -const measure: TbMeasure = { +const measure: TbMeasure = { METRIC, IMPERIAL, }; diff --git a/ui-ngx/src/app/shared/models/units/volume.ts b/ui-ngx/src/app/shared/models/units/volume.ts index 92e1a2dfa7..42b78ad8f2 100644 --- a/ui-ngx/src/app/shared/models/units/volume.ts +++ b/ui-ngx/src/app/shared/models/units/volume.ts @@ -48,42 +48,42 @@ const METRIC: TbMeasureUnits = { units: { 'mm³': { name: 'unit.cubic-millimeter', - tags: ['volume', 'capacity', 'extent', 'cubic millimeter', 'mm³'], + tags: ['capacity', 'extent'], to_anchor: 1 / 1000000, }, 'cm³': { name: 'unit.cubic-centimeter', - tags: ['volume', 'capacity', 'extent', 'cubic centimeter', 'cubic centimeters', 'cm³'], + tags: ['capacity', 'extent'], to_anchor: 1 / 1000, }, µL: { name: 'unit.microliter', - tags: ['volume', 'liquid measurement', 'microliter', 'µL'], + tags: ['liquid measurement'], to_anchor: 0.000001, }, mL: { name: 'unit.milliliter', - tags: ['volume', 'capacity', 'extent', 'milliliter', 'milliliters', 'mL'], + tags: ['capacity', 'extent'], to_anchor: 1 / 1000, }, L: { name: 'unit.liter', - tags: ['volume', 'capacity', 'extent', 'liter', 'liters', 'l'], + tags: ['capacity', 'extent'], to_anchor: 1, }, hL: { name: 'unit.hectoliter', - tags: ['volume', 'capacity', 'extent', 'hectoliter', 'hectoliters', 'hl'], + tags: ['capacity', 'extent'], to_anchor: 100, }, 'm³': { name: 'unit.cubic-meter', - tags: ['volume', 'capacity', 'extent', 'cubic meter', 'cubic meters', 'm³'], + tags: ['capacity', 'extent'], to_anchor: 1000, }, 'km³': { name: 'unit.cubic-kilometer', - tags: ['volume', 'capacity', 'extent', 'cubic kilometer', 'cubic kilometers', 'km³'], + tags: ['capacity', 'extent'], to_anchor: 1000000000000, }, }, @@ -94,67 +94,67 @@ const IMPERIAL: TbMeasureUnits = { units: { tsp: { name: 'unit.teaspoon', - tags: ['volume', 'cooking measurement', 'tsp'], + tags: ['cooking measurement'], to_anchor: 1 / 6, }, tbsp: { name: 'unit.tablespoon', - tags: ['volume', 'cooking measurement', 'tbsp'], + tags: ['cooking measurement'], to_anchor: 1 / 2, }, 'in³': { name: 'unit.cubic-inch', - tags: ['volume', 'capacity', 'extent', 'cubic inch', 'cubic inches', 'in³'], + tags: ['capacity', 'extent'], to_anchor: 0.55411, }, 'fl-oz': { name: 'unit.fluid-ounce', - tags: ['volume', 'capacity', 'extent', 'fluid ounce', 'fluid ounces', 'fl-oz'], + tags: ['capacity', 'extent'], to_anchor: 1, }, cup: { name: 'unit.cup', - tags: ['volume', 'cooking measurement', 'cup'], + tags: ['cooking measurement'], to_anchor: 8, }, pt: { name: 'unit.pint', - tags: ['volume', 'capacity', 'extent', 'pint', 'pints', 'pt'], + tags: ['capacity', 'extent'], to_anchor: 16, }, qt: { name: 'unit.quart', - tags: ['volume', 'capacity', 'extent', 'quart', 'quarts', 'qt'], + tags: ['capacity', 'extent'], to_anchor: 32, }, gal: { name: 'unit.gallon', - tags: ['volume', 'capacity', 'extent', 'gallon', 'gallons', 'gal'], + tags: ['capacity', 'extent'], to_anchor: 128, }, 'ft³': { name: 'unit.cubic-foot', - tags: ['volume', 'capacity', 'extent', 'cubic foot', 'cubic feet', 'ft³'], + tags: ['capacity', 'extent'], to_anchor: 957.506, }, 'yd³': { name: 'unit.cubic-yard', - tags: ['volume', 'capacity', 'extent', 'cubic yard', 'cubic yards', 'yd³'], + tags: ['capacity', 'extent'], to_anchor: 25852.7, }, bbl: { name: 'unit.oil-barrels', - tags: ['volume', 'capacity', 'extent', 'oil barrel', 'oil barrels', 'bbl'], + tags: ['capacity', 'extent'], to_anchor: 5376, }, gi: { name: 'unit.gill', - tags: ['volume', 'liquid measurement', 'gi'], + tags: ['liquid measurement'], to_anchor: 4, }, hhd: { name: 'unit.hogshead', - tags: ['volume', 'liquid measurement', 'hhd'], + tags: ['liquid measurement'], to_anchor: 8064, }, }, diff --git a/ui-ngx/src/app/shared/models/units/wavenumber.ts b/ui-ngx/src/app/shared/models/units/wavenumber.ts deleted file mode 100644 index 6745601328..0000000000 --- a/ui-ngx/src/app/shared/models/units/wavenumber.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; - -export type WavenumberMetricUnits = 'm⁻¹'; - -export type WavenumberUnits = WavenumberMetricUnits; - -const METRIC: TbMeasureUnits = { - units: { - 'm⁻¹': { - name: 'unit.reciprocal-metre', - tags: ['wavenumber', 'wave density', 'wave frequency', 'm⁻¹'], - to_anchor: 1, - }, - }, -}; - -const measure: TbMeasure = { - METRIC, -}; - -export default measure; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c6e2d31cbc..3798e96a9b 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -5865,30 +5865,37 @@ "HYBRID": "Hybrid" }, "measures": { + "absorbed-dose-rate": "Absorbed dose rate", "acceleration": "Acceleration", + "acidity": "Acidity", "air-quality-index": "Air quality index", "amount-of-substance": "Amount of substance", "angle": "Angle", "angular-acceleration": "Angular acceleration", "area": "Area", + "area-density": "Area density", "capacitance": "Capacitance", "catalytic-activity": "Catalytic activity", - "catalytic-concetration": "Catalytic concetration", + "catalytic-concentration": "Catalytic concentration", "charge": "Charge", "current-density": "Current density", "data-transfer-rate": "Data transfer rate", + "density": "Density", "digital": "Digital", "dimension-ratio": "Dimension ratio", "dynamic-viscosity": "Dynamic viscosity", "earthquake-magnitude": "Earthquake magnitude", + "electric-charge-density": "Electric charge density", "electric-current": "Electric current", "electric-dipole-moment": "Electric dipole moment", "electric-field-strength": "Electric field strength", "electric-flux": "Electric flux", "electric-permittivity": "Electric permittivity", + "electric-polarizability": "Electric polarizability", "electrical-conductance": "Electrical conductance", "electrical-conductivity": "Electrical conductivity", "energy": "Energy", + "energy-density": "Energy density", "force": "Force", "frequency": "Frequency", "fuel-efficiency": "fuel efficiency", @@ -5898,6 +5905,7 @@ "kinematic-viscosity": "Kinematic viscosity", "length": "Length", "light-exposure": "Light exposure", + "linear-charge-density": "Linear charge density", "logarithmic-ratio": "Logarithmic ratio", "luminous-efficacy": "Luminous efficacy", "luminous-flux": "Luminous flux", @@ -5909,13 +5917,41 @@ "magnetic-permeability": "Magnetic permeability", "mass": "Mass", "mass-fraction": "Mass fraction", - "molar-concetration": "Molar concetration", + "molar-concentration": "Molar concentration", "molar-energy": "Molar energy", "molar-heat-capacity": "Molar heat capacity", "molar-mass": "Molar mass", + "number-concentration": "Number concentration", + "parts-per-million": "Parts per million", + "power": "Power", + "power-density": "Power density", + "pressure": "Pressure", + "radiance": "Radiance", + "radiant-intensity": "Radiant intensity", + "radiation-dose": "Radiation dose", + "radioactive-decay": "Radioactive decay", + "radioactivity": "Radioactivity", + "radioactivity-concentration": "Radioactivity concentration", + "reciprocal-length": "Reciprocal length", + "resistance": "Resistance", + "reynolds-number": "Reynolds number", + "signal-level": "Signal level", + "solid-angle": "Solid angle", + "specific-energy": "Specific energy", + "specific-heat-capacity": "Specific heat capacity", "specific-humidity": "Specific humidity", + "specific-volume": "Specific volume", + "speed": "Speed", + "surface-charge-density": "Surface charge density", + "surface-tension": "Surface tension", "temperature": "Temperature", - "time": "Time" + "thermal-conductivity": "Thermal conductivity", + "time": "Time", + "torque": "Torque", + "turbidity": "Turbidity", + "voltage": "Voltage", + "volume": "Volume", + "volume-flow": "Volume flow" }, "millimeter": "Millimeter", "centimeter": "Centimeter", @@ -6098,6 +6134,7 @@ "ampere-hours": "Ampere-hours", "kiloampere-hours": "Kiloampere-hours", "nanoampere": "Nanoampere", + "picoampere": "Picoampere", "microampere": "Microampere", "milliampere": "Milliampere", "ampere": "Ampere", From 2e65c79184e2261451287522a149efb758ce1e77 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 6 May 2025 18:42:38 +0300 Subject: [PATCH 15/27] UI: Updated unit definition --- ui-ngx/src/app/core/services/unit.service.ts | 6 +- .../shared/components/unit-input.component.ts | 12 ++-- ui-ngx/src/app/shared/models/unit.models.ts | 69 +++++-------------- .../app/shared/models/units/acceleration.ts | 4 +- ui-ngx/src/app/shared/models/units/length.ts | 6 ++ .../shared/models/units/logarithmic-ratio.ts | 4 +- .../assets/locale/locale.constant-en_US.json | 3 +- 7 files changed, 40 insertions(+), 64 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 3d1fc4de0a..9e656907f3 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -44,10 +44,8 @@ export class UnitService { takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); - console.warn(this.converter?.listUnits()); - console.warn(this.converter?.listUnits('temperature')); - console.warn(this.converter?.listUnits('temperature', UnitSystem.METRIC)); - console.warn(this.converter?.listUnits(null, UnitSystem.IMPERIAL)); + console.warn(this.converter.listUnits()); + console.warn(this.converter.listUnits(null, UnitSystem.IMPERIAL)); }); } diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 9b582d1266..9ec990a0da 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -31,7 +31,7 @@ import { } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormControl, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, of, shareReplay } from 'rxjs'; -import { AllMeasures, searchUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; import { UnitService } from '@core/services/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; @@ -247,9 +247,13 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { const filterValue = searchText.trim().toUpperCase() - return units - .map(measure => [measure[0], searchUnits(measure[1], filterValue)] as [AllMeasures, UnitInfo[]]) - .filter((measure) => measure[1].length > 0); + return units.reduce((result: Array<[AllMeasures, Array]>, [measure, unitInfos]) => { + const filteredUnits = unitInfos.filter(unit => unit.searchText.toUpperCase().includes(filterValue)); + if (filteredUnits.length > 0) { + result.push([measure, filteredUnits]); + } + return result; + }, []); } return units; } diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 3fcf742cb0..57a2f26f6e 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -83,6 +83,7 @@ import radioactivity, { RadioactivityUnits } from '@shared/models/units/radioact import radioactivityConcentration, { RadioactivityConcentrationUnits } from '@shared/models/units/radioactivity-concentration'; +import reciprocalLength, { ReciprocalLengthUnits } from '@shared/models/units/reciprocal-length'; import resistance, { ResistanceUnits } from '@shared/models/units/resistance'; import reynoldsNumber, { ReynoldsNumberUnits } from '@shared/models/units/reynolds-number'; import signalLevel, { SignalLevelUnits } from '@shared/models/units/signal-level'; @@ -103,7 +104,6 @@ import voltage, { VoltageUnits } from '@shared/models/units/voltage'; import volume, { VolumeUnits } from '@shared/models/units/volume'; import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow'; import { TranslateService } from '@ngx-translate/core'; -import reciprocalLength, { ReciprocalLengthUnits } from '@shared/models/units/reciprocal-length'; export type AllMeasuresUnits = | AbsorbedDoseRateUnits @@ -388,6 +388,7 @@ export interface UnitInfo { system: UnitSystem; name: string; tags: string[]; + searchText: string; } export enum UnitSystem { @@ -423,43 +424,21 @@ export interface TbMeasureUnits { units?: Partial>; } -export interface Conversion { - abbr: TUnits; - measure: TMeasures; +export interface UnitCacheInfo { system: UnitSystem; + measure: AllMeasures; unit: Unit; + abbr: AllMeasuresUnits; + searchText: string; } -export type UnitCache = Map; - -const searchUnitTags = (unit: UnitInfo, searchText: string): boolean => - !!unit.tags.find(t => t.toUpperCase().includes(searchText)); - -export const searchUnits = (_units: Array, searchText: string): Array => _units.filter( - u => u.abbr.toUpperCase().includes(searchText) || - u.name.toUpperCase().includes(searchText) || - searchUnitTags(u, searchText) -); +export type UnitCache = Map; type Entries = [S, T[keyof T]]; export class Converter { private readonly measureData: Record>; - private unitCache: Map< - string, - { - system: UnitSystem; - measure: AllMeasures; - unit: Unit; - abbr: AllMeasuresUnits; - } - >; + private unitCache: UnitCache; constructor( measures: Record>, @@ -534,7 +513,7 @@ export class Converter { return null; } - getUnit(abbr: AllMeasuresUnits | string): Conversion | null { + getUnit(abbr: AllMeasuresUnits | string): UnitCacheInfo | null { return this.unitCache.get(abbr) ?? null; } @@ -565,22 +544,15 @@ export class Converter { continue; } - for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { - results.push( - this.describeUnit({ - abbr, - measure: name as AllMeasures, - system, - unit, - }) - ); + for (const abbr of Object.keys(units) as AllMeasuresUnits[]) { + results.push(this.describe(abbr)); } } } return results; } - unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure | never { + unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { const results: UnitInfoGroupByMeasure = {}; const measures = measureName @@ -604,28 +576,22 @@ export class Converter { continue; } - for (const [abbr, unit] of Object.entries(units) as [AllMeasuresUnits, Unit][]) { - results[name].push( - this.describeUnit({ - abbr, - measure: name as AllMeasures, - system, - unit, - }) - ); + for (const abbr of Object.keys(units) as AllMeasuresUnits[]) { + results[name].push(this.describe(abbr)); } } } return results; } - private describeUnit(unit: Conversion): UnitInfo { + private describeUnit(unit: UnitCacheInfo): UnitInfo { return { abbr: unit.abbr, measure: unit.measure, system: unit.system, name: unit.unit.name, - tags: unit.unit.tags + tags: unit.unit.tags, + searchText: unit.searchText }; } @@ -671,6 +637,7 @@ function buildUnitCache(measures: Record = { ratio: 3.28084, units: { - G: { + 'g₀': { name: 'unit.g-force', tags: ['gravity', 'load'], to_anchor: 9.80665, diff --git a/ui-ngx/src/app/shared/models/units/length.ts b/ui-ngx/src/app/shared/models/units/length.ts index 549a66c3e6..57847fb71d 100644 --- a/ui-ngx/src/app/shared/models/units/length.ts +++ b/ui-ngx/src/app/shared/models/units/length.ts @@ -27,6 +27,7 @@ export type LengthImperialUnits = | 'fathom' | 'mi' | 'nmi' + | 'pouce' | 'thou' | 'barleycorn' | 'hand' @@ -122,6 +123,11 @@ const IMPERIAL: TbMeasureUnits = { tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nautical mile'], to_anchor: 6076.12, }, + pouce: { + name: 'unit.paris-inch', + tags: ['level', 'height', 'distance', 'width', 'gap', 'depth', 'nautical mile'], + to_anchor: 1.0657, + }, thou: { name: 'unit.thou', tags: ['measurement'], diff --git a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts index 83a26c3030..709eb44e35 100644 --- a/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts +++ b/ui-ngx/src/app/shared/models/units/logarithmic-ratio.ts @@ -16,7 +16,7 @@ import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; -export type LogarithmicRatioUnits = 'dB' | 'B' | 'Np'; +export type LogarithmicRatioUnits = 'dB' | 'bel' | 'Np'; const METRIC: TbMeasureUnits = { units: { @@ -25,7 +25,7 @@ const METRIC: TbMeasureUnits = { tags: ['noise level', 'sound level', 'volume', 'acoustics'], to_anchor: 1, }, - B: { + bel: { name: 'unit.bel', tags: ['power ratio', 'intensity ratio'], to_anchor: 10, diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3798e96a9b..aa4ef10837 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6376,7 +6376,8 @@ "rotation-per-minute": "Rotation per minute", "degrees-brix": "Degrees Brix", "katal": "Katal", - "katal-per-cubic-metre": "Katal per Cubic Metre" + "katal-per-cubic-metre": "Katal per Cubic Metre", + "paris-inch": "Paris inch" }, "user": { "user": "User", From 76237337b2daf7ca5a85e97c631d39e48bf80869 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 9 May 2025 09:25:23 +0300 Subject: [PATCH 16/27] UI: Add new widhet type parameters supportsUnitConversion --- .../basic/cards/aggregated-data-key-row.component.html | 4 +++- .../basic/cards/aggregated-data-key-row.component.ts | 7 ++++++- .../widget/config/basic/common/data-key-row.component.html | 1 + .../widget/config/basic/common/data-key-row.component.ts | 7 ++++++- .../components/widget/config/datasource.component.html | 1 + .../home/components/widget/config/datasource.component.ts | 4 ++++ .../common/key/data-key-config-dialog.component.html | 1 + .../common/key/data-key-config-dialog.component.ts | 1 + .../lib/settings/common/key/data-key-config.component.html | 1 + .../lib/settings/common/key/data-key-config.component.ts | 4 ++++ .../widget/lib/settings/common/key/data-keys.component.ts | 7 ++++++- .../home/components/widget/widget-component.service.ts | 3 +++ .../home/components/widget/widget-config.component.html | 1 + ui-ngx/src/app/shared/models/widget.models.ts | 1 + 14 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html index 58df05162b..9b77abf74f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html @@ -47,7 +47,9 @@ />
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index d790b1d477..23b700ccbf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -123,6 +123,10 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType); } + get supportsUnitConversion(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; + } + private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, @@ -222,7 +226,8 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn callbacks: this.callbacks, hideDataKeyName: true, hideDataKeyLabel: true, - hideDataKeyColor: true + hideDataKeyColor: true, + supportsUnitConversion: this.supportsUnitConversion } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html index 2ed3afa3bf..ace382d228 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html @@ -69,6 +69,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index 7997e332d5..0b3b8107f6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -225,6 +225,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan return this.hasAdditionalLatestDataKeys && this.keyRowFormGroup.get('latest').value === true; } + get supportsUnitConversion(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; + } + private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, @@ -337,7 +341,8 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan hideDataKeyLabel: this.hideDataKeyLabel, hideDataKeyColor: this.hideDataKeyColor, hideDataKeyUnits: this.hideDataKeyUnits || !this.displayUnitsOrDigits, - hideDataKeyDecimals: this.hideDataKeyDecimals || !this.displayUnitsOrDigits + hideDataKeyDecimals: this.hideDataKeyDecimals || !this.displayUnitsOrDigits, + supportsUnitConversion: this.supportsUnitConversion } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index f7bb0662e9..9059decca4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -71,6 +71,7 @@ [hideDataKeyUnits]="hideDataKeyUnits" [hideDataKeyDecimals]="hideDataKeyDecimals" [maxDataKeys]="maxDataKeys" + [supportsUnitConversion]="supportsUnitConversion" [optDataKeys]="isDataKeysOptional(datasourceFormGroup.get('type').value)" [simpleDataKeysLabel]="!hasAdditionalLatestDataKeys" [aliasController]="aliasController" diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts index 5a519c18d1..48b31439db 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.ts @@ -135,6 +135,10 @@ export class DatasourceComponent implements ControlValueAccessor, OnInit, Valida return this.widgetConfigComponent.modelValue?.dataKeySettingsFunction; } + public get supportsUnitConversion(): boolean { + return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; + } + public get dashboard(): Dashboard { return this.widgetConfigComponent.dashboard; } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html index 49f578079b..de54ff86d4 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.html @@ -50,6 +50,7 @@ [hideDataKeyColor]="data.hideDataKeyColor" [hideDataKeyUnits]="data.hideDataKeyUnits" [hideDataKeyDecimals]="data.hideDataKeyDecimals" + [supportsUnitConversion]="data.supportsUnitConversion" formControlName="dataKey">
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts index 2b11c62632..829402a53f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config-dialog.component.ts @@ -56,6 +56,7 @@ export interface DataKeyConfigDialogData { hideDataKeyColor?: boolean; hideDataKeyUnits?: boolean; hideDataKeyDecimals?: boolean; + supportsUnitConversion?: boolean } @Component({ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html index 0af5159846..68a2b5b851 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html @@ -49,6 +49,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts index c85a8f0ba6..c7b65bfc83 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.ts @@ -153,6 +153,10 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con @coerceBoolean() hideDataKeyDecimals = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @ViewChild('keyInput') keyInput: ElementRef; @ViewChild('funcBodyEdit', {static: false}) funcBodyEdit: JsFuncComponent; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts index a9be1fd352..a1c388845f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-keys.component.ts @@ -169,6 +169,10 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange @coerceBoolean() simpleDataKeysLabel = false; + @Input() + @coerceBoolean() + supportsUnitConversion = false; + @Input() aliasController: IAliasController; @@ -610,7 +614,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange hideDataKeyLabel: this.hideDataKeyLabel, hideDataKeyColor: this.hideDataKeyColor, hideDataKeyUnits: this.hideDataKeyUnits, - hideDataKeyDecimals: this.hideDataKeyDecimals + hideDataKeyDecimals: this.hideDataKeyDecimals, + supportsUnitConversion: this.supportsUnitConversion } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts index c0f37b3a37..c8b9eb963b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts @@ -662,6 +662,9 @@ export class WidgetComponentService { if (isUndefined(result.typeParameters.targetDeviceOptional)) { result.typeParameters.targetDeviceOptional = false; } + if (isUndefined(result.typeParameters.supportsUnitConversion)) { + result.typeParameters.supportsUnitConversion = false; + } if (isDefinedAndNotNull(result.typeParameters.additionalWidgetActionTypes)) { if (Array.isArray(result.typeParameters.additionalWidgetActionTypes)) { result.typeParameters.additionalWidgetActionTypes = result.typeParameters.additionalWidgetActionTypes.filter(type => WidgetActionType[type]); diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index efa1dffe0b..77764ba408 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -291,6 +291,7 @@
widget-config.units-by-default
diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 21d7927d16..221358d8c8 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -196,6 +196,7 @@ export interface WidgetTypeParameters { dataKeySettingsFunction?: DataKeySettingsFunction; displayRpcMessageToast?: boolean; targetDeviceOptional?: boolean; + supportsUnitConversion?: boolean; additionalWidgetActionTypes?: WidgetActionType[]; } From 9662b263e4f48934a33e93e7d960c85b6c1ccb1d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 9 May 2025 10:19:37 +0300 Subject: [PATCH 17/27] UI: Refactoring ValueFormatProcessor and fixed unit input component --- ui-ngx/src/app/core/services/unit.service.ts | 6 +- .../common/key/data-key-config.component.html | 2 +- .../components/unit-input.component.html | 8 +- .../shared/components/unit-input.component.ts | 74 ++++++------- ui-ngx/src/app/shared/models/unit.models.ts | 13 +++ .../shared/models/widget-settings.models.ts | 104 ++++++++++-------- 6 files changed, 111 insertions(+), 96 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 9e656907f3..026ac0f3fe 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -88,11 +88,11 @@ export class UnitService { return this.converter.getUnitConverter(unit as string, to); } - getTargetUnitSymbol(unit: TbUnitMapping): string { + getTargetUnitSymbol(unit: TbUnitMapping | string): string { if (isObject(unit)) { - return isNotEmptyStr(unit[this.currentUnitSystem]) ? unit[this.currentUnitSystem] : unit.from; + return isNotEmptyStr(unit[this.currentUnitSystem]) ? unit[this.currentUnitSystem] : (unit as TbUnitMapping).from; } - return null; + return typeof unit === 'string' ? unit : null; } convertUnitValue(value: number, unit: TbUnitMapping): number; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html index 68a2b5b851..73c1865cc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/key/data-key-config.component.html @@ -49,7 +49,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index e577db6395..e1ab29fab3 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -22,11 +22,11 @@ [class.!pointer-events-none]="disabled" (focusin)="onFocus()" [matAutocomplete]="unitsAutocomplete" - [matAutocompleteDisabled]="allowConverted"> + [matAutocompleteDisabled]="supportsUnitConversion"> +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts index 70e85f26b5..910035a615 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts @@ -47,6 +47,7 @@ import { import { isDefinedAndNotNull, isNumeric } from '@core/utils'; import { WidgetComponent } from '@home/components/widget/widget.component'; import tinycolor from 'tinycolor2'; +import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-slider-widget', @@ -115,6 +116,8 @@ export class SliderWidgetComponent extends showTicks = true; ticksStyle: ComponentStyle = {}; + tickMinText: number; + tickMaxText: number; sliderStep: number = undefined; @@ -137,7 +140,8 @@ export class SliderWidgetComponent extends private utils: UtilsService, private widgetComponent: WidgetComponent, protected cd: ChangeDetectorRef, - private elementRef: ElementRef) { + private elementRef: ElementRef, + private unitService: UnitService) { super(cd); } @@ -180,6 +184,8 @@ export class SliderWidgetComponent extends if (this.showTicks) { this.ticksStyle = textStyle(this.settings.ticksFont); this.ticksStyle.color = this.settings.ticksColor; + this.tickMinText = this.unitService.convertUnitValue(this.settings.tickMin, this.settings.valueUnits); + this.tickMaxText = this.unitService.convertUnitValue(this.settings.tickMax, this.settings.valueUnits); } if (this.settings.showTickMarks) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html index b32c7b5ab1..d1b01c8607 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/dynamic-form/dynamic-form-property-panel.component.html @@ -380,6 +380,11 @@ +
+ + {{ 'dynamic-form.property.support-unit-conversion' | translate }} + +
dynamic-form.property.default-value
{ for (const property of properties) { @@ -213,6 +218,9 @@ export const cleanupFormProperty = (property: FormProperty): FormProperty => { delete property.htmlClassList; delete property.htmlContent; } + if (property.type !== FormPropertyType.units) { + delete property.supportsUnitConversion; + } for (const key of Object.keys(property)) { const val = property[key]; if (isUndefinedOrNull(val) || isEmptyStr(val)) { @@ -276,9 +284,9 @@ export const toPropertyGroups = (properties: FormProperty[], customTranslate: CustomTranslatePipe, sanitizer: DomSanitizer): FormPropertyGroup[] => { const groups: {title: string, properties: FormProperty[]}[] = []; - for (let property of properties) { + for (const property of properties) { if (!property.group) { - let group = groups.length ? groups[groups.length - 1] : null; + const group = groups.length ? groups[groups.length - 1] : null; if (group && !group.title) { group.properties.push(property); } else { @@ -311,7 +319,7 @@ const toPropertyContainers = (properties: FormProperty[], customTranslate: CustomTranslatePipe, sanitizer: DomSanitizer): FormPropertyContainer[] => { const result: FormPropertyContainer[] = []; - for (let property of properties) { + for (const property of properties) { if (property.type === FormPropertyType.array) { const propertyArray: FormPropertyArray = { property, @@ -382,7 +390,7 @@ const toPropertyContainers = (properties: FormProperty[], } } } - for (let container of result.filter(c => + for (const container of result.filter(c => c.type === FormPropertyContainerType.row && !c.switch && c.properties?.length === 1)) { const property = container.properties[0]; if (isInputFieldPropertyType(property.type)) { diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index c904f07575..971c888991 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -54,7 +54,7 @@ import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { UnitService } from '@core/services/unit.service'; -import { TbUnit, TbUnitConverter, TbUnitMapping } from '@shared/models/unit.models'; +import { TbUnit, TbUnitConverter } from '@shared/models/unit.models'; export type ComponentStyle = {[klass: string]: any}; @@ -932,7 +932,7 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor { protected settings: ValueFormatSettings) { super($injector, settings); const unitService = this.$injector.get(UnitService); - const unit = settings.units as TbUnitMapping; + const unit = settings.units; this.unitSymbol = settings.ignoreUnitSymbol ? null : unitService.getTargetUnitSymbol(unit); this.unitConverter = unitService.geUnitConverter(unit); @@ -942,10 +942,7 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor { format(value: any): string { if (isDefinedAndNotNull(value) && isNumeric(value)) { - let formatted = Number(value); - if (this.unitConverter) { - formatted = this.unitConverter(value); - } + const formatted = this.unitConverter(Number(value)); return this.formatValue(formatted); } return value ?? ''; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 73946df6f6..5bfc6c0309 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1790,7 +1790,8 @@ "array-item": "Array item", "item-type": "Item type", "item-name": "Item name", - "no-items": "No items" + "no-items": "No items", + "support-unit-conversion": "Support unit conversion" }, "clear-form": "Clear form", "clear-form-prompt": "Are you sure you want to remove all form properties?", From 111ad28816b3b7e192b17f075b7d113fa7bc14f9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 13 May 2025 17:34:24 +0300 Subject: [PATCH 20/27] UI: Refactoring search in unit component --- ui-ngx/src/app/core/services/unit.service.ts | 3 - ...uid-level-card-basic-config.component.html | 8 +- ...iquid-level-card-basic-config.component.ts | 4 +- ...-level-card-widget-settings.component.html | 8 +- ...id-level-card-widget-settings.component.ts | 4 +- ...convert-unit-settings-panel.component.html | 82 ++++++----- ...convert-unit-settings-panel.component.scss | 5 +- .../convert-unit-settings-panel.component.ts | 4 +- .../components/unit-input.component.html | 2 +- .../shared/components/unit-input.component.ts | 69 +++++++-- ui-ngx/src/app/shared/models/unit.models.ts | 135 +++++++++--------- .../assets/locale/locale.constant-en_US.json | 22 +-- 12 files changed, 200 insertions(+), 146 deletions(-) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 86f74e78b5..5d60e80390 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -46,8 +46,6 @@ export class UnitService { takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); - console.warn(this.converter.listUnits()); - console.warn(this.converter.listUnits(null, UnitSystem.IMPERIAL)); }); } @@ -61,7 +59,6 @@ export class UnitService { } else { this.currentUnitSystem = this.getUnitSystemByTimezone(); } - console.warn('[Unit system] setUnitSystem', this.currentUnitSystem); } getUnits(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfo[] { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html index d2752aebab..c270b17551 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.html @@ -125,7 +125,7 @@
widgets.liquid-level-card.datasource-units
@@ -143,7 +143,7 @@ @@ -199,7 +199,7 @@
@@ -270,7 +270,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts index 3a64cf6b1d..6cd016a85d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/indicator/liquid-level-card-basic-config.component.ts @@ -56,7 +56,7 @@ import { ShapesTranslations, updatedFormSettingsValidators } from '@home/components/widget/lib/indicator/liquid-level-widget.models'; -import { getSourceTbUnitSymbol, UnitsType } from '@shared/models/unit.models'; +import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; import { map, share, tap } from 'rxjs/operators'; @@ -116,8 +116,6 @@ export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigCompon shapesImageMap: Map = new Map(); ShapesTranslationMap = ShapesTranslations; - unitsType = UnitsType; - levelCardWidgetConfigForm: FormGroup; valuePreviewFn = this._valuePreviewFn.bind(this); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html index 88f62477dd..70eec82e2d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.html @@ -83,7 +83,7 @@
widgets.liquid-level-card.datasource-units
@@ -100,7 +100,7 @@ @@ -156,7 +156,7 @@
@@ -239,7 +239,7 @@ {{ 'widgets.liquid-level-card.level' | translate }}
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts index 843c4470b5..386ada1cab 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/indicator/liquid-level-card-widget-settings.component.ts @@ -44,7 +44,7 @@ import { ShapesTranslations, updatedFormSettingsValidators } from '@home/components/widget/lib/indicator/liquid-level-widget.models'; -import { getSourceTbUnitSymbol, UnitsType } from '@shared/models/unit.models'; +import { getSourceTbUnitSymbol } from '@shared/models/unit.models'; import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; import { Observable, of, ReplaySubject } from 'rxjs'; @@ -96,8 +96,6 @@ export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsCompon shapesImageMap: Map = new Map(); ShapesTranslationMap = ShapesTranslations; - unitsType = UnitsType; - levelCardWidgetSettingsForm: FormGroup; valuePreviewFn = this._valuePreviewFn.bind(this); diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html index 7c935728a3..3f14ba290c 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -16,48 +16,60 @@ -->
-
unit.convert.units-conversion-settings
+
unit.conversion.unit-settings
-
unit.convert.convert-from
+
unit.conversion.source-unit
-
- -
- {{ 'unit.convert.convert-unit' | translate }} +
+ +
+ {{ 'unit.conversion.enable-unit-conversion' | translate }}
-
-
-
unit.convert.to-metric
- - -
-
-
unit.convert.to-imperial
- - -
-
-
unit.convert.to-imperial
- - +
+
unit.conversion.target-metric-unit
+ + +
+
+
unit.conversion.target-imperial-unit
+ + +
+
+
unit.conversion.target-hybrid-unit
+ + +
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss index fb2bf1be09..24df54f839 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss @@ -16,13 +16,16 @@ @import '../scss/constants'; .tb-convert-settings-panel { - width: 320px; + width: 360px; display: flex; flex-direction: column; gap: 16px; max-height: calc(100vh - 24px); @media #{$mat-xs} { width: 90vw; + .tb-form-row tb-unit-input { + width: 90px; + } } .tb-convert-settings-title { font-size: 16px; diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts index 7d3b74e27e..92b8162576 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts @@ -15,7 +15,7 @@ /// import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitsType, UnitSystem } from '@shared/models/unit.models'; +import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -48,7 +48,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { unitSettingsApplied = new EventEmitter(); @Input() - tagFilter: UnitsType; + tagFilter: string; @Input() measure: AllMeasures; diff --git a/ui-ngx/src/app/shared/components/unit-input.component.html b/ui-ngx/src/app/shared/components/unit-input.component.html index 33c77130b9..022c4f15e1 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.html +++ b/ui-ngx/src/app/shared/components/unit-input.component.html @@ -40,7 +40,7 @@ mdi:swap-vertical-circle-outline diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index 510f05826d..1f263772b5 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -37,7 +37,6 @@ import { isTbUnitMapping, TbUnit, UnitInfo, - UnitsType, UnitSystem } from '@shared/models/unit.models'; import { map, mergeMap } from 'rxjs/operators'; @@ -73,7 +72,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang required = false; @Input() - tagFilter: UnitsType; + tagFilter: string; @Input() measure: AllMeasures; @@ -264,18 +263,68 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { if (isNotEmptyStr(searchText)) { - const filterValue = searchText.trim().toUpperCase() - return units.reduce((result: Array<[AllMeasures, Array]>, [measure, unitInfos]) => { - const filteredUnits = unitInfos.filter(unit => unit.searchText.toUpperCase().includes(filterValue)); - if (filteredUnits.length > 0) { - result.push([measure, filteredUnits]); - } - return result; - }, []); + const filterValue = searchText.trim().toUpperCase(); + + const scoredGroups = units + .map(([measure, unitInfos]) => { + const scoredUnits = unitInfos + .map(unit => ({ + unit, + score: this.calculateRelevanceScore(unit, filterValue) + })) + .filter(({ score }) => score > 0) + .sort((a, b) => b.score - a.score) + .map(({ unit }) => unit); + + let groupScore = scoredUnits.length > 0 + ? Math.max(...scoredUnits.map(unit => this.calculateRelevanceScore(unit, filterValue))) + : 0; + + if (measure.toUpperCase() === filterValue) { + groupScore += 200; + } + + return { measure, units: scoredUnits, groupScore }; + }) + .filter(group => group.units.length > 0) + .sort((a, b) => { + if (b.groupScore !== a.groupScore) { + return b.groupScore - a.groupScore; + } + return b.units.length - a.units.length; + }); + + return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array]); } return units; } + private calculateRelevanceScore(unit: UnitInfo, filterValue: string): number { + const name = unit.name.toUpperCase(); + const abbr = unit.abbr.toUpperCase(); + const tags = unit.tags.map(tag => tag.toUpperCase()); + + let score = 0; + + if (name === filterValue || abbr === filterValue) { + score += 100; + } else if (tags.includes(filterValue)) { + score += 80; + } else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) { + score += 60; + } else if (tags.some(tag => tag.startsWith(filterValue))) { + score += 50; + } else if (tags.some(tag => tag.includes(filterValue))) { + score += 30; + } + + if (score > 0) { + score += Math.max(0, 10 - (name.length + abbr.length) / 2); + } + + return score; + } + private extractTbUnit(value: TbUnit | UnitInfo | null): TbUnit { if (value === null) { return null; diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index f388b6ab81..94a831afa0 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -204,6 +204,7 @@ export type AllMeasures = | 'angle' | 'angular-acceleration' | 'area' + | 'area-density' | 'capacitance' | 'catalytic-activity' | 'catalytic-concentration' @@ -287,28 +288,51 @@ const allMeasures: Record< AllMeasures, TbMeasure > = Object.freeze({ - 'absorbed-dose-rate': absorbedDoseRate, + temperature, + pressure, + voltage, + 'current-density': currentDensity, + 'electric-current': electricCurrent, + power, + energy, + speed, + length, + mass, + time, + area, + volume, + 'volume-flow': volumeFlow, + density, acceleration, - acidity, 'air-quality-index': airQualityIndex, - 'amount-of-substance': amountOfSubstance, - angle, - 'angular-acceleration': angularAcceleration, - area, - 'area-density': areaDensity, + illuminance, + 'signal-level': signalLevel, + 'fuel-efficiency': fuelEfficiency, + frequency, capacitance, + inductance, + resistance, + torque, + force, + 'magnetic-flux-density': magneticFluxDensity, + 'magnetic-flux': magneticFlux, + radioactivity, + 'radioactive-decay': radioactiveDecay, + 'specific-energy': specificEnergy, + 'specific-heat-capacity': specificHeatCapacity, + 'kinematic-viscosity': kinematicViscosity, + 'dynamic-viscosity': dynamicViscosity, + 'thermal-conductivity': thermalConductivity, + turbidity, + 'earthquake-magnitude': earthquakeMagnitude, + 'data-transfer-rate': dataTransferRate, + 'parts-per-million': partsPerMillion, + 'molar-concentration': molarConcentration, + 'number-concentration': numberConcentration, 'catalytic-activity': catalyticActivity, 'catalytic-concentration': catalyticConcentration, charge, - 'current-density': currentDensity, - 'data-transfer-rate': dataTransferRate, - density, - digital, - 'dimension-ratio': dimensionRatio, - 'dynamic-viscosity': dynamicViscosity, - 'earthquake-magnitude': earthquakeMagnitude, 'electric-charge-density': electricChargeDensity, - 'electric-current': electricCurrent, 'electric-dipole-moment': electricDipoleMoment, 'electric-field-strength': electricFieldStrength, 'electric-flux': electricFlux, @@ -316,70 +340,43 @@ const allMeasures: Record< 'electric-polarizability': electricPolarizability, 'electrical-conductance': electricalConductance, 'electrical-conductivity': electricalConductivity, - energy, - 'energy-density': energyDensity, - force, - frequency, - 'fuel-efficiency': fuelEfficiency, - 'heat-capacity': heatCapacity, - illuminance, - inductance, - 'kinematic-viscosity': kinematicViscosity, - length, - 'light-exposure': lightExposure, - 'linear-charge-density': linerChargeDensity, - 'logarithmic-ratio': logarithmicRatio, - 'luminous-efficacy': luminousEfficacy, - 'luminous-flux': luminousFlux, - 'luminous-intensity': luminousIntensity, - 'number-concentration': numberConcentration, 'magnetic-field-gradient': magneticFieldGradient, - 'magnetic-flux': magneticFlux, - 'magnetic-flux-density': magneticFluxDensity, 'magnetic-moment': magneticMoment, 'magnetic-permeability': magneticPermeability, - mass, - 'mass-fraction': massFraction, - 'molar-concentration': molarConcentration, - 'molar-energy': molarEnergy, - 'molar-heat-capacity': molarHeatCapacity, - 'molar-mass': molarMass, - 'parts-per-million': partsPerMillion, - power, - 'power-density': powerDensity, - pressure, radiance, 'radiant-intensity': radiantIntensity, 'radiation-dose': radiationDose, - 'radioactive-decay': radioactiveDecay, - radioactivity, 'radioactivity-concentration': radioactivityConcentration, 'reciprocal-length': reciprocalLength, - resistance, 'reynolds-number': reynoldsNumber, - 'signal-level': signalLevel, - 'solid-angle': solidAngle, - 'specific-energy': specificEnergy, - 'specific-heat-capacity': specificHeatCapacity, - 'specific-humidity': specificHumidity, - 'specific-volume': specificVolume, - speed, 'surface-charge-density': surfaceChargeDensity, 'surface-tension': surfaceTension, - temperature, - 'thermal-conductivity': thermalConductivity, - time, - torque, - turbidity, - voltage, - volume, - 'volume-flow': volumeFlow, + 'specific-volume': specificVolume, + 'specific-humidity': specificHumidity, + 'angular-acceleration': angularAcceleration, + angle, + 'solid-angle': solidAngle, + 'light-exposure': lightExposure, + 'luminous-intensity': luminousIntensity, + 'luminous-flux': luminousFlux, + 'luminous-efficacy': luminousEfficacy, + 'molar-energy': molarEnergy, + 'molar-heat-capacity': molarHeatCapacity, + 'molar-mass': molarMass, + 'mass-fraction': massFraction, + 'logarithmic-ratio': logarithmicRatio, + 'dimension-ratio': dimensionRatio, + 'absorbed-dose-rate': absorbedDoseRate, + acidity, + 'amount-of-substance': amountOfSubstance, + digital, + 'area-density': areaDensity, + 'energy-density': energyDensity, + 'heat-capacity': heatCapacity, + 'linear-charge-density': linerChargeDensity, + 'power-density': powerDensity, }); -export enum UnitsType { - capacity = 'capacity' -} - export type TbUnitConverter = (value: number) => number; export type UnitInfoGroupByMeasure = Partial>; @@ -389,7 +386,6 @@ export interface UnitInfo { system: UnitSystem; name: string; tags: string[]; - searchText: string; } export enum UnitSystem { @@ -430,7 +426,6 @@ export interface UnitCacheInfo { measure: AllMeasures; unit: Unit; abbr: AllMeasuresUnits; - searchText: string; } export type UnitCache = Map; @@ -591,8 +586,7 @@ export class Converter { measure: unit.measure, system: unit.system, name: unit.unit.name, - tags: unit.unit.tags, - searchText: unit.searchText + tags: unit.unit.tags }; } @@ -638,7 +632,6 @@ function buildUnitCache(measures: Record Date: Tue, 13 May 2025 17:52:22 +0300 Subject: [PATCH 21/27] UI: Fixed typo --- .../json/system/widget_types/signal_strength.json | 13 +++++++++++++ .../convert-unit-settings-panel.component.html | 2 +- .../convert-unit-settings-panel.component.ts | 5 ----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/application/src/main/data/json/system/widget_types/signal_strength.json b/application/src/main/data/json/system/widget_types/signal_strength.json index 516f87fe4c..4784808baa 100644 --- a/application/src/main/data/json/system/widget_types/signal_strength.json +++ b/application/src/main/data/json/system/widget_types/signal_strength.json @@ -32,5 +32,18 @@ "wireless", "link", "quality" + ], + "resources": [ + { + "link": "/api/images/system/signal_strength_system_widget_image.png", + "title": "\"Signal strength\" system widget image", + "type": "IMAGE", + "subType": "IMAGE", + "fileName": "signal_strength_system_widget_image.png", + "publicResourceKey": "oOkg5kXnhiyzZ2pEe66hRVSLV94D8v4i", + "mediaType": "image/png", + "data": "iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAAgVBMVEXg4ODf39/g4OAAAADg4ODf39////9c35Dh4eEhISE9PT3r+/GF56z1/fhw457C89Vm4Zfx8fHHx8fW9+Ou78isrKx0dHSQkJC48c5YWFij7cGCgoIvLy+Z67p65aWenp7M9dyP6bPh+eq6urqZ67lmZmbV1dVLS0tKSkrv7++48c8QruqjAAAABnRSTlPvIL8Ar7DvmsykAAAGE0lEQVR42uzX3WrjMBAF4GRbhjlzNwJJCAl8IWSn7/+CO7F3I+ciLNtQ1w0+UAn9VXxoCMnp7fzrxD897+e30/n0QT8+aox3pRfIh5UVvUROrwLhA7KzHJC95YDsLQdkbzkge8sB2Vueh4yOPhd1vBHEtZS89VmUHkeEPhcPvw0kooSEiWgQ/g/IICP9K06G7SAKUaLcb9O+1Ls1RHXxu76q/ci6d4gLRDeAMC7WjtFRFjZRwdSESEIGmhINYj13CCdgGqkVlExNIuI8lfh6pgGZiHyBRBntaBGDZIH4L4dQwlTpmgAmj4sPABEQfMNAIy51QOoQkeqkqGswe0IJVaUMQ7EtpvAXVGKITcCNGc2Tn0fl6yEagBJ4gUz2Zw0RhIgRiP38Gh1yfcAadSmtBF5qx4YjXb0VkbJhrHG9tCiAN/j4ZZ8gOl+GNL9Rh5ALk2AFiZDgif5C5pecUhL4+bBDtFm19a0h7K4rGcMCudxD5tJy0iFXmWC6h7RgcTdI+xaIR1za+TIpeg/JGO9Ki6ONA8YVZMBgrdINkuG+AaKC7LxgnC+LaC5jBYkINtEhWibnJjANdmyBaCm+ZtEbxEGqF4MwproZhDgAKMOfy0yRZAXRBqQL9FZaVZbdLEgLhHiZukEoFpRkEGrAdhAL90VVJim0ivKD3ayP9qiNA1zfu/2Xxiq+ZmR6KirZRQj1bA/hAJSs9FyiAKlfuCmkh59gPPgnu/o9spMckL3lgOwtB2RvOSC/2a13FYtBIADDnRZzURgLERQNef9nXHa3CAu65maORf4+JF9Gxdl6IbP1QmbrhczWC5mtFzJbL2S2RkAIsg+xJGarf2JGjM7nhdTW3BAwobBuxxjyor6bGLL4YvWeLHqZFUImWn0kWwxMByGP+kxoYCaIbIozlkkg5Ky+Fkf4PERQ3xHmoZDxjC02d0A+ztgo1yATMDbKsxDoMzitq/PmN+9CQda9EB6E9E8qXn2uf9EivqR/KREehAjrZhiEer9BHPbX13gIBd3IhkxqZ7JyZyijIcIthaitSxY2D0Aa40AhdaK86mqBBkMgVYfhQJ0NAleHAkMhYqsMUpcyNYr1AyGuzRhAccMgsckYQyk0BEKpsyevBZVxM42AxMpJ1cXDItkYIyJAJ249ZQAEDu1GWkwobP8+kDB4Oba+4H6I2X29oxySboch0+6h+IuQ/okVWp/iUfdDD/te4+6HfLV3RjuSgkAU7Z7ZxUmKAjbwQEw0anzZ/v8P3J7N7nSPoEiYkcJwX9t0PLEqxb1lov3UhTo5S+mlnwU+lZf8ehB8vgnvPegxMoXotJdk/N4eYe12WQ082RK65WW+dY6YffMsGsUt4zl6IEZMX66D9j0dBWYVSO4SD40IGGPfH4p3t3h40mhUkyyTP2mEPlA4oud3CRG4DDKD3NT6BqSVEzwj66HlKvBQMoHguAIxrk7uSa5Nmw6zgYDwUswaQ0ZdpZVXOkjY93KLbIckb1wpnQXk5sPQEVlQ48pmABkCGGGBB0UfDyI8GOlNxg8Hmbx+MT1zgKNB9LpfTKovnfWJqJQmtXlBmHoaABN7KG0cieOb3T7KCjeyFNt272rNoPec1+RhIK6Pm3dnKX034Lb7NFkmu3y/US7jUgguIfBfWQ6NgP5NgYrfEiJg/ndRvIYxjEL0NSdnwRhEoQwyqIjIgS4IdlGRA1kQEHE5EBAFmWLjFCVJgoBqojVRBPH2h+rn1tw1ci8mpwgS2oJM0pO5ID0QWLayQfca10fRA8F9Y0IK6k+E8eYhg/t2eD3FHhkcoxUeN5IiCDOO0QolrYbkHPlX/8ruXxkRnex3oYb9VobwoTFOFaSCpAgNV41aiRwKAvlYzQnLXJUDYraXhMWA3AJbnFJAFlZLOZOjFJA2tIEuBcTJftlCpYA0Sy1+Lxdk0STlgix+LwbkND1imsCbqaWA4GKOAFuoFBBmTzLZz3PWYsyK/3VV9umXMZB//Yg/6CoJZEMVpIKUogpCTRWEmioINVUQaqog1FRBqOlEIBd2Cr1dXk7w6WnGfv28XC8n+Pj07zvG6/XHW+m6vFxf/wBvcT51JCx2jQAAAABJRU5ErkJggg==", + "public": true + } ] } \ No newline at end of file diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html index 3f14ba290c..7790d185dd 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html +++ b/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.html @@ -61,7 +61,7 @@
unit.conversion.target-hybrid-unit
{ - this.unitFrom.unitInput.nativeElement.focus(); - }); - } } clearUnit() { From c7ed268f8ce9d05891ecfd66d1fe8796fdcb4586 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 13 May 2025 18:22:44 +0300 Subject: [PATCH 22/27] UI: Clear app component --- ui-ngx/src/app/app.component.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 85bddd56cb..dc3d539acb 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -16,7 +16,7 @@ import 'hammerjs'; -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { environment as env } from '@env/environment'; @@ -40,7 +40,7 @@ import { UnitService } from '@core/services/unit.service'; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent implements OnInit { +export class AppComponent { constructor(private store: Store, private storageService: LocalStorageService, @@ -103,7 +103,7 @@ export class AppComponent implements OnInit { userLang = settings?.userLang ?? null; } this.notifyUserLang(userLang); - this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem) + this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem); }), skip(1), ).subscribe((data) => { @@ -112,10 +112,7 @@ export class AppComponent implements OnInit { this.authService.reloadUser(); } - ngOnInit() { - } - - onActivateComponent($event: any) { + onActivateComponent(_$event: any) { const loadingElement = $('div#tb-loading-spinner'); if (loadingElement.length) { loadingElement.remove(); From 19405416bee3babd9fba7410f545c38ddb464684 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 14 May 2025 11:01:03 +0300 Subject: [PATCH 23/27] UI: Fixed license header --- .../models/units/specific-heat-capacity.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts index 595d1ca7ff..0de957396b 100644 --- a/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts +++ b/ui-ngx/src/app/shared/models/units/specific-heat-capacity.ts @@ -1,3 +1,19 @@ +/// +/// Copyright © 2016-2025 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + import { TbMeasure, TbMeasureUnits } from '@shared/models/unit.models'; export type SpecificHeatCapacityUnits = 'J/(kg·K)'; From 59f23ba25d27fd626a14a738c07d9796752b83ce Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 14 May 2025 13:32:51 +0300 Subject: [PATCH 24/27] UI: Add unit conversion in dashboard filter --- .../filter-user-info-dialog.component.html | 44 ++++++++----- .../filter-user-info-dialog.component.ts | 28 +++++---- .../filter/user-filter-dialog.component.html | 6 +- .../filter/user-filter-dialog.component.scss | 30 +++++++++ .../filter/user-filter-dialog.component.ts | 62 ++++++++++--------- .../app/shared/models/query/query.models.ts | 9 ++- .../assets/locale/locale.constant-en_US.json | 8 ++- 7 files changed, 127 insertions(+), 60 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/filter/user-filter-dialog.component.scss diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html index 68e3489866..f3e540f3f6 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-user-info-dialog.component.html @@ -26,24 +26,36 @@
-
- - {{ 'filter.editable' | translate }} - -
- - filter.display-label - +
+
+ +
+ {{ 'filter.editable' | translate }} +
+
+
+
+ +
+ {{ 'filter.custom-label' | translate }} +
+
+ + + +
+
+
filter.order-priority
+ + - - {{ 'filter.autogenerated-label' | translate }} -
- - filter.order-priority - - -
+
+
filter.unit
+ + +
+
@@ -93,7 +93,7 @@ type="button" *ngIf="!disabled" (click)="applyUnitSettings()" - [disabled]="convertUnitForm.invalid || convertUnitForm.pristine"> + [disabled]="unitSettingForm.invalid || unitSettingForm.pristine"> {{ 'action.apply' | translate }}
diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss b/ui-ngx/src/app/shared/components/unit-settings-panel.component.scss similarity index 100% rename from ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.scss rename to ui-ngx/src/app/shared/components/unit-settings-panel.component.scss diff --git a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts b/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts similarity index 56% rename from ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts rename to ui-ngx/src/app/shared/components/unit-settings-panel.component.ts index 4684d2b68d..f378ec59f0 100644 --- a/ui-ngx/src/app/shared/components/convert-unit-settings-panel.component.ts +++ b/ui-ngx/src/app/shared/components/unit-settings-panel.component.ts @@ -14,26 +14,23 @@ /// limitations under the License. /// -import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { AllMeasures, isNotEmptyTbUnits, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { AllMeasures, TbUnit, UnitInfo, UnitSystem } from '@shared/models/unit.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; import { FormBuilder, Validators } from '@angular/forms'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { UnitService } from '@core/services/unit.service'; -import { debounceTime, first } from 'rxjs/operators'; +import { debounceTime } from 'rxjs/operators'; import { isUndefinedOrNull } from '@core/utils'; -import type { UnitInputComponent } from '@shared/components/unit-input.component'; @Component({ selector: 'tb-covert-unit-settings-panel', - templateUrl: './convert-unit-settings-panel.component.html', - styleUrls: ['./convert-unit-settings-panel.component.scss'], + templateUrl: './unit-settings-panel.component.html', + styleUrls: ['./unit-settings-panel.component.scss'], providers: [], encapsulation: ViewEncapsulation.None }) -export class ConvertUnitSettingsPanelComponent implements OnInit { - - @ViewChild('unitFrom', {static: true}) unitFrom: UnitInputComponent; +export class UnitSettingsPanelComponent implements OnInit { @Input() unit: TbUnit; @@ -57,7 +54,7 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { targetMeasure: AllMeasures; - convertUnitForm = this.fb.group({ + unitSettingForm = this.fb.group({ from: [''], convertUnit: [true], METRIC: [''], @@ -66,32 +63,32 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { }) constructor( - private popover: TbPopoverComponent, + private popover: TbPopoverComponent, private fb: FormBuilder, private unitService: UnitService ) { - this.convertUnitForm.get('from').valueChanges.pipe( + this.unitSettingForm.get('from').valueChanges.pipe( debounceTime(200), takeUntilDestroyed() ).subscribe(unit => { const unitDescription = this.unitService.getUnitInfo(unit); const units = unitDescription ? this.unitService.getUnits(unitDescription.measure) : []; if (unitDescription && units.length > 1) { - this.convertUnitForm.get('convertUnit').enable({emitEvent: true}); + this.unitSettingForm.get('convertUnit').enable({emitEvent: true}); this.targetMeasure = unitDescription.measure; if (unitDescription.system === UnitSystem.IMPERIAL) { - this.convertUnitForm.get('METRIC').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.METRIC), {emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').setValue(unit, {emitEvent: false}); - this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('METRIC').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.METRIC), {emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('HYBRID').setValue(unit, {emitEvent: false}); } else { - this.convertUnitForm.get('METRIC').setValue(unit, {emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.IMPERIAL), {emitEvent: false}); - this.convertUnitForm.get('HYBRID').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('METRIC').setValue(unit, {emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').setValue(this.unitService.getDefaultUnit(this.targetMeasure, UnitSystem.IMPERIAL), {emitEvent: false}); + this.unitSettingForm.get('HYBRID').setValue(unit, {emitEvent: false}); } } else { - this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); - this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); - this.convertUnitForm.patchValue({ + this.unitSettingForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.unitSettingForm.get('convertUnit').disable({emitEvent: false}); + this.unitSettingForm.patchValue({ METRIC: '', IMPERIAL: '', HYBRID: '' @@ -99,17 +96,17 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { } }) - this.convertUnitForm.get('convertUnit').valueChanges.pipe( + this.unitSettingForm.get('convertUnit').valueChanges.pipe( takeUntilDestroyed() ).subscribe(value => { if (value) { - this.convertUnitForm.get('METRIC').enable({emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').enable({emitEvent: false}); - this.convertUnitForm.get('HYBRID').enable({emitEvent: false}); + this.unitSettingForm.get('METRIC').enable({emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').enable({emitEvent: false}); + this.unitSettingForm.get('HYBRID').enable({emitEvent: false}); } else { - this.convertUnitForm.get('METRIC').disable({emitEvent: false}); - this.convertUnitForm.get('IMPERIAL').disable({emitEvent: false}); - this.convertUnitForm.get('HYBRID').disable({emitEvent: false}); + this.unitSettingForm.get('METRIC').disable({emitEvent: false}); + this.unitSettingForm.get('IMPERIAL').disable({emitEvent: false}); + this.unitSettingForm.get('HYBRID').disable({emitEvent: false}); } }); } @@ -117,27 +114,27 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { ngOnInit() { let unitDescription: UnitInfo; if (this.required) { - this.convertUnitForm.get('from').setValidators(Validators.required); + this.unitSettingForm.get('from').setValidators(Validators.required); } if (typeof this.unit === 'string') { - this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); - this.convertUnitForm.get('from').setValue(this.unit); + this.unitSettingForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.unitSettingForm.get('from').setValue(this.unit); unitDescription = this.unitService.getUnitInfo(this.unit); } else if (isUndefinedOrNull(this.unit)) { - this.convertUnitForm.get('convertUnit').setValue(false, {onlySelf: true}); - this.convertUnitForm.get('from').setValue(null); + this.unitSettingForm.get('convertUnit').setValue(false, {onlySelf: true}); + this.unitSettingForm.get('from').setValue(null); } else { - this.convertUnitForm.patchValue(this.unit, {emitEvent: false}); + this.unitSettingForm.patchValue(this.unit, {emitEvent: false}); unitDescription = this.unitService.getUnitInfo(this.unit.from); } if (unitDescription?.measure) { this.targetMeasure = unitDescription.measure; } else { - this.convertUnitForm.get('convertUnit').disable({emitEvent: false}); + this.unitSettingForm.get('convertUnit').disable({emitEvent: false}); } if (this.disabled) { - this.convertUnitForm.disable({emitEvent: false}); + this.unitSettingForm.disable({emitEvent: false}); } } @@ -150,12 +147,12 @@ export class ConvertUnitSettingsPanelComponent implements OnInit { } applyUnitSettings() { - if (this.convertUnitForm.value.convertUnit) { - const formValue = this.convertUnitForm.value; + if (this.unitSettingForm.value.convertUnit) { + const formValue = this.unitSettingForm.value; delete formValue.convertUnit; this.unitSettingsApplied.emit(formValue as TbUnit); } else { - this.unitSettingsApplied.emit(this.convertUnitForm.value.from); + this.unitSettingsApplied.emit(this.unitSettingForm.value.from); } } } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 1f8ac63926..47a65631db 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -185,7 +185,7 @@ import { ToggleHeaderComponent, ToggleOption } from '@shared/components/toggle-h import { RuleChainSelectComponent } from '@shared/components/rule-chain/rule-chain-select.component'; import { ToggleSelectComponent } from '@shared/components/toggle-select.component'; import { UnitInputComponent } from '@shared/components/unit-input.component'; -import { ConvertUnitSettingsPanelComponent } from '@shared/components/convert-unit-settings-panel.component'; +import { UnitSettingsPanelComponent } from '@shared/components/unit-settings-panel.component'; import { MaterialIconsComponent } from '@shared/components/material-icons.component'; import { ColorPickerPanelComponent } from '@shared/components/color-picker/color-picker-panel.component'; import { TbIconComponent } from '@shared/components/icon.component'; @@ -414,7 +414,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) ToggleOption, ToggleSelectComponent, UnitInputComponent, - ConvertUnitSettingsPanelComponent, + UnitSettingsPanelComponent, StringAutocompleteComponent, MaterialIconsComponent, RuleChainSelectComponent, diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index 2fd045dad0..61de661ac9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -3105,7 +3105,6 @@ "filter-user-params": "معلمات مستخدم الفلتر الفعلي", "user-parameters": "معلمات المستخدم", "display-label": "تسمية للعرض", - "autogenerated-label": "إنشاء تسمية تلقائية", "order-priority": "أولوية ترتيب الحقل", "key-filter": "فلتر المفتاح", "key-filters": "فلاتر المفاتيح", diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index d986a24970..8633400394 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -2637,7 +2637,6 @@ "filter-user-params": "Filtre de paràmetres d'usuari (predicado)", "user-parameters": "Paràmetres d'usuari", "display-label": "Etiqueta a mostrar", - "autogenerated-label": "Auto generar etiqueta", "order-priority": "Prioritat orden de campos", "key-filter": "Filtres (clau)", "key-filters": "Filtres (claus)", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index ab3f6cba56..041e3efe56 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -1972,7 +1972,6 @@ "filter-user-params": "Filtr predikátu parametrů uživatele", "user-parameters": "Parametry uživatele", "display-label": "Zobrazované označení", - "autogenerated-label": "Automaticky vygenerovat označení", "order-priority": "Priority pořadí polí", "key-filter": "Klíčový filtr", "key-filters": "Klíčové filtry", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 4759b873b4..e96ec45e3e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -1912,7 +1912,6 @@ "filter-user-params": "Filtrerprædikat for brugerparametre", "user-parameters": "Brugerparametre", "display-label": "Etiket, der skal vises", - "autogenerated-label": "Generer automatisk etiket", "order-priority": "Prioritet af feltrækkefølge", "key-filter": "Nøglefilter", "key-filters": "Nøglefiltre", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b2a772eb93..7163e81240 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2991,7 +2991,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "User parameters", "display-label": "Label to display", - "autogenerated-label": "Auto generate label", "custom-label": "Custom label", "custom-label-hint": "Enable to set your own label for the filter. When disabled, a label will be generated automatically.", "order-priority": "Display order", @@ -5856,8 +5855,8 @@ "background-blur": "Background blur" }, "unit": { - "conversion": { - "set-unit-conversion": "Set unit conversion", + "set-unit-conversion": "Set unit conversion", + "unit-settings": { "unit-settings": "Unit settings", "source-unit": "Source unit", "source-unit-hint": "This is the unit of the stored value. The unit you’re converting from. Enter the symbol your source data uses (e.g. m, km, ft, in).", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 8b7f07d789..e1329e657d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -2573,7 +2573,6 @@ "filter-user-params": "Filtro de parámetros de usuario (predicado)", "user-parameters": "Parámetros de usuario", "display-label": "Etiqueta a mostrar", - "autogenerated-label": "Auto generar etiqueta", "order-priority": "Prioridad orden de campos", "key-filter": "Filtros (clave)", "key-filters": "Filtros (claves)", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index ef1a09c00f..a122a04ee7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -1535,7 +1535,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "User parameters", "display-label": "Label to display", - "autogenerated-label": "Auto generate label", "order-priority": "Field order priority", "key-filter": "Key filter", "key-filters": "Key filters", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index 73ed884662..d3cc6f6c47 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -3073,7 +3073,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "User parameters", "display-label": "Label to display", - "autogenerated-label": "Auto generate label", "order-priority": "Field order priority", "key-filter": "Key filter", "key-filters": "Key filters", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index e64970bca3..f4951582b5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -2992,7 +2992,6 @@ "filter-user-params": "Gebruikersparameters voor predicaten filteren", "user-parameters": "Parameters van de gebruiker", "display-label": "Label om weer te geven", - "autogenerated-label": "Automatisch label genereren", "order-priority": "Prioriteit voor veldorders", "key-filter": "Toets filter", "key-filters": "Belangrijkste filters", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 26e7e23d63..80b65b83f9 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -3082,7 +3082,6 @@ "filter-user-params": "Filtruj parametry użytkownika predykatu", "user-parameters": "Parametry użytkownika", "display-label": "Etykieta do wyświetlenia", - "autogenerated-label": "Automatyczne generowanie etykiety", "order-priority": "Priorytet zamówienia pola", "key-filter": "Filtr kluczowy", "key-filters": "Kluczowe filtry", diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index eb20180447..a528be78eb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1212,7 +1212,6 @@ "edit-filter-user-params": "Editar parâmetros de usuário de predicado de filtro", "user-parameters": "Parâmetros de usuário", "display-label": "Etiqueta para exibição", - "autogenerated-label": "Gerar etiqueta automaticamente", "order-priority": "Prioridade de ordem de campo", "key-filter": "Filtro chave", "key-filters": "Filtros chave", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index b667fd0b46..41dea2e169 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1535,7 +1535,6 @@ "filter-user-params": "Filter predicate user parameters", "user-parameters": "Uporabniški parametri", "display-label": "Oznaka za prikaz", - "autogenerated-label": "Samodejno ustvari oznako", "order-priority": "Prednostni vrstni red", "key-filter": "Ključni filter", "key-filters": "Ključni filtri", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index 98998ab312..1af97591a4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -1987,7 +1987,6 @@ "filter-user-params": "Filtre belirteci kullanıcı parametreleri", "user-parameters": "Kullanıcı parametreleri", "display-label": "Görüntülenecek etiket", - "autogenerated-label": "Otomatik etiket oluştur", "order-priority": "Alan sırası önceliği", "key-filter": "Anahtar filtresi", "key-filters": "Anahtar filtreleri", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index aaf69bb984..770f5df355 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -2878,7 +2878,6 @@ "filter-user-params": "过滤谓词用户参数", "user-parameters": "用户参数", "display-label": "要显示的标签", - "autogenerated-label": "自动生成标签", "order-priority": "字段顺序优先级", "key-filter": "键名筛选器", "key-filters": "键名筛选器", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index 599da1b41e..9e2246b83d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2198,7 +2198,6 @@ "filter-user-params": "過濾謂詞用戶參數", "user-parameters": "用戶參數", "display-label": "要顯示的標籤", - "autogenerated-label": "自動生成標籤", "order-priority": "字段順序優先級", "key-filter": "關鍵過濾器", "key-filters": "關鍵過濾器", From 8b14e72f8c15c8a3063af11c5c131b53585f6bbd Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 16 May 2025 14:01:44 +0300 Subject: [PATCH 26/27] UI: Refactoring after review --- ui-ngx/src/app/app.component.ts | 8 +- ui-ngx/src/app/core/api/widget-api.models.ts | 5 +- .../src/app/core/api/widget-subscription.ts | 2 +- ui-ngx/src/app/core/services/unit.service.ts | 17 ++- .../aggregated-data-key-row.component.html | 2 +- .../aggregated-data-key-row.component.ts | 6 +- ...rt-with-labels-basic-config.component.html | 2 +- .../range-chart-basic-config.component.html | 2 +- .../components/widget/widget.component.ts | 4 +- .../shared/components/unit-input.component.ts | 103 ++---------------- ui-ngx/src/app/shared/models/unit.models.ts | 95 +++++++++++++++- .../shared/models/widget-settings.models.ts | 22 ++-- 12 files changed, 137 insertions(+), 131 deletions(-) diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index dc3d539acb..17a473c8b8 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -33,7 +33,6 @@ import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; -import { UnitService } from '@core/services/unit.service'; @Component({ selector: 'tb-root', @@ -47,8 +46,7 @@ export class AppComponent { private translate: TranslateService, private matIconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer, - private authService: AuthService, - private unitService: UnitService) { + private authService: AuthService) { console.log(`ThingsBoard Version: ${env.tbVersion}`); @@ -96,14 +94,12 @@ export class AppComponent { this.store.select(selectUserReady).pipe( filter((data) => data.isUserLoaded), tap((data) => { - const userDetails = getCurrentAuthState(this.store).userDetails; - let userLang = userDetails?.additionalInfo?.lang ?? null; + let userLang = getCurrentAuthState(this.store).userDetails?.additionalInfo?.lang ?? null; if (!userLang && !data.isAuthenticated) { const settings = this.storageService.getItem(SETTINGS_KEY); userLang = settings?.userLang ?? null; } this.notifyUserLang(userLang); - this.unitService.setUnitSystem(userDetails?.additionalInfo?.unitSystem); }), skip(1), ).subscribe((data) => { diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index 3338cffa25..8c4120739e 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -62,10 +62,11 @@ import { AlarmDataService } from '@core/api/alarm-data.service'; import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models'; import { PopoverPlacement } from '@shared/components/popover.models'; import { PersistentRpc } from '@shared/models/rpc.models'; -import { EventEmitter, Injector } from '@angular/core'; +import { EventEmitter } from '@angular/core'; import { DashboardUtilsService } from '@core/services/dashboard-utils.service'; import { MatDialogRef } from '@angular/material/dialog'; import { TbUnit } from '@shared/models/unit.models'; +import { UnitService } from '@core/services/unit.service'; export interface TimewindowFunctions { onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void; @@ -234,8 +235,8 @@ export class WidgetSubscriptionContext { utils: UtilsService; dashboardUtils: DashboardUtilsService; raf: RafService; + unitService: UnitService; widgetUtils: IWidgetUtils; - $injector: Injector; getServerTimeDiff: () => Observable; } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index d3a851016d..e86685bfe1 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -1419,7 +1419,7 @@ export class WidgetSubscription implements IWidgetSubscription { if (this.displayLegend) { const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : this.decimals; const units = isNotEmptyTbUnits(dataKey.units) ? dataKey.units : this.units; - const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.$injector, {decimals, units}) + const valueFormat = ValueFormatProcessor.fromSettings(this.ctx.unitService, {decimals, units}) const legendKey: LegendKey = { dataKey, dataIndex: dataKeyIndex, diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index 5d60e80390..c68115f1a3 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -32,6 +32,10 @@ import { import { isNotEmptyStr, isObject } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { selectAuth, selectIsAuthenticated } from '@core/auth/auth.selectors'; +import { filter, switchMap, take } from 'rxjs/operators'; @Injectable({ providedIn: 'root' @@ -41,12 +45,19 @@ export class UnitService { private currentUnitSystem: UnitSystem = UnitSystem.METRIC; private converter: Converter; - constructor(private translate: TranslateService) { + constructor(private translate: TranslateService, + private store: Store) { this.translate.onLangChange.pipe( takeUntilDestroyed() ).subscribe(() => { this.converter = getUnitConverter(this.translate); }); + this.store.select(selectIsAuthenticated).pipe( + filter((data) => data), + switchMap(() => this.store.select(selectAuth).pipe(take(1))) + ).subscribe((data) => { + this.setUnitSystem(data.userDetails?.additionalInfo?.unitSystem) + }) } getUnitSystem(): UnitSystem { @@ -65,8 +76,8 @@ export class UnitService { return this.converter?.listUnits(measure, unitSystem); } - getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { - return this.converter?.unitsGroupByMeasure(measure, unitSystem); + getUnitsGroupedByMeasure(measure?: AllMeasures, unitSystem?: UnitSystem, tagFilter?: string): UnitInfoGroupByMeasure { + return this.converter?.unitsGroupByMeasure(measure, unitSystem, tagFilter); } getUnitInfo(symbol: AllMeasuresUnits | string): UnitInfo { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html index f1c8787fff..9c32e5f74e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.html @@ -48,7 +48,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts index 87b2fbbc48..2fee7a3a27 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/cards/aggregated-data-key-row.component.ts @@ -118,10 +118,6 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective; } - get supportsUnitConversion(): boolean { - return this.widgetConfigComponent.modelValue?.typeParameters?.supportsUnitConversion ?? false; - } - private propagateChange = (_val: any) => {}; constructor(private fb: UntypedFormBuilder, @@ -220,7 +216,7 @@ export class AggregatedDataKeyRowComponent implements ControlValueAccessor, OnIn hideDataKeyName: true, hideDataKeyLabel: true, hideDataKeyColor: true, - supportsUnitConversion: this.supportsUnitConversion + supportsUnitConversion: true } }).afterClosed().subscribe((updatedDataKey) => { if (updatedDataKey) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html index 4b4dcd148d..307645822d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.html @@ -100,7 +100,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html index f0d36264e9..608284996e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.html @@ -84,7 +84,7 @@
widget-config.units-short
diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index d5a0b78ce5..fed2615bbd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -105,6 +105,7 @@ import { ExceptionData } from '@shared/models/error.models'; import { WidgetComponentService } from './widget-component.service'; import { Timewindow } from '@shared/models/time/time.models'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; +import { UnitService } from '@core/services/unit.service'; import { DashboardService } from '@core/http/dashboard.service'; import { WidgetSubscription } from '@core/api/widget-subscription'; import { EntityService } from '@core/http/entity.service'; @@ -216,6 +217,7 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, private dashboardUtils: DashboardUtilsService, private mobileService: MobileService, private raf: RafService, + private unitService: UnitService, private ngZone: NgZone, private cd: ChangeDetectorRef, private http: HttpClient) { @@ -341,8 +343,8 @@ export class WidgetComponent extends PageComponent implements OnInit, OnChanges, this.subscriptionContext.utils = this.utils; this.subscriptionContext.dashboardUtils = this.dashboardUtils; this.subscriptionContext.raf = this.raf; + this.subscriptionContext.unitService = this.unitService; this.subscriptionContext.widgetUtils = this.widgetContext.utils; - this.subscriptionContext.$injector = this.injector; this.subscriptionContext.getServerTimeDiff = this.dashboardService.getServerTimeDiff.bind(this.dashboardService); this.widgetComponentService.getWidgetInfo(this.widget.typeFullFqn).subscribe({ diff --git a/ui-ngx/src/app/shared/components/unit-input.component.ts b/ui-ngx/src/app/shared/components/unit-input.component.ts index ff807e0e6f..ad63bee626 100644 --- a/ui-ngx/src/app/shared/components/unit-input.component.ts +++ b/ui-ngx/src/app/shared/components/unit-input.component.ts @@ -34,7 +34,9 @@ import { Observable, of, shareReplay } from 'rxjs'; import { AllMeasures, getSourceTbUnitSymbol, + getTbUnitFromSearch, isTbUnitMapping, + searchUnit, TbUnit, UnitInfo, UnitSystem @@ -43,7 +45,7 @@ import { map, mergeMap } from 'rxjs/operators'; import { UnitService } from '@core/services/unit.service'; import { TbPopoverService } from '@shared/components/popover.service'; import { UnitSettingsPanelComponent } from '@shared/components/unit-settings-panel.component'; -import { isDefinedAndNotNull, isEqual, isNotEmptyStr } from '@core/utils'; +import { isDefinedAndNotNull, isEqual } from '@core/utils'; @Component({ selector: 'tb-unit-input', @@ -200,7 +202,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang hostView: this.viewContainerRef, preferredPlacement: ['left', 'bottom', 'top'], context: { - unit: this.extractTbUnit(this.unitsFormControl.value), + unit: getTbUnitFromSearch(this.unitsFormControl.value), required: this.required, disabled: this.disabled, tagFilter: this.tagFilter, @@ -217,7 +219,7 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang } private updateModel(value: UnitInfo | TbUnit ) { - let res = this.extractTbUnit(value); + let res = getTbUnitFromSearch(value); if (this.onlySystemUnits && !isTbUnitMapping(res)) { const unitInfo = this.unitService.getUnitInfo(res as string); if (unitInfo) { @@ -238,106 +240,17 @@ export class UnitInputComponent implements ControlValueAccessor, OnInit, OnChang private fetchUnits(searchText?: string): Observable]>> { this.searchText = searchText; return this.getGroupedUnits().pipe( - map(unit => this.searchUnit(unit, searchText)) + map(unit => searchUnit(unit, searchText)) ); } private getGroupedUnits(): Observable]>> { if (this.fetchUnits$ === null) { - this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem)).pipe( - map(data => { - let objectData = Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>; - - if (this.tagFilter) { - objectData = objectData - .map((measure) => [measure[0], measure[1].filter(u => u.tags.includes(this.tagFilter))] as [AllMeasures, UnitInfo[]]) - .filter((measure) => measure[1].length > 0); - } - return objectData; - }), + this.fetchUnits$ = of(this.unitService.getUnitsGroupedByMeasure(this.measure, this.unitSystem, this.tagFilter)).pipe( + map(data => Object.entries(data) as Array<[AllMeasures, UnitInfo[]]>), shareReplay(1) ); } return this.fetchUnits$; } - - private searchUnit(units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> { - if (isNotEmptyStr(searchText)) { - const filterValue = searchText.trim().toUpperCase(); - - const scoredGroups = units - .map(([measure, unitInfos]) => { - const scoredUnits = unitInfos - .map(unit => ({ - unit, - score: this.calculateRelevanceScore(unit, filterValue) - })) - .filter(({ score }) => score > 0) - .sort((a, b) => b.score - a.score) - .map(({ unit }) => unit); - - let groupScore = scoredUnits.length > 0 - ? Math.max(...scoredUnits.map(unit => this.calculateRelevanceScore(unit, filterValue))) - : 0; - - if (measure.toUpperCase() === filterValue) { - groupScore += 200; - } - - return { measure, units: scoredUnits, groupScore }; - }) - .filter(group => group.units.length > 0) - .sort((a, b) => { - if (b.groupScore !== a.groupScore) { - return b.groupScore - a.groupScore; - } - return b.units.length - a.units.length; - }); - - return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array]); - } - return units; - } - - private calculateRelevanceScore(unit: UnitInfo, filterValue: string): number { - const name = unit.name.toUpperCase(); - const abbr = unit.abbr.toUpperCase(); - const tags = unit.tags.map(tag => tag.toUpperCase()); - - let score = 0; - - if (name === filterValue || abbr === filterValue) { - score += 100; - } else if (tags.includes(filterValue)) { - score += 80; - } else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) { - score += 60; - } else if (tags.some(tag => tag.startsWith(filterValue))) { - score += 50; - } else if (tags.some(tag => tag.includes(filterValue))) { - score += 30; - } - - if (score > 0) { - score += Math.max(0, 10 - (name.length + abbr.length) / 2); - } - - return score; - } - - private extractTbUnit(value: TbUnit | UnitInfo | null): TbUnit { - if (value === null) { - return null; - } - if (value === undefined) { - return undefined; - } - if (typeof value === 'string') { - return value; - } - if ('abbr' in value) { - return value.abbr; - } - return value; - } } diff --git a/ui-ngx/src/app/shared/models/unit.models.ts b/ui-ngx/src/app/shared/models/unit.models.ts index 94a831afa0..7d91e59441 100644 --- a/ui-ngx/src/app/shared/models/unit.models.ts +++ b/ui-ngx/src/app/shared/models/unit.models.ts @@ -104,7 +104,7 @@ import voltage, { VoltageUnits } from '@shared/models/units/voltage'; import volume, { VolumeUnits } from '@shared/models/units/volume'; import volumeFlow, { VolumeFlowUnits } from '@shared/models/units/volume-flow'; import { TranslateService } from '@ngx-translate/core'; -import { isNotEmptyStr } from '@core/utils'; +import { deepClone, isNotEmptyStr } from '@core/utils'; export type AllMeasuresUnits = | AbsorbedDoseRateUnits @@ -548,7 +548,7 @@ export class Converter { return results; } - unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem): UnitInfoGroupByMeasure { + unitsGroupByMeasure(measureName?: AllMeasures, unitSystem?: UnitSystem, tagFilter?: string): UnitInfoGroupByMeasure { const results: UnitInfoGroupByMeasure = {}; const measures = measureName @@ -573,9 +573,15 @@ export class Converter { } for (const abbr of Object.keys(units) as AllMeasuresUnits[]) { - results[name].push(this.describe(abbr)); + const unitInfo = this.describe(abbr); + if (!tagFilter || unitInfo.tags.includes(tagFilter)) { + results[name].push(unitInfo); + } } } + if (!results[name].length) { + delete results[name]; + } } return results; } @@ -641,7 +647,7 @@ function buildUnitCache(measures: Record { if (typeof unit !== 'object' || unit === null) return false; return isNotEmptyStr(unit.from); }; + + +export const searchUnit = + (units: Array<[AllMeasures, Array]>, searchText?: string): Array<[AllMeasures, Array]> => { + if (isNotEmptyStr(searchText)) { + const filterValue = searchText.trim().toUpperCase(); + + const scoredGroups = units + .map(([measure, unitInfos]) => { + const scoredUnits = unitInfos + .map(unit => ({ + unit, + score: calculateRelevanceScore(unit, filterValue) + })) + .filter(({score}) => score > 0) + .sort((a, b) => b.score - a.score) + .map(({unit}) => unit); + + let groupScore = scoredUnits.length > 0 + ? Math.max(...scoredUnits.map(unit => calculateRelevanceScore(unit, filterValue))) + : 0; + + if (measure.toUpperCase() === filterValue) { + groupScore += 200; + } + + return {measure, units: scoredUnits, groupScore}; + }) + .filter(group => group.units.length > 0) + .sort((a, b) => { + if (b.groupScore !== a.groupScore) { + return b.groupScore - a.groupScore; + } + return b.units.length - a.units.length; + }); + + return scoredGroups.map(group => [group.measure, group.units] as [AllMeasures, Array]); + } + return units; + } + +function calculateRelevanceScore (unit: UnitInfo, filterValue: string): number{ + const name = unit.name.toUpperCase(); + const abbr = unit.abbr.toUpperCase(); + const tags = unit.tags.map(tag => tag.toUpperCase()); + let score = 0; + + if (name === filterValue || abbr === filterValue) { + score += 100; + } else if (tags.includes(filterValue)) { + score += 80; + } else if (name.startsWith(filterValue) || abbr.startsWith(filterValue)) { + score += 60; + } else if (tags.some(tag => tag.startsWith(filterValue))) { + score += 50; + } else if (tags.some(tag => tag.includes(filterValue))) { + score += 30; + } + + if (score > 0) { + score += Math.max(0, 10 - (name.length + abbr.length) / 2); + } + + return score; +} + +export const getTbUnitFromSearch = (value: TbUnit | UnitInfo | null): TbUnit => { + if (value === null) { + return null; + } + if (value === undefined) { + return undefined; + } + if (typeof value === 'string') { + return value; + } + if ('abbr' in value) { + return value.abbr; + } + return value; +} diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 971c888991..193a18cf89 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -875,15 +875,16 @@ export abstract class ValueFormatProcessor { protected hideZeroDecimals: boolean; protected unitSymbol: string; - static fromSettings($injector: Injector, settings: ValueFormatSettings): ValueFormatProcessor { + static fromSettings($injector: Injector, settings: ValueFormatSettings): ValueFormatProcessor; + static fromSettings(unitService: UnitService, settings: ValueFormatSettings): ValueFormatProcessor; + static fromSettings(unitServiceOrInjector: Injector | UnitService, settings: ValueFormatSettings): ValueFormatProcessor { if (settings.units !== null && typeof settings.units === 'object') { - return new UnitConverterValueFormatProcessor($injector, settings) + return new UnitConverterValueFormatProcessor(unitServiceOrInjector, settings) } - return new SimpleValueFormatProcessor($injector, settings); + return new SimpleValueFormatProcessor(settings); } - protected constructor(protected $injector: Injector, - protected settings: ValueFormatSettings) { + protected constructor(protected settings: ValueFormatSettings) { } abstract format(value: any): string; @@ -908,9 +909,8 @@ export class SimpleValueFormatProcessor extends ValueFormatProcessor { private readonly isDefinedUnit: boolean; - constructor(protected $injector: Injector, - protected settings: ValueFormatSettings) { - super($injector, settings); + constructor(protected settings: ValueFormatSettings) { + super(settings); this.unitSymbol = !settings.ignoreUnitSymbol && isNotEmptyStr(settings.units) ? (settings.units as string) : null; this.isDefinedDecimals = isDefinedAndNotNull(settings.decimals); this.hideZeroDecimals = !settings.showZeroDecimals; @@ -928,10 +928,10 @@ export class UnitConverterValueFormatProcessor extends ValueFormatProcessor { private readonly unitConverter: TbUnitConverter; - constructor(protected $injector: Injector, + constructor(protected unitServiceOrInjector: Injector | UnitService, protected settings: ValueFormatSettings) { - super($injector, settings); - const unitService = this.$injector.get(UnitService); + super(settings); + const unitService = this.unitServiceOrInjector instanceof UnitService ? this.unitServiceOrInjector : this.unitServiceOrInjector.get(UnitService); const unit = settings.units; this.unitSymbol = settings.ignoreUnitSymbol ? null : unitService.getTargetUnitSymbol(unit); this.unitConverter = unitService.geUnitConverter(unit); From 9922fba7a171bd8245073fda951ab7d92dc158e3 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 16 May 2025 16:41:52 +0300 Subject: [PATCH 27/27] UI: Refactoring after review --- ui-ngx/src/app/core/services/unit.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/core/services/unit.service.ts b/ui-ngx/src/app/core/services/unit.service.ts index c68115f1a3..1cba587a0c 100644 --- a/ui-ngx/src/app/core/services/unit.service.ts +++ b/ui-ngx/src/app/core/services/unit.service.ts @@ -47,6 +47,7 @@ export class UnitService { constructor(private translate: TranslateService, private store: Store) { + this.converter = getUnitConverter(this.translate); this.translate.onLangChange.pipe( takeUntilDestroyed() ).subscribe(() => {