From 70de7f441226f7fc669e8ff0878283fe2d132d6f Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 11 Sep 2024 17:30:13 +0300 Subject: [PATCH] UI: Introduce new SCADA symbol property type 'icon'. --- .../left-analog-water-level-meter.svg | 58 +++++++++++++-- .../right-analog-water-level-meter.svg | 60 ++++++++++++++-- .../scada/scada-symbol-widget.component.ts | 5 +- .../widget/lib/scada/scada-symbol.models.ts | 72 +++++++++++++++++-- ...cada-symbol-object-settings.component.html | 4 ++ ...scada-symbol-property-panel.component.html | 4 ++ .../scada-symbol-property-row.component.ts | 2 + .../scada-symbol-editor.models.ts | 48 ++++++++++--- .../assets/locale/locale.constant-en_US.json | 1 + 9 files changed, 229 insertions(+), 25 deletions(-) diff --git a/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg b/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg index e242edfc11..c2b2a32a2d 100644 --- a/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg @@ -37,12 +37,12 @@ }, { "tag": "icon", - "stateRenderFunction": "var icon = ctx.properties.icon;\nif (icon) {\n element.show();\n} else {\n element.hide()\n}", + "stateRenderFunction": "var showIcon = ctx.properties.showIcon;\nif (showIcon) {\n element.show();\n var icon = ctx.properties.icon;\n var iconSize = ctx.properties.iconSize;\n var iconColor = ctx.properties.iconColor;\n ctx.api.icon(element, icon, iconSize, iconColor, true);\n} else {\n element.hide()\n}", "actions": null }, { "tag": "label", - "stateRenderFunction": "var label = ctx.properties.label;\nif (label) {\n var icon = ctx.properties.icon;\n var labelTextFont = ctx.properties.labelTextFont;\n var labelTextColor = ctx.properties.labelTextColor;\n ctx.api.font(element, labelTextFont, labelTextColor);\n ctx.api.text(element, ctx.properties.labelText);\n if (!icon) {\n element.transform({translateX: 10});\n }\n element.show();\n} else {\n element.hide();\n}", + "stateRenderFunction": "var label = ctx.properties.label;\nif (label) {\n var showIcon = ctx.properties.showIcon;\n var labelTextFont = ctx.properties.labelTextFont;\n var labelTextColor = ctx.properties.labelTextColor;\n ctx.api.font(element, labelTextFont, labelTextColor);\n ctx.api.text(element, ctx.properties.labelText);\n if (!showIcon) {\n element.transform({translateX: 10});\n }\n element.show();\n} else {\n element.hide();\n}", "actions": null }, { @@ -357,7 +357,7 @@ "step": null }, { - "id": "icon", + "id": "showIcon", "name": "{i18n:scada.symbol.icon}", "type": "switch", "default": true, @@ -372,6 +372,54 @@ "max": null, "step": null }, + { + "id": "iconSize", + "name": "{i18n:scada.symbol.icon}", + "type": "number", + "default": 14, + "required": null, + "subLabel": null, + "divider": null, + "fieldSuffix": "px", + "disableOnProperty": "showIcon", + "rowClass": "", + "fieldClass": "", + "min": 0, + "max": null, + "step": null + }, + { + "id": "icon", + "name": "{i18n:scada.symbol.icon}", + "type": "icon", + "default": "water", + "required": null, + "subLabel": null, + "divider": null, + "fieldSuffix": null, + "disableOnProperty": "showIcon", + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "iconColor", + "name": "{i18n:scada.symbol.icon}", + "type": "color", + "default": "#0000008A", + "required": null, + "subLabel": null, + "divider": null, + "fieldSuffix": null, + "disableOnProperty": "showIcon", + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, { "id": "defaultBorderColor", "name": "{i18n:scada.symbol.default-border-color}", @@ -616,7 +664,9 @@ }]]> - Water + Water + + diff --git a/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg b/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg index e75949e374..739505d421 100644 --- a/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg @@ -1,5 +1,5 @@ { - "title": "Rigth analog water level meter", + "title": "Right analog water level meter", "description": "Right analog water level meter", "searchTags": [ "water meter" @@ -37,12 +37,12 @@ }, { "tag": "icon", - "stateRenderFunction": "var icon = ctx.properties.icon;\nif (icon) {\n element.show();\n} else {\n element.hide()\n}", + "stateRenderFunction": "var showIcon = ctx.properties.showIcon;\nif (showIcon) {\n element.show();\n var icon = ctx.properties.icon;\n var iconSize = ctx.properties.iconSize;\n var iconColor = ctx.properties.iconColor;\n ctx.api.icon(element, icon, iconSize, iconColor, true);\n} else {\n element.hide()\n}", "actions": null }, { "tag": "label", - "stateRenderFunction": "var label = ctx.properties.label;\nif (label) {\n var icon = ctx.properties.icon;\n var labelTextFont = ctx.properties.labelTextFont;\n var labelTextColor = ctx.properties.labelTextColor;\n ctx.api.font(element, labelTextFont, labelTextColor);\n ctx.api.text(element, ctx.properties.labelText);\n if (!icon) {\n element.transform({translateX: 10});\n }\n element.show();\n} else {\n element.hide();\n}", + "stateRenderFunction": "var label = ctx.properties.label;\nif (label) {\n var showIcon = ctx.properties.showIcon;\n var labelTextFont = ctx.properties.labelTextFont;\n var labelTextColor = ctx.properties.labelTextColor;\n ctx.api.font(element, labelTextFont, labelTextColor);\n ctx.api.text(element, ctx.properties.labelText);\n if (!showIcon) {\n element.transform({translateX: 10});\n }\n element.show();\n} else {\n element.hide();\n}", "actions": null }, { @@ -357,7 +357,7 @@ "step": null }, { - "id": "icon", + "id": "showIcon", "name": "{i18n:scada.symbol.icon}", "type": "switch", "default": true, @@ -372,6 +372,54 @@ "max": null, "step": null }, + { + "id": "iconSize", + "name": "{i18n:scada.symbol.icon}", + "type": "number", + "default": 14, + "required": null, + "subLabel": null, + "divider": null, + "fieldSuffix": "px", + "disableOnProperty": "showIcon", + "rowClass": "", + "fieldClass": "", + "min": 0, + "max": null, + "step": null + }, + { + "id": "icon", + "name": "{i18n:scada.symbol.icon}", + "type": "icon", + "default": "water", + "required": null, + "subLabel": null, + "divider": null, + "fieldSuffix": null, + "disableOnProperty": "showIcon", + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, + { + "id": "iconColor", + "name": "{i18n:scada.symbol.icon}", + "type": "color", + "default": "#0000008A", + "required": null, + "subLabel": null, + "divider": null, + "fieldSuffix": null, + "disableOnProperty": "showIcon", + "rowClass": "", + "fieldClass": "", + "min": null, + "max": null, + "step": null + }, { "id": "defaultBorderColor", "name": "{i18n:scada.symbol.default-border-color}", @@ -616,7 +664,9 @@ } - Water + Water + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts index 6db0d202c7..010cb9d6bc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts @@ -40,6 +40,7 @@ import { WidgetComponent } from '@home/components/widget/widget.component'; import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; import { WidgetContext } from '@home/models/widget-component.models'; import { catchError, share } from 'rxjs/operators'; +import { MatIconRegistry } from '@angular/material/icon'; @Component({ selector: 'tb-scada-symbol-widget', @@ -75,6 +76,7 @@ export class ScadaSymbolWidgetComponent implements OnInit, AfterViewInit, OnDest protected imagePipe: ImagePipe, protected sanitizer: DomSanitizer, private imageService: ImageService, + private iconRegistry: MatIconRegistry, protected cd: ChangeDetectorRef) { } @@ -138,7 +140,8 @@ export class ScadaSymbolWidgetComponent implements OnInit, AfterViewInit, OnDest this.noScadaSymbol = true; this.cd.markForCheck(); } else { - this.scadaSymbolObject = new ScadaSymbolObject(rootElement, this.ctx, content, + this.scadaSymbolObject = new ScadaSymbolObject(rootElement, this.ctx, this.iconRegistry, + content, this.settings.scadaSymbolObjectSettings, this, simulated); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index 263297ee75..8bf3080433 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -34,7 +34,7 @@ import { mergeDeep, parseFunction } from '@core/utils'; -import { BehaviorSubject, forkJoin, Observable, Observer, Subject } from 'rxjs'; +import { BehaviorSubject, forkJoin, Observable, Observer, of, Subject } from 'rxjs'; import { ValueAction, ValueGetter, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; import { WidgetContext } from '@home/models/widget-component.models'; import { ColorProcessor, constantColor, Font } from '@shared/models/widget-settings.models'; @@ -42,13 +42,16 @@ import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { WidgetAction, WidgetActionType, widgetActionTypeTranslationMap } from '@shared/models/widget.models'; import { ResizeObserver } from '@juggle/resize-observer'; -import { takeUntil } from 'rxjs/operators'; +import { catchError, map, take, takeUntil } from 'rxjs/operators'; +import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; +import { MatIconRegistry } from '@angular/material/icon'; export interface ScadaSymbolApi { generateElementId: () => string; formatValue: (value: any, dec?: number, units?: string, showZeroDecimals?: boolean) => string | undefined; text: (element: Element | Element[], text: string) => void; font: (element: Element | Element[], font: Font, color: string) => void; + icon: (element: Element | Element[], icon: string, size?: number, color?: string, center?: boolean) => void; animate: (element: Element, duration: number) => Runner; resetAnimation: (element: Element) => void; finishAnimation: (element: Element) => void; @@ -133,7 +136,8 @@ export enum ScadaSymbolPropertyType { color = 'color', color_settings = 'color_settings', font = 'font', - units = 'units' + units = 'units', + icon = 'icon' } export const scadaSymbolPropertyTypes = Object.keys(ScadaSymbolPropertyType) as ScadaSymbolPropertyType[]; @@ -146,7 +150,8 @@ export const scadaSymbolPropertyTypeTranslations = new Map { + if (element.type === 'g') { + element.clear(); + this.createIconElement(icon, size, color).subscribe((iconElement) => { + if (iconElement) { + element.add(iconElement); + if (center) { + const box = iconElement.bbox(); + iconElement.translate(-box.cx, -box.cy); + } + } + }); + } + }); + } + + private createIconElement(icon: string, size: number, color: string): Observable { + const isSvg = isSvgIcon(icon); + if (isSvg) { + const [namespace, iconName] = splitIconName(icon); + return this.iconRegistry + .getNamedSvgIcon(iconName, namespace) + .pipe( + take(1), + map((svgElement) => { + const element = new Element(svgElement.firstChild); + const box = element.bbox(); + const scale = size / box.height; + element.scale(scale); + element.fill(color); + return element; + }), + catchError(() => of(null) + )); + } else { + const iconName = splitIconName(icon)[1]; + const textElement = this.svgShape.text(iconName); + const fontSetClasses = ( + this.iconRegistry.getDefaultFontSetClass() + ).filter(className => className.length > 0); + fontSetClasses.forEach(className => textElement.addClass(className)); + textElement.font({size}); + textElement.attr({ + 'text-anchor': 'start', + 'dominant-baseline': 'hanging', + style: `font-size: ${size}px` + }); + textElement.fill(color); + return of(textElement); + } + } + private animate(element: Element, duration: number): Runner { this.finishAnimation(element); return element.animate(duration, 0, 'now'); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html index 5b6d50bf1c..1a4d5eb77b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/scada/scada-symbol-object-settings.component.html @@ -79,6 +79,10 @@ [class]="property.fieldClass" [required]="property.required" formControlName="{{ property.id }}"> + + diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html index 15322b38d4..6090a9504e 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-panel.component.html @@ -76,6 +76,10 @@ + +
scada.property.number-settings
diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts index 3c5ea6cb07..3da851063f 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/metadata-components/scada-symbol-property-row.component.ts @@ -81,6 +81,8 @@ export const defaultPropertyValue = (type: ScadaSymbolPropertyType): any => { } as Font; case ScadaSymbolPropertyType.units: return ''; + case ScadaSymbolPropertyType.icon: + return 'star'; } }; diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts index 433e311fb7..9c1ad96894 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts @@ -491,17 +491,6 @@ export class ScadaSymbolElement { element.show(); } this.box = element.rbox(this.editObject.svgShape); -/* if (element.visible()) { - this.box = element.rbox(this.editObject.svgShape); - if (parentGroup && parentGroup.invisible) { - this.invisible = true; - } - } else { - element.show(); - this.box = element.rbox(this.editObject.svgShape); - element.hide(); - this.invisible = true; - }*/ } public init() { @@ -1297,6 +1286,41 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags } ] }, + icon: { + meta: 'function', + description: 'Draws icon inside element(s). Only applicable for elements of type ' + + 'SVG.G.', + args: [ + { + name: 'element', + description: 'SVG element or an array of SVG elements', + type: 'Element | Array<Element>' + }, + { + name: 'icon', + description: 'Icon to draw', + type: 'string' + }, + { + name: 'size', + description: 'Optional icon size in pixels. Default 12 pixels.', + type: 'number', + optional: true + }, + { + name: 'color', + description: 'Optional icon color. Default #0000008A.', + type: 'string', + optional: true + }, + { + name: 'center', + description: 'Whether to center icon inside group element. Default true.', + type: 'boolean', + optional: true + } + ] + }, disable: { meta: 'function', description: 'Disables element(s). Disabled element doesn\'t accept any user interaction. ' + @@ -1417,6 +1441,8 @@ const scadaSymbolPropertyCompletionType = (type: ScadaSymbolPropertyType): strin return 'Font'; case ScadaSymbolPropertyType.units: return 'units string'; + case ScadaSymbolPropertyType.icon: + return 'icon string'; } }; 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 2b5a33a9b3..37ea4a1a87 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3746,6 +3746,7 @@ "type-color-settings": "Color settings", "type-font": "Font", "type-units": "Units", + "type-icon": "Icon", "no-properties": "No properties configured", "add-property": "Add property", "property-settings": "Property settings",