-
help
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html
index b1e810bcb2..3d524b8402 100644
--- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/copy-keys-config.component.html
@@ -27,7 +27,7 @@
[placeholder]="'rule-node-config.add-key' | translate"
[requiredText]="'rule-node-config.key-val.at-least-one-key-error' | translate"
formControlName="keys">
-
help
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html
index 3df9c1d7d3..aa6ff0bab7 100644
--- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/deduplication-config.component.html
@@ -25,7 +25,7 @@
{{'rule-node-config.interval-min-error' | translate}}
- help
@@ -73,7 +73,7 @@
{{'rule-node-config.max-pending-msgs-min-error' | translate}}
- help
@@ -89,7 +89,7 @@
{{'rule-node-config.max-retries-min-error' | translate}}
- help
diff --git a/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html b/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html
index 75f9c243e9..4385341686 100644
--- a/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html
+++ b/ui-ngx/src/app/modules/home/components/rule-node/transformation/delete-keys-config.component.html
@@ -26,7 +26,7 @@
[placeholder]="'rule-node-config.add-key' | translate"
[requiredText]="'rule-node-config.key-val.at-least-one-key-error' | translate"
formControlName="keys">
-
help
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts
index 7384f76b62..5321e27c05 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts
@@ -43,7 +43,6 @@ import {
isDefined,
isDefinedAndNotNull,
isNotEmptyStr,
- isNumber,
isObject,
isUndefined
} from '@core/utils';
@@ -77,6 +76,8 @@ import {
getHeaderTitle,
getRowStyleInfo,
getTableCellButtonActions,
+ isValidPageStepCount,
+ isValidPageStepIncrement,
noDataMessage,
prepareTableCellButtonActions,
RowStyleInfo,
@@ -392,10 +393,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.rowStylesInfo = getRowStyleInfo(this.ctx, this.settings, 'alarm, ctx');
const pageSize = this.settings.defaultPageSize;
- let pageStepIncrement = this.settings.pageStepIncrement;
- let pageStepCount = this.settings.pageStepCount;
+ let pageStepIncrement = isValidPageStepIncrement(this.settings.pageStepIncrement) ? this.settings.pageStepIncrement : null;
+ let pageStepCount = isValidPageStepCount(this.settings.pageStepCount) ? this.settings.pageStepCount : null;
- if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
+ if (Number.isInteger(pageSize) && pageSize > 0) {
this.defaultPageSize = pageSize;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts
index 2cb5c5a031..d4279caa69 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/pie-chart.ts
@@ -122,17 +122,10 @@ export class TbPieChart extends TbLatestChart {
seriesData.push(
{id: dataItem.id, value: dataItem.value, name: dataItem.dataKey.label, itemStyle: {color: dataItem.dataKey.color}}
);
- if (this.settings.doughnut && enabledDataItems.length > 1) {
- seriesData.push({
- value: 0, name: '', itemStyle: {color: 'transparent'}, emphasis: {disabled: true}
- });
- }
}
}
if (this.settings.doughnut) {
- for (let i = 1; i < seriesData.length; i += 2) {
- seriesData[i].value = this.total / 100;
- }
+ this.latestChartOption.series[0].padAngle = enabledDataItems.length > 1 ? 2 : 0;
}
this.latestChartOption.series[0].data = seriesData;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts
index 653c388364..2a87af2519 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts
@@ -42,7 +42,7 @@ import {
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
-import { deepClone, hashCode, isDefined, isDefinedAndNotNull, isNumber, isObject, isUndefined } from '@core/utils';
+import { deepClone, hashCode, isDefined, isDefinedAndNotNull, isObject, isUndefined } from '@core/utils';
import cssjs from '@core/css/css';
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
@@ -75,6 +75,8 @@ import {
getHeaderTitle,
getRowStyleInfo,
getTableCellButtonActions,
+ isValidPageStepCount,
+ isValidPageStepIncrement,
noDataMessage,
prepareTableCellButtonActions,
RowStyleInfo,
@@ -311,10 +313,10 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
this.rowStylesInfo = getRowStyleInfo(this.ctx, this.settings, 'entity, ctx');
const pageSize = this.settings.defaultPageSize;
- let pageStepIncrement = this.settings.pageStepIncrement;
- let pageStepCount = this.settings.pageStepCount;
+ let pageStepIncrement = isValidPageStepIncrement(this.settings.pageStepIncrement) ? this.settings.pageStepIncrement : null;
+ let pageStepCount = isValidPageStepCount(this.settings.pageStepCount) ? this.settings.pageStepCount : null;
- if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
+ if (Number.isInteger(pageSize) && pageSize > 0) {
this.defaultPageSize = pageSize;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts
index 586f425b13..60f522b192 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts
@@ -36,6 +36,8 @@ import { BehaviorSubject, merge, Observable, of, ReplaySubject, Subject, throwEr
import { catchError, map, tap } from 'rxjs/operators';
import {
constructTableCssString,
+ isValidPageStepCount,
+ isValidPageStepIncrement,
noDataMessage,
TableCellButtonActionDescriptor,
TableWidgetSettings
@@ -43,7 +45,7 @@ import {
import cssjs from '@core/css/css';
import { UtilsService } from '@core/services/utils.service';
import { TranslateService } from '@ngx-translate/core';
-import { hashCode, isDefined, isDefinedAndNotNull, isNumber, parseHttpErrorMessage } from '@core/utils';
+import { hashCode, isDefined, isDefinedAndNotNull, parseHttpErrorMessage } from '@core/utils';
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { emptyPageData, PageData } from '@shared/models/page/page-data';
import {
@@ -207,10 +209,10 @@ export class PersistentTableComponent extends PageComponent implements OnInit, O
this.displayedColumns = [...this.displayTableColumns];
const pageSize = this.settings.defaultPageSize;
- let pageStepIncrement = this.settings.pageStepIncrement;
- let pageStepCount = this.settings.pageStepCount;
+ let pageStepIncrement = isValidPageStepIncrement(this.settings.pageStepIncrement) ? this.settings.pageStepIncrement : null;
+ let pageStepCount = isValidPageStepCount(this.settings.pageStepCount) ? this.settings.pageStepCount : null;
- if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
+ if (Number.isInteger(pageSize) && pageSize > 0) {
this.defaultPageSize = pageSize;
}
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 abe570fa3c..7bd835de88 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
@@ -83,6 +83,10 @@ export interface ScadaSymbolApi {
cssAnimation: (element: Element) => ScadaSymbolAnimation | undefined;
resetCssAnimation: (element: Element) => void;
finishCssAnimation: (element: Element) => void;
+ connectorAnimation:(element: Element) => ConnectorScadaSymbolAnimation | undefined;
+ connectorAnimate:(element: Element, path: string, reversedPath: string) => ConnectorScadaSymbolAnimation;
+ resetConnectorAnimation: (element: Element) => void;
+ finishConnectorAnimation: (element: Element) => void;
disable: (element: Element | Element[]) => void;
enable: (element: Element | Element[]) => void;
callAction: (event: Event, behaviorId: string, value?: any, observer?: Partial>) => void;
@@ -186,6 +190,8 @@ const tbNamespaceRegex = //gm
const tbTagRegex = /tb:tag="([^"]*)"/gms;
+const syncTime = Date.now();
+
const generateElementId = () => {
const id = guid();
const firstChar = id.charAt(0);
@@ -485,6 +491,7 @@ export class ScadaSymbolObject {
private settings: ScadaSymbolObjectSettings;
private context: ScadaSymbolContext;
private cssAnimations: CssScadaSymbolAnimations;
+ private connectorAnimations: ScadaSymbolFlowConnectorAnimations;
private svgShape: Svg;
private box: Box;
@@ -604,6 +611,7 @@ export class ScadaSymbolObject {
private init() {
this.cssAnimations = new CssScadaSymbolAnimations(this.svgShape, this.raf);
+ this.connectorAnimations = new ScadaSymbolFlowConnectorAnimations();
this.context = {
api: {
generateElementId: () => generateElementId(),
@@ -615,6 +623,10 @@ export class ScadaSymbolObject {
cssAnimation: this.cssAnimation.bind(this),
resetCssAnimation: this.resetCssAnimation.bind(this),
finishCssAnimation: this.finishCssAnimation.bind(this),
+ connectorAnimation: this.connectorAnimation.bind(this),
+ connectorAnimate: this.connectorAnimate.bind(this),
+ resetConnectorAnimation: this.resetConnectorAnimation.bind(this),
+ finishConnectorAnimation: this.finishConnectorAnimation.bind(this),
disable: this.disableElement.bind(this),
enable: this.enableElement.bind(this),
callAction: this.callAction.bind(this),
@@ -959,6 +971,22 @@ export class ScadaSymbolObject {
this.cssAnimations.finishAnimation(element);
}
+ private connectorAnimate(element: Element, path: string, reversedPath: string): ConnectorScadaSymbolAnimation {
+ return this.connectorAnimations.animate(element, path, reversedPath);
+ }
+
+ private connectorAnimation(element: Element): ConnectorScadaSymbolAnimation | undefined {
+ return this.connectorAnimations.animation(element);
+ }
+
+ private resetConnectorAnimation(element: Element) {
+ this.connectorAnimations.resetAnimation(element);
+ }
+
+ private finishConnectorAnimation(element: Element) {
+ this.connectorAnimations.finishAnimation(element);
+ }
+
private disableElement(e: Element | Element[]) {
this.elements(e).forEach(element => {
element.attr({'pointer-events': 'none'});
@@ -1108,6 +1136,20 @@ interface ScadaSymbolAnimation {
}
+const scadaSymbolConnectorFlowAnimationId = 'scadaSymbolConnectorFlowAnimation';
+
+type StrokeLineCap = 'butt' | 'round '| 'square';
+
+interface ConnectorScadaSymbolAnimation {
+ play(): void;
+ stop(): void;
+ finish(): void;
+
+ flowAppearance(width: number, color: string, lineCap: StrokeLineCap, dashWidth: number, dashGap: number): ConnectorScadaSymbolAnimation;
+ duration(speed: number): ConnectorScadaSymbolAnimation;
+ direction(direction: boolean): ConnectorScadaSymbolAnimation;
+}
+
class CssScadaSymbolAnimations {
constructor(private svgShape: Svg,
private raf: RafService) {}
@@ -1159,6 +1201,132 @@ class CssScadaSymbolAnimations {
}
}
+class ScadaSymbolFlowConnectorAnimations {
+ constructor() {}
+
+ public animate(element: Element, path = '', reversedPath = ''): ConnectorScadaSymbolAnimation {
+ this.checkOldAnimation(element);
+ return this.setupAnimation(element, this.createAnimation(element, path, reversedPath));
+ }
+
+ public animation(element: Element): ConnectorScadaSymbolAnimation | undefined {
+ return element.remember(scadaSymbolConnectorFlowAnimationId);
+ }
+
+ public resetAnimation(element: Element) {
+ const animation: ConnectorScadaSymbolAnimation = element.remember(scadaSymbolConnectorFlowAnimationId);
+ if (animation) {
+ animation.stop();
+ element.remember(scadaSymbolConnectorFlowAnimationId, null);
+ }
+ }
+
+ public finishAnimation(element: Element) {
+ const animation: ConnectorScadaSymbolAnimation = element.remember(scadaSymbolConnectorFlowAnimationId);
+ if (animation) {
+ animation.finish();
+ element.remember(scadaSymbolConnectorFlowAnimationId, null);
+ }
+ }
+
+ private setupAnimation(element: Element, animation: ConnectorScadaSymbolAnimation): ConnectorScadaSymbolAnimation {
+ element.remember(scadaSymbolConnectorFlowAnimationId, animation);
+ return animation;
+ }
+
+ private checkOldAnimation(element: Element) {
+ const previousAnimation: ConnectorScadaSymbolAnimation = element.remember(scadaSymbolConnectorFlowAnimationId);
+ if (previousAnimation) {
+ previousAnimation.finish();
+ }
+ }
+
+ private createAnimation(element: Element, path: string, reversedPath: string): ConnectorScadaSymbolAnimation {
+ return new FlowConnectorAnimation(element, path, reversedPath);
+ }
+}
+
+class FlowConnectorAnimation implements ConnectorScadaSymbolAnimation {
+
+ private readonly _path: string;
+ private readonly _reversedPath: string;
+ private readonly _animation: Element;
+
+ private _duration: number = 1;
+ private _lineColor: string = '#C8DFF7';
+ private _lineWidth: number = 4;
+ private _strokeLineCap: StrokeLineCap = 'butt';
+ private _dashWidth: number = 10;
+ private _dashGap: number = 10;
+ private _direction: boolean = true;
+
+ constructor(private element: Element,
+ path: string,
+ pathReversed: string) {
+ this._path = path;
+ this._reversedPath = pathReversed;
+
+ const dashArray = `${this._dashWidth} ${this._dashGap}`;
+ const values = `${this._dashWidth + this._dashGap};0`;
+
+ this._animation = SVG(
+ `` +
+ ``
+ );
+ }
+
+ public play() {
+ if (!this.element.node.childElementCount) {
+ this.element.add(this._animation);
+ }
+ const animateElement = this.element.node.getElementsByTagName('animate')[0];
+ const offset = ((Date.now() - syncTime) % 1000) * -1;
+ (animateElement as SVGAnimationElement).beginElementAt(offset);
+ }
+
+ public stop() {
+ const animateElement = this.element.node.getElementsByTagName('animate')[0];
+ (animateElement as SVGAnimationElement)?.endElement();
+ }
+
+ public finish() {
+ this.element.findOne('path')?.remove();
+ }
+
+ public flowAppearance(width: number, color: string, linecap: StrokeLineCap, dashWidth: number, dashGap: number): this {
+ const totalLength = (this._animation.node as SVGPathElement).getTotalLength();
+ let offset = 0;
+ if ((totalLength % 100) !== 0) {
+ const clientWidth = totalLength < 100 ? 100 : this.element.node.ownerSVGElement.clientWidth;
+ const clientWidthDash = clientWidth / (dashWidth + dashGap);
+ const totalLengthDash = totalLength / clientWidthDash;
+ offset = ((dashWidth + dashGap) - totalLengthDash) / 2;
+ }
+ this._lineColor = color;
+ this._lineWidth = width;
+ this._strokeLineCap = linecap;
+ this._dashWidth = dashWidth - offset;
+ this._dashGap = dashGap - offset;
+ const dashArray = `${this._dashWidth} ${this._dashGap}`;
+ const values = `${this._dashWidth + (this._dashGap || this._dashWidth)};0`;
+ this._animation.stroke({width, color, linecap, dasharray: dashArray});
+ this._animation.findOne('animate').attr('values', values);
+ return this;
+ }
+
+ public duration(speed: number): this {
+ this._duration = speed;
+ this._animation.findOne('animate').attr('dur', `${speed}s`);
+ return this;
+ }
+
+ public direction(direction: boolean): this {
+ this._direction = direction;
+ this._animation.attr('d', direction ? this._path : this._reversedPath);
+ return this;
+ }
+}
+
interface ScadaSymbolAnimationKeyframe {
stop: string;
style: any;
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..af59457abe 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
@@ -30,6 +30,12 @@
+