From 0c1fd9785097cec8948135cb196d65fe8870eabc Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 23 Oct 2020 17:50:15 +0300 Subject: [PATCH 01/12] UI: Improvement photo camera input widget --- .../system/widget_bundles/input_widgets.json | 8 +- ...html => photo-camera-input.component.html} | 17 +-- ...scss => photo-camera-input.component.scss} | 8 ++ ...ent.ts => photo-camera-input.component.ts} | 108 ++++++++++++------ .../widget/widget-components.module.ts | 6 +- .../assets/locale/locale.constant-en_US.json | 5 +- 6 files changed, 98 insertions(+), 54 deletions(-) rename ui-ngx/src/app/modules/home/components/widget/lib/{web-camera-input.component.html => photo-camera-input.component.html} (81%) rename ui-ngx/src/app/modules/home/components/widget/lib/{web-camera-input.component.scss => photo-camera-input.component.scss} (92%) rename ui-ngx/src/app/modules/home/components/widget/lib/{web-camera-input.component.ts => photo-camera-input.component.ts} (72%) diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json index ac3486223a..097c31dac4 100644 --- a/application/src/main/data/json/system/widget_bundles/input_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json @@ -391,16 +391,16 @@ }, { "alias": "web_camera_input", - "name": "Web Camera Input", + "name": "Photo camera input", "descriptor": { "type": "latest", "sizeX": 7.5, "sizeY": 3, "resources": [], - "templateHtml": "\n", + "templateHtml": "\n", "templateCss": "", - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.webCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Web Camera\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"imageFormat\": {\n \"title\": \"Image Format\",\n \"type\": \"string\",\n \"default\": \"image/png\"\n },\n \"imageQuality\":{\n \"title\":\"Image quality that use lossy compression such as jpeg and webp\",\n \"type\":\"number\",\n \"default\": 0.92,\n \"min\": 0,\n \"max\": 1\n },\n \"maxWidth\": {\n \"title\": \"The maximal image width\",\n \"type\": \"number\",\n \"default\": 640\n }, \n \"maxHeight\": {\n \"title\": \"The maximal image heigth\",\n \"type\": \"number\",\n \"default\": 480\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n {\n \"key\": \"imageFormat\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"image/jpeg\",\n \"label\": \"JPEG\"\n },\n {\n \"value\": \"image/png\",\n \"label\": \"PNG\"\n },\n {\n \"value\": \"image/webp\",\n \"label\": \"WEBP\"\n }\n ]\n },\n \"imageQuality\",\n \"maxWidth\",\n \"maxHeight\"\n ]\n}", + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.photoCameraInputWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Photo Camera\",\n \"properties\": {\n \"widgetTitle\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"imageFormat\": {\n \"title\": \"Image Format\",\n \"type\": \"string\",\n \"default\": \"image/png\"\n },\n \"imageQuality\":{\n \"title\":\"Image quality that use lossy compression such as jpeg and webp\",\n \"type\":\"number\",\n \"default\": 0.92,\n \"min\": 0,\n \"max\": 1\n },\n \"maxWidth\": {\n \"title\": \"The maximal image width\",\n \"type\": \"number\",\n \"default\": 640\n }, \n \"maxHeight\": {\n \"title\": \"The maximal image heigth\",\n \"type\": \"number\",\n \"default\": 480\n }\n },\n \"required\": []\n },\n \"form\": [\n \"widgetTitle\",\n {\n \"key\": \"imageFormat\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"image/jpeg\",\n \"label\": \"JPEG\"\n },\n {\n \"value\": \"image/png\",\n \"label\": \"PNG\"\n },\n {\n \"value\": \"image/webp\",\n \"label\": \"WEBP\"\n }\n ]\n },\n \"imageQuality\",\n \"maxWidth\",\n \"maxHeight\"\n ]\n}", "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{},\"title\":\"Web Camera Input\",\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.html similarity index 81% rename from ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.html index 88e5475a40..b3b457849b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.html @@ -16,13 +16,13 @@ -->
-
+
widgets.input-widgets.no-image last photo
-
@@ -59,16 +59,7 @@
-
- {{ 'widgets.input-widgets.no-entity-selected' | translate }} -
-
- {{ 'widgets.input-widgets.no-datakey-selected' | translate }} -
-
- {{ 'widgets.input-widgets.no-support-web-camera' | translate }} -
-
- {{ 'widgets.input-widgets.no-support-web-camera' | translate }} +
+ {{ textMessage | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.scss similarity index 92% rename from ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.scss index 94a51e6870..ea364e17a1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.scss @@ -18,6 +18,7 @@ &__last-photo { width: 100%; + min-height: 0; margin: 5px 0; text-align: center; border: solid 1px; @@ -71,4 +72,11 @@ color: #a0a0a0; text-align: center; } + + .image-container{ + height: 100%; + min-height: 0; + width: 100%; + min-width: 100%; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.ts similarity index 72% rename from ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.ts index ee92ee8fe0..aebc500faf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/web-camera-input.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/photo-camera-input.component.ts @@ -40,7 +40,7 @@ import { Observable } from 'rxjs'; import { isString } from '@core/utils'; import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; -interface WebCameraInputWidgetSettings { +interface PhotoCameraInputWidgetSettings { widgetTitle: string; imageQuality: number; imageFormat: string; @@ -50,12 +50,12 @@ interface WebCameraInputWidgetSettings { // @dynamic @Component({ - selector: 'tb-web-camera-widget', - templateUrl: './web-camera-input.component.html', - styleUrls: ['./web-camera-input.component.scss'], + selector: 'tb-photo-camera-widget', + templateUrl: './photo-camera-input.component.html', + styleUrls: ['./photo-camera-input.component.scss'], encapsulation: ViewEncapsulation.None }) -export class WebCameraInputWidgetComponent extends PageComponent implements OnInit, OnDestroy { +export class PhotoCameraInputWidgetComponent extends PageComponent implements OnInit, OnDestroy { constructor(@Inject(WINDOW) private window: Window, protected store: Store, @@ -93,11 +93,11 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn @Input() ctx: WidgetContext; - @ViewChild('videoStream', {static: true}) videoStreamRef: ElementRef; - @ViewChild('canvas', {static: true}) canvasRef: ElementRef; + @ViewChild('videoStream', {static: false}) videoStreamRef: ElementRef; + @ViewChild('canvas', {static: false}) canvasRef: ElementRef; private videoInputsIndex = 0; - private settings: WebCameraInputWidgetSettings; + private settings: PhotoCameraInputWidgetSettings; private datasource: Datasource; private width = 640; private height = 480; @@ -106,10 +106,13 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn isEntityDetected = false; dataKeyDetected = false; + isProtocolHttps = false; isCameraSupport = false; isDeviceDetect = false; isShowCamera = false; isPreviewPhoto = false; + isHavePermissionCamera = true; + isLoading = false; singleDevice = true; updatePhoto = false; previewPhoto: SafeUrl; @@ -132,7 +135,8 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn } ngOnInit(): void { - this.ctx.$scope.webCameraInputWidget = this; + this.ctx.$scope.photoCameraInputWidget = this; + this.isLoading = true; this.settings = this.ctx.settings; this.datasource = this.ctx.datasources[0]; @@ -168,25 +172,33 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn } public onDataUpdated() { - this.ngZone.run(() => { - this.updateWidgetData(this.ctx.defaultSubscription.data); - this.ctx.detectChanges(); - }); + this.updateWidgetData(this.ctx.defaultSubscription.data); + this.ctx.detectChanges(); } private detectAvailableDevices(): void { - if (WebCameraInputWidgetComponent.hasGetUserMedia()) { - this.isCameraSupport = true; - WebCameraInputWidgetComponent.getAvailableVideoInputs().then((devices) => { - this.isDeviceDetect = !!devices.length; - this.singleDevice = devices.length < 2; - this.availableVideoInputs = devices; - this.ctx.detectChanges(); - }, () => { - this.availableVideoInputs = []; - } - ) + if (this.window.location.protocol === 'https:' || this.window.location.hostname === 'localhost') { + this.isProtocolHttps = true; + + if (PhotoCameraInputWidgetComponent.hasGetUserMedia()) { + this.isCameraSupport = true; + PhotoCameraInputWidgetComponent.getAvailableVideoInputs().then((devices) => { + this.isLoading = false; + this.isDeviceDetect = !!devices.length; + this.singleDevice = devices.length < 2; + this.availableVideoInputs = devices; + this.ctx.detectChanges(); + }, () => { + this.isLoading = false; + this.availableVideoInputs = []; + } + ); + } else { + this.isLoading = false; + } + } else { + this.isLoading = false; } } @@ -206,8 +218,7 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn } takePhoto() { - this.isShowCamera = true; - this.initWebCamera(this.availableVideoInputs[this.videoInputsIndex].deviceId); + this.inititedVideoStream(this.availableVideoInputs[this.videoInputsIndex].deviceId, true); } closeCamera() { @@ -243,13 +254,13 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn this.closeCamera(); }, () => { this.updatePhoto = false; - }) + }); } switchWebCamera() { this.videoInputsIndex = (this.videoInputsIndex + 1) % this.availableVideoInputs.length; this.stopMediaTracks(); - this.initWebCamera(this.availableVideoInputs[this.videoInputsIndex].deviceId) + this.inititedVideoStream(this.availableVideoInputs[this.videoInputsIndex].deviceId); } createPhoto() { @@ -257,22 +268,53 @@ export class WebCameraInputWidgetComponent extends PageComponent implements OnIn this.canvasElement.height = this.videoHeight; this.canvasElement.getContext('2d').drawImage(this.videoElement, 0, 0, this.videoWidth, this.videoHeight); - const mimeType: string = this.settings.imageFormat ? this.settings.imageFormat : WebCameraInputWidgetComponent.DEFAULT_IMAGE_TYPE; - const quality: number = this.settings.imageQuality ? this.settings.imageQuality : WebCameraInputWidgetComponent.DEFAULT_IMAGE_QUALITY; - this.previewPhoto = this.sanitizer.bypassSecurityTrustUrl(this.canvasElement.toDataURL(mimeType, quality)); + const mimeType: string = this.settings.imageFormat ? this.settings.imageFormat : PhotoCameraInputWidgetComponent.DEFAULT_IMAGE_TYPE; + const quality: number = this.settings.imageQuality ? this.settings.imageQuality : PhotoCameraInputWidgetComponent.DEFAULT_IMAGE_QUALITY; + this.previewPhoto = this.canvasElement.toDataURL(mimeType, quality); this.isPreviewPhoto = true; } - private initWebCamera(deviceId?: string) { + private inititedVideoStream(deviceId?: string, init = false) { if (window.navigator.mediaDevices && window.navigator.mediaDevices.getUserMedia) { const videoTrackConstraints = { video: {deviceId: deviceId !== '' ? {exact: deviceId} : undefined} }; window.navigator.mediaDevices.getUserMedia(videoTrackConstraints).then((stream: MediaStream) => { + if (init) { + this.isShowCamera = true; + } this.mediaStream = stream; this.videoElement.srcObject = stream; - }) + this.ctx.detectChanges(); + }, () => { + this.isHavePermissionCamera = false; + }); } } + + get textMessage() { + if (this.isLoading) { + return ''; + } + if (!this.isProtocolHttps) { + return 'widgets.input-widgets.enable-https-use-widget'; + } + if (!this.isCameraSupport) { + return 'widgets.input-widgets.no-support-web-camera'; + } + if (!this.isEntityDetected) { + return 'widgets.input-widgets.no-entity-selected'; + } + if (!this.dataKeyDetected) { + return 'widgets.input-widgets.no-datakey-selected'; + } + if (!this.isDeviceDetect) { + return 'widgets.input-widgets.no-found-your-camera'; + } + if (!this.isHavePermissionCamera) { + return 'widgets.input-widgets.no-permission-camera'; + } + return null; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index a97096a953..c2a158fcdf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -32,7 +32,7 @@ import { } from '@home/components/widget/lib/date-range-navigator/date-range-navigator.component'; import { MultipleInputWidgetComponent } from './lib/multiple-input-widget.component'; import { TripAnimationComponent } from './trip-animation/trip-animation.component'; -import { WebCameraInputWidgetComponent } from './lib/web-camera-input.component'; +import { PhotoCameraInputWidgetComponent } from './lib/photo-camera-input.component'; import { GatewayFormComponent } from './lib/gateway/gateway-form.component'; import { ImportExportService } from '@home/components/import-export/import-export.service'; @@ -49,7 +49,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor DateRangeNavigatorPanelComponent, MultipleInputWidgetComponent, TripAnimationComponent, - WebCameraInputWidgetComponent, + PhotoCameraInputWidgetComponent, GatewayFormComponent ], imports: [ @@ -67,7 +67,7 @@ import { ImportExportService } from '@home/components/import-export/import-expor DateRangeNavigatorWidgetComponent, MultipleInputWidgetComponent, TripAnimationComponent, - WebCameraInputWidgetComponent, + PhotoCameraInputWidgetComponent, GatewayFormComponent ], providers: [ 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 453d5d2abd..20a01cc99a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2283,7 +2283,10 @@ "no-entity-selected": "No entity selected", "no-image": "No image", "no-support-geolocation": "Your browser doesn't support geolocation", - "no-support-web-camera": "No supported web camera", + "no-support-web-camera": "Your browser does not support cameras", + "enable-https-use-widget": "Please enable HTTPS to use this widget", + "no-found-your-camera": "Can't find your camera", + "no-permission-camera": "Permission was denied by the user / This site doesn't have permission to use the camera", "no-timeseries-selected": "No timeseries selected", "secret-key": "Secret key", "secret-key-required": "Secret key is required", From c40f6bf47c58cfc26d6d04729093da50e0a3b054 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 23 Oct 2020 18:42:07 +0300 Subject: [PATCH 02/12] UI: Added new options for legend config (sort datakeys in legend) --- ui-ngx/src/app/core/api/widget-subscription.ts | 3 --- .../components/widget/legend-config-panel.component.html | 3 +++ .../components/widget/legend-config-panel.component.ts | 1 + .../app/modules/home/components/widget/legend.component.ts | 7 +++++-- ui-ngx/src/app/shared/models/widget.models.ts | 2 ++ ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index f7017652d8..123fe010c8 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -1226,9 +1226,6 @@ export class WidgetSubscription implements IWidgetSubscription { }); }); } - if (this.displayLegend) { - this.legendData.keys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label)); - } if (this.caulculateLegendData) { this.data.forEach((dataSetHolder, keyIndex) => { this.updateLegend(keyIndex, dataSetHolder.data, false); diff --git a/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.html index 15e5466e80..28cabf7cd8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.html @@ -38,6 +38,9 @@ + + {{ 'legend.sort-legend' | translate }} + {{ 'legend.show-min' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.ts index 5f5abff7a3..05e515100e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/legend-config-panel.component.ts @@ -68,6 +68,7 @@ export class LegendConfigPanelComponent extends PageComponent implements OnInit this.legendConfigForm = this.fb.group({ direction: [this.data.legendConfig.direction, []], position: [this.data.legendConfig.position, []], + sortDataKeys: [this.data.legendConfig.sortDataKeys, []], showMin: [this.data.legendConfig.showMin, []], showMax: [this.data.legendConfig.showMax, []], showAvg: [this.data.legendConfig.showAvg, []], diff --git a/ui-ngx/src/app/modules/home/components/widget/legend.component.ts b/ui-ngx/src/app/modules/home/components/widget/legend.component.ts index 55fffa2b5b..3691742406 100644 --- a/ui-ngx/src/app/modules/home/components/widget/legend.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/legend.component.ts @@ -60,8 +60,11 @@ export class LegendComponent implements OnInit { } legendKeys(): LegendKey[] { - return this.legendData.keys - .filter(legendKey => this.legendData.keys[legendKey.dataIndex].dataKey.inLegend); + let keys = this.legendData.keys; + if (this.legendConfig.sortDataKeys) { + keys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label)); + } + return keys.filter(legendKey => this.legendData.keys[legendKey.dataIndex].dataKey.inLegend); } } diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 8ea338a98f..a97a66a1ef 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -203,6 +203,7 @@ export const legendPositionTranslationMap = new Map( export interface LegendConfig { position: LegendPosition; direction?: LegendDirection; + sortDataKeys: boolean; showMin: boolean; showMax: boolean; showAvg: boolean; @@ -213,6 +214,7 @@ export function defaultLegendConfig(wType: widgetType): LegendConfig { return { direction: LegendDirection.column, position: LegendPosition.bottom, + sortDataKeys: false, showMin: false, showMax: false, showAvg: wType === widgetType.timeseries, 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 453d5d2abd..11cf7dcbc0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1664,6 +1664,7 @@ "legend": { "direction": "Legend direction", "position": "Legend position", + "sort-legend": "Sort datakeys in legend", "show-max": "Show max value", "show-min": "Show min value", "show-avg": "Show average value", From fec424f5c536f9e6d3ff015a0685e131d1b278cc Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Sat, 24 Oct 2020 20:56:34 +0300 Subject: [PATCH 03/12] UI: Fixed problem for json-form component selector --- ui-ngx/package.json | 6 +-- ui-ngx/yarn.lock | 126 +++++++++++++++++++++++++++----------------- 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 4612631ff6..8e3ede2934 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -76,8 +76,8 @@ "prettier": "^2.1.2", "prop-types": "^15.7.2", "raphael": "^2.3.0", - "rc-select": "^11.3.3", - "react": "^16.13.1", + "rc-select": "~10.5.1", + "react": "~16.14.0", "react-ace": "^9.1.4", "react-dom": "^16.13.1", "react-dropzone": "^11.2.0", @@ -92,7 +92,7 @@ "tslib": "^2.0.2", "tv4": "^1.3.0", "typeface-roboto": "^1.1.13", - "zone.js": "~0.11.1" + "zone.js": "~0.10.3" }, "devDependencies": { "@angular-builders/custom-webpack": "^10.0.1", diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 58d275db1b..dcdc0909c0 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -272,6 +272,11 @@ dependencies: tslib "^2.0.0" +"@ant-design/css-animation@^1.7.2": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@ant-design/css-animation/-/css-animation-1.7.3.tgz#60a1c970014e86b28f940510d69e503e428f1136" + integrity sha512-LrX0OGZtW+W6iLnTAqnTaoIsRelYeuLZWsrmBJFUXDALQphPsN8cE5DCsmoSlL0QYb94BQxINiuS70Ar/8BNgA== + "@auth0/angular-jwt@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@auth0/angular-jwt/-/angular-jwt-5.0.1.tgz#37851d3ca2a0e88b3e673afd7dd2891f0c61bdf5" @@ -1787,6 +1792,13 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +add-dom-event-listener@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310" + integrity sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw== + dependencies: + object-assign "4.x" + adjust-sourcemap-loader@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-2.0.0.tgz#6471143af75ec02334b219f54bc7970c52fb29a4" @@ -6752,7 +6764,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -7645,7 +7657,7 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" -prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -7810,6 +7822,13 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +raf@^3.4.0, raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -7866,50 +7885,63 @@ rc-align@^4.0.0: rc-util "^5.3.0" resize-observer-polyfill "^1.5.1" -rc-motion@^2.0.0, rc-motion@^2.0.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.3.1.tgz#a0c9f402c267bd03543ef80a970297a6ba77c503" - integrity sha512-UAB2gwS9c1DuCFKVCimAkL2JUEGCwzgCbb+VslhIMFg6vY7oyMxYIQ6hkoji1PzgDEw0tHIhP+a17R6Y5DGMrQ== +rc-animate@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-3.1.1.tgz#defdd863f56816c222534e4dc68feddecd081386" + integrity sha512-8wg2Zg3EETy0k/9kYuis30NJNQg1D6/WSQwnCiz6SvyxQXNet/rVraRz3bPngwY6rcU2nlRvoShiYOorXyF7Sg== + dependencies: + "@ant-design/css-animation" "^1.7.2" + classnames "^2.2.6" + raf "^3.4.0" + rc-util "^4.15.3" + +rc-motion@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-1.1.2.tgz#07212f1b96c715b8245ec121339146c4a9b0968c" + integrity sha512-YC/E7SSWKBFakYg4PENhSRWD4ZLDqkI7FKmutJcrMewZ91/ZIWfoZSDvPaBdKO0hsFrrzWepFhXQIq0FNnCMWA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.2.1" + raf "^3.4.1" + rc-util "^5.0.6" -rc-resize-observer@^0.2.3: - version "0.2.5" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-0.2.5.tgz#03e3a5c3dfccd6c996a547e4f82721e4f20f6156" - integrity sha512-cc4sOI722MVoCkGf/ZZybDVsjxvnH0giyDdA7wBJLTiMSFJ0eyxBMnr0JLYoClxftjnr75Xzl/VUB3HDrAx04Q== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.1" - rc-util "^5.0.0" - resize-observer-polyfill "^1.5.1" - -rc-select@^11.3.3: - version "11.3.3" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-11.3.3.tgz#ba445ac4d2d933dd1f80b796c1de28ce6c81bbf8" - integrity sha512-YMsGVEZxXctj15nIZKlFCkiOxMe0PNBeACN6nHqDozDYKR/aqP8J3XZqZ5Gw/fcgS4bI50zPVMieJKlY8/6Wfw== +rc-select@~10.5.1: + version "10.5.1" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-10.5.1.tgz#4d4c5d4f8d2fd3b7e3dccf74e4c43142b2979247" + integrity sha512-fZraoNNhjUmDJccfk6VYgrgHBWFmHWh/NJZh2Xttcm/usOolYI1RjO9ikP4QGhzlJBFtnTLH2pE2nchjn3TCXA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-motion "^2.0.1" - rc-trigger "^5.0.4" + rc-animate "^3.0.0" + rc-trigger "^4.3.0" rc-util "^5.0.1" - rc-virtual-list "^3.0.3" + rc-virtual-list "^1.1.2" warning "^4.0.3" -rc-trigger@^5.0.4: - version "5.0.6" - resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.0.6.tgz#7e84717525871a7923a671edb5a290e9e616525b" - integrity sha512-L/xIX5OO7a/bdTH0yYT9mwAsV6oM1inAqAbLjoUzpcIW+UUE3jjVOjm5VaKDfHd41rzDzOfN05URmhet5QzXKQ== +rc-trigger@^4.3.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-4.4.3.tgz#ed449cd6cce446555bc57f4394229c5c7154f4b0" + integrity sha512-yq/WyuiPwxd2q6jy+VPyy0GUCRFJ2eFqAaCwPE27AOftXeIupOcJ/2t1wakSq63cfk7qtzev5DKHUAjb8LOJCw== dependencies: "@babel/runtime" "^7.11.2" classnames "^2.2.6" + raf "^3.4.1" rc-align "^4.0.0" - rc-motion "^2.0.0" - rc-util "^5.3.4" + rc-motion "^1.0.0" + rc-util "^5.0.1" -rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.0.7, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.3.4: +rc-util@^4.15.3: + version "4.21.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.21.1.tgz#88602d0c3185020aa1053d9a1e70eac161becb05" + integrity sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg== + dependencies: + add-dom-event-listener "^1.1.0" + prop-types "^15.5.10" + react-is "^16.12.0" + react-lifecycles-compat "^3.0.4" + shallowequal "^1.1.0" + +rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.3.0: version "5.4.0" resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.4.0.tgz#688eaeecfdae9dae2bfdf10bedbe884591dba004" integrity sha512-kXDn1JyLJTAWLBFt+fjkTcUtXhxKkipQCobQmxIEVrX62iXgo24z8YKoWehWfMxPZFPE+RXqrmEu9j5kHz/Lrg== @@ -7917,14 +7949,14 @@ rc-util@^5.0.0, rc-util@^5.0.1, rc-util@^5.0.7, rc-util@^5.2.1, rc-util@^5.3.0, react-is "^16.12.0" shallowequal "^1.1.0" -rc-virtual-list@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.1.0.tgz#ca7ddbb291dace89c00cc4198ca7ef6e5e2034f7" - integrity sha512-DYU3wOjVuQo4hzYvmmpnoNtxrd8OIcutazA90x374ZFGGm4xYoSjCdh6UhBLi47IJI2BRry4l583nuoi7+06GA== +rc-virtual-list@^1.1.2: + version "1.1.6" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-1.1.6.tgz#b255baf9aacde149a8893324e6307214094f4c0a" + integrity sha512-u3+izqWL8p8bQy8nYH48qWpiGyxR/ye8D2k0zJlXmfYeL55/xh83YrzHqiDzO78uj0Ewag3nXDA0JTVrYO7ygQ== dependencies: classnames "^2.2.6" - rc-resize-observer "^0.2.3" - rc-util "^5.0.7" + raf "^3.4.1" + rc-util "^5.0.0" react-ace@^9.1.4: version "9.1.4" @@ -7961,6 +7993,11 @@ react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-transition-group@^4.0.0, react-transition-group@^4.4.0: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" @@ -7971,10 +8008,10 @@ react-transition-group@^4.0.0, react-transition-group@^4.4.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" - integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== +react@~16.14.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -10206,10 +10243,3 @@ zone.js@~0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== - -zone.js@~0.11.1: - version "0.11.1" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.11.1.tgz#0301d00d26febb2722f074c46aac4a948698ce39" - integrity sha512-KcZawpmVgS+3U2rzKTM6fLKaCX1QDv3//NxiSOOsqpQY/r5hl+xpYikPwY93Sp7CAB+J5mZJpb/YubxEYLGK5g== - dependencies: - tslib "^2.0.0" From b9950d82e906b51ad4ce8a4039b78bef1049118d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 29 Oct 2020 14:10:05 -0400 Subject: [PATCH 04/12] UI: Fixed show not correct time for device profiles scheduler preview --- ui-ngx/src/app/shared/models/device.models.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 9fbe289543..e6d96346cd 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -26,7 +26,7 @@ import { EntityInfoData } from '@shared/models/entity.models'; import { KeyFilter } from '@shared/models/query/query.models'; import { TimeUnit } from '@shared/models/time/time.models'; import * as _moment from 'moment-timezone'; -import { AbstractControl, FormGroup, ValidationErrors } from '@angular/forms'; +import { AbstractControl, ValidationErrors } from '@angular/forms'; export enum DeviceProfileType { DEFAULT = 'DEFAULT' @@ -513,7 +513,7 @@ export function timeOfDayToUTCTimestamp(date: Date | number): number { } export function utcTimestampToTimeOfDay(time = 0): Date { - return new Date(time + new Date().getTimezoneOffset() * 60 * 1000); + return new Date(time + new Date(time).getTimezoneOffset() * 60 * 1000); } function timeOfDayToMoment(date: Date | number): _moment.Moment { From 6dbb4897b4816a4aa4733e0745f18dd5cd755a43 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 4 Nov 2020 20:17:56 +0200 Subject: [PATCH 05/12] UI: Fixed resize width windows device-wizard-dialog; improvement translate en_US --- .../wizard/device-wizard-dialog.component.html | 5 ++++- .../wizard/device-wizard-dialog.component.scss | 10 ++++++++++ ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 362b3c14ce..8e91be0cc5 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -82,7 +82,8 @@ [selectFirstProfile]="true" [editProfileEnabled]="false"> - + device-profile.new-device-profile-name @@ -93,12 +94,14 @@
diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss index 55878d638b..b84ff8105f 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.scss @@ -22,6 +22,10 @@ max-height: 75vh; } } + + .invisible{ + visibility: hidden; + } } :host ::ng-deep { @@ -60,6 +64,12 @@ height: 100%; } } + + tb-device-profile-autocomplete, tb-queue-type-list{ + .mat-form-field-wrapper{ + width: 180px !important; + } + } } } } 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 e3b7b4b81d..5c044cbff0 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -874,7 +874,7 @@ "profile-configuration": "Profile configuration", "transport-configuration": "Transport configuration", "default-rule-chain": "Default rule chain", - "select-queue-hint": "The queue name can be selected from a drop-down list or add a custom name.", + "select-queue-hint": "Select from a drop-down list or add a custom name.", "delete-device-profile-title": "Are you sure you want to delete the device profile '{{deviceProfileName}}'?", "delete-device-profile-text": "Be careful, after the confirmation the device profile and all related data will become unrecoverable.", "delete-device-profiles-title": "Are you sure you want to delete { count, plural, 1 {1 device profile} other {# device profiles} }?", From f5550f52cfcb7b041b6d5b40561ef2a6508d8758 Mon Sep 17 00:00:00 2001 From: Vladyslav Date: Wed, 4 Nov 2020 23:17:01 +0200 Subject: [PATCH 06/12] Refactoring: add invisible elements --- .../home/components/wizard/device-wizard-dialog.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 8e91be0cc5..57bf9ed5cb 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -76,6 +76,7 @@ [required]="!createProfile" [transportType]="deviceWizardFormGroup.get('transportType').value" formControlName="deviceProfileId" + [ngClass]="{invisible: deviceWizardFormGroup.get('addProfileType').value !== 0}" (deviceProfileChanged)="$event?.transportType ? deviceWizardFormGroup.get('transportType').patchValue($event?.transportType) : {}" [addNewProfile]="false" [selectDefaultProfile]="true" From 39c2e3c39dd11f2f4bd9cc41a0a72cf031f39d22 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 6 Nov 2020 15:58:00 +0200 Subject: [PATCH 07/12] UI: Fixed updated Alarm widget in change entity state parameters --- .../src/app/core/api/alarm-data-subscription.ts | 4 ++-- ui-ngx/src/app/core/api/alarm-data.service.ts | 17 ++++++++--------- ui-ngx/src/app/core/api/widget-subscription.ts | 4 ++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ui-ngx/src/app/core/api/alarm-data-subscription.ts b/ui-ngx/src/app/core/api/alarm-data-subscription.ts index 4d3524324e..35a43c3f0f 100644 --- a/ui-ngx/src/app/core/api/alarm-data-subscription.ts +++ b/ui-ngx/src/app/core/api/alarm-data-subscription.ts @@ -52,6 +52,7 @@ export interface AlarmDataSubscriptionOptions { export class AlarmDataSubscription { + private alarmDataSubscriptionOptions = this.listener.alarmDataSubscriptionOptions; private datasourceType: DatasourceType = this.alarmDataSubscriptionOptions.datasourceType; private history: boolean; @@ -65,8 +66,7 @@ export class AlarmDataSubscription { private subsTw: SubscriptionTimewindow; - constructor(public alarmDataSubscriptionOptions: AlarmDataSubscriptionOptions, - private listener: AlarmDataListener, + constructor(private listener: AlarmDataListener, private telemetryService: TelemetryService) { } diff --git a/ui-ngx/src/app/core/api/alarm-data.service.ts b/ui-ngx/src/app/core/api/alarm-data.service.ts index 23488c99c5..3454036db7 100644 --- a/ui-ngx/src/app/core/api/alarm-data.service.ts +++ b/ui-ngx/src/app/core/api/alarm-data.service.ts @@ -32,6 +32,7 @@ export interface AlarmDataListener { alarmSource: Datasource; alarmsLoaded: (pageData: PageData, allowedEntities: number, totalEntities: number) => void; alarmsUpdated: (update: Array, pageData: PageData) => void; + alarmDataSubscriptionOptions?: AlarmDataSubscriptionOptions; subscription?: AlarmDataSubscription; } @@ -47,11 +48,11 @@ export class AlarmDataService { pageLink: AlarmDataPageLink, keyFilters: KeyFilter[]) { const alarmSource = listener.alarmSource; + listener.alarmDataSubscriptionOptions = this.createAlarmSubscriptionOptions(listener, pageLink, keyFilters); if (alarmSource.type === DatasourceType.entity && (!alarmSource.entityFilter || !pageLink)) { return; } - listener.subscription = this.createSubscription(listener, - pageLink, alarmSource.keyFilters, keyFilters); + listener.subscription = new AlarmDataSubscription(listener, this.telemetryService); return listener.subscription.subscribe(); } @@ -61,10 +62,9 @@ export class AlarmDataService { } } - private createSubscription(listener: AlarmDataListener, - pageLink: AlarmDataPageLink, - keyFilters: KeyFilter[], - additionalKeyFilters: KeyFilter[]): AlarmDataSubscription { + private createAlarmSubscriptionOptions(listener: AlarmDataListener, + pageLink: AlarmDataPageLink, + additionalKeyFilters: KeyFilter[]): AlarmDataSubscriptionOptions { const alarmSource = listener.alarmSource; const alarmSubscriptionDataKeys: Array = []; alarmSource.dataKeys.forEach((dataKey) => { @@ -82,11 +82,10 @@ export class AlarmDataService { if (alarmDataSubscriptionOptions.datasourceType === DatasourceType.entity) { alarmDataSubscriptionOptions.entityFilter = alarmSource.entityFilter; alarmDataSubscriptionOptions.pageLink = pageLink; - alarmDataSubscriptionOptions.keyFilters = keyFilters; + alarmDataSubscriptionOptions.keyFilters = alarmSource.keyFilters; alarmDataSubscriptionOptions.additionalKeyFilters = additionalKeyFilters; } - return new AlarmDataSubscription(alarmDataSubscriptionOptions, - listener, this.telemetryService); + return alarmDataSubscriptionOptions; } } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index f7017652d8..070538ddc5 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -960,8 +960,8 @@ export class WidgetSubscription implements IWidgetSubscription { private updateAlarmDataSubscription() { if (this.alarmDataListener) { - const pageLink = this.alarmDataListener.subscription.alarmDataSubscriptionOptions.pageLink; - const keyFilters = this.alarmDataListener.subscription.alarmDataSubscriptionOptions.additionalKeyFilters; + const pageLink = this.alarmDataListener.alarmDataSubscriptionOptions.pageLink; + const keyFilters = this.alarmDataListener.alarmDataSubscriptionOptions.additionalKeyFilters; this.subscribeForAlarms(pageLink, keyFilters); } } From d40f5823ff1c7d996f7deea208826f17b4284346 Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Mon, 9 Nov 2020 15:18:37 +0200 Subject: [PATCH 08/12] Add getEntityTimeseries method to Attributes Service --- ui-ngx/src/app/core/http/attribute.service.ts | 30 ++++++++++++++++++- .../models/telemetry/telemetry.models.ts | 14 +++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/http/attribute.service.ts b/ui-ngx/src/app/core/http/attribute.service.ts index 044b9314ba..775360be5c 100644 --- a/ui-ngx/src/app/core/http/attribute.service.ts +++ b/ui-ngx/src/app/core/http/attribute.service.ts @@ -19,8 +19,9 @@ import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { forkJoin, Observable, of } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { EntityId } from '@shared/models/id/entity-id'; -import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; +import { AttributeData, AttributeScope, DataSortOrder, TimeseriesData } from '@shared/models/telemetry/telemetry.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { AggregationType } from '@shared/models/time/time.models'; @Injectable({ providedIn: 'root' @@ -110,4 +111,31 @@ export class AttributeService { } return forkJoin([saveEntityTimeseriesObservable, deleteEntityTimeseriesObservable]); } + + public getEntityTimeseries(entityId: EntityId, keys: Array, startTs: number, endTs: number, + limit: number = 100, agg: AggregationType = AggregationType.NONE, interval?: number, + orderBy: DataSortOrder = DataOrder.DESC, useStrictDataTypes: boolean = false, + config?: RequestConfig): Observable { + let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`; + url += `?keys=${keys.join(',')}`; + url += `&startTs=${startTs}`; + url += `&endTs=${endTs}`; + if (isDefinedAndNotNull(limit)) { + url += `&limit=${limit}`; + } + if (isDefinedAndNotNull(agg)) { + url += `&agg=${agg}`; + } + if (isDefinedAndNotNull(interval)) { + url += `&interval=${interval}`; + } + if (isDefinedAndNotNull(orderBy)) { + url += `&orderBy=${orderBy}`; + } + if (isDefinedAndNotNull(useStrictDataTypes)) { + url += `&useStrictDataTypes=${useStrictDataTypes}`; + } + + return this.http.get(url, defaultHttpOptionsFromConfig(config)); + } } diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index 18858bdbf7..28a887fe9b 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -81,6 +81,20 @@ export interface AttributeData { value: any; } +export interface TimeseriesData { + [key: string]: Array; +} + +export interface TimeseriesKeyData { + value: any; + ts: number; +} + +export enum DataSortOrder { + ASC = 'ASC', + DESC = 'DESC' +} + export interface WebsocketCmd { cmdId: number; } From 9f5d3a6c9533b146ca6fe3f0a455f531ff671a5e Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Mon, 9 Nov 2020 15:32:19 +0200 Subject: [PATCH 09/12] Type correction --- ui-ngx/src/app/core/http/attribute.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/http/attribute.service.ts b/ui-ngx/src/app/core/http/attribute.service.ts index 775360be5c..1fce00290d 100644 --- a/ui-ngx/src/app/core/http/attribute.service.ts +++ b/ui-ngx/src/app/core/http/attribute.service.ts @@ -114,7 +114,7 @@ export class AttributeService { public getEntityTimeseries(entityId: EntityId, keys: Array, startTs: number, endTs: number, limit: number = 100, agg: AggregationType = AggregationType.NONE, interval?: number, - orderBy: DataSortOrder = DataOrder.DESC, useStrictDataTypes: boolean = false, + orderBy: DataSortOrder = DataSortOrder.DESC, useStrictDataTypes: boolean = false, config?: RequestConfig): Observable { let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`; url += `?keys=${keys.join(',')}`; From ae8686605f4dfe6d8a34dd877be124dbb7213021 Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Mon, 9 Nov 2020 15:54:05 +0200 Subject: [PATCH 10/12] Refactoring --- ui-ngx/src/app/core/http/attribute.service.ts | 7 ++----- .../shared/models/telemetry/telemetry.models.ts | 16 +++++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ui-ngx/src/app/core/http/attribute.service.ts b/ui-ngx/src/app/core/http/attribute.service.ts index 1fce00290d..c9e4246d86 100644 --- a/ui-ngx/src/app/core/http/attribute.service.ts +++ b/ui-ngx/src/app/core/http/attribute.service.ts @@ -116,10 +116,7 @@ export class AttributeService { limit: number = 100, agg: AggregationType = AggregationType.NONE, interval?: number, orderBy: DataSortOrder = DataSortOrder.DESC, useStrictDataTypes: boolean = false, config?: RequestConfig): Observable { - let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries`; - url += `?keys=${keys.join(',')}`; - url += `&startTs=${startTs}`; - url += `&endTs=${endTs}`; + let url = `/api/plugins/telemetry/${entityId.entityType}/${entityId.id}/values/timeseries?keys=${keys.join(',')}&startTs=${startTs}&endTs=${endTs}`; if (isDefinedAndNotNull(limit)) { url += `&limit=${limit}`; } @@ -136,6 +133,6 @@ export class AttributeService { url += `&useStrictDataTypes=${useStrictDataTypes}`; } - return this.http.get(url, defaultHttpOptionsFromConfig(config)); + return this.http.get(url, defaultHttpOptionsFromConfig(config)); } } diff --git a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts index 28a887fe9b..aa4fb51fd1 100644 --- a/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts +++ b/ui-ngx/src/app/shared/models/telemetry/telemetry.models.ts @@ -21,7 +21,14 @@ import { Observable, ReplaySubject, Subject } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; import { map } from 'rxjs/operators'; import { NgZone } from '@angular/core'; -import { AlarmData, AlarmDataQuery, EntityData, EntityDataQuery, EntityKey } from '@shared/models/query/query.models'; +import { + AlarmData, + AlarmDataQuery, + EntityData, + EntityDataQuery, + EntityKey, + TsValue +} from '@shared/models/query/query.models'; import { PageData } from '@shared/models/page/page-data'; export enum DataKeyType { @@ -82,12 +89,7 @@ export interface AttributeData { } export interface TimeseriesData { - [key: string]: Array; -} - -export interface TimeseriesKeyData { - value: any; - ts: number; + [key: string]: Array; } export enum DataSortOrder { From 6d90dc8420b20de82a47cbe77a7ecddf8e11cd0c Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 9 Nov 2020 18:09:24 +0200 Subject: [PATCH 11/12] AccessValidator improvements --- .../server/service/security/AccessValidator.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index b4bc79e06e..8c7c629414 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -53,6 +53,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.user.UserService; @@ -152,7 +153,11 @@ public class AccessValidator { new FutureCallback>() { @Override public void onSuccess(@Nullable DeferredResult result) { - onSuccess.accept(response, currentUser.getTenantId(), entityId); + try { + onSuccess.accept(response, currentUser.getTenantId(), entityId); + } catch (Exception e) { + onFailure(e); + } } @Override @@ -404,9 +409,9 @@ public class AccessValidator { public static void handleError(Throwable e, final DeferredResult response, HttpStatus defaultErrorStatus) { ResponseEntity responseEntity; - if (e != null && e instanceof ToErrorResponseEntity) { + if (e instanceof ToErrorResponseEntity) { responseEntity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); - } else if (e != null && e instanceof IllegalArgumentException) { + } else if (e instanceof IllegalArgumentException || e instanceof IncorrectParameterException) { responseEntity = new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); } else { responseEntity = new ResponseEntity<>(defaultErrorStatus); From b1c4332a0376e1da9d02ef2223b77dac64375c58 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 10 Nov 2020 17:08:16 +0200 Subject: [PATCH 12/12] UI: Improvement logo container size --- ui-ngx/src/app/modules/home/home.component.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui-ngx/src/app/modules/home/home.component.scss b/ui-ngx/src/app/modules/home/home.component.scss index f77e85fd87..6cb0adea27 100644 --- a/ui-ngx/src/app/modules/home/home.component.scss +++ b/ui-ngx/src/app/modules/home/home.component.scss @@ -37,6 +37,7 @@ .tb-nav-header-toolbar { min-height: 64px; height: inherit; + padding: 0; z-index: 2; flex-shrink: 0; white-space: nowrap; @@ -45,7 +46,9 @@ height: 64px; .tb-logo-title { width: auto; + max-width: 100%; height: 36px; + max-height: 100%; margin: auto; } }