2025-04-28 18:27:35 +03:00

178 lines
5.5 KiB
TypeScript

///
/// 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 { 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'
}
export type TbUnitConvertor = (value: number) => number;
export type UnitDescriptionGroupByMeasure<TMeasure extends string> = Partial<Record<TMeasure, UnitDescription[]>>;
export interface UnitDescription {
abbr: string;
measure: AllMeasures;
system: UnitSystem;
name: string;
tags: string[];
}
export enum UnitSystem {
METRIC = 'METRIC',
IMPERIAL = 'IMPERIAL',
HYBRID = 'HYBRID'
}
export const UnitSystems = Object.values(UnitSystem);
export interface Unit {
name: string;
tags: string[];
to_anchor: number;
anchor_shift?: number;
}
export type TbUnit = string | TbUnitMapping;
export interface TbUnitMapping {
from: string;
METRIC: string;
IMPERIAL: string;
HYBRID: string;
}
export type TbMeasure<TUnits extends string> = Partial<Record<UnitSystem, TbMeasureUnits<TUnits>>>;
export interface TbMeasureUnits<TUnits extends string> {
ratio?: number;
transform?: (value: number) => number;
units?: Partial<Record<TUnits, Unit>>;
}
const searchUnitTags = (unit: UnitDescription, searchText: string): boolean =>
!!unit.tags.find(t => t.toUpperCase().includes(searchText));
export const searchUnits = (_units: Array<UnitDescription>, searchText: string): Array<UnitDescription> => _units.filter(
u => u.abbr.toUpperCase().includes(searchText) ||
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 ?? '';
}
}