diff --git a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json index 1f7278892b..b1abb45b62 100644 --- a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json @@ -12,11 +12,10 @@ "gateway_widgets.attributes_card", "gateway_widgets.gateway_configuration2", "gateway_widgets.config_form_latest", - "gateway_widgets.gateway_events", "gateway_widgets.gateway_connector", "gateway_widgets.gateway_logs", "gateway_widgets.gateway_statistics", "gateway_widgets.gateway_general_statistics", "gateway_widgets.service_rpc" ] -} \ No newline at end of file +} diff --git a/ui-ngx/src/app/core/http/device.service.ts b/ui-ngx/src/app/core/http/device.service.ts index 137421e93d..801bdeb8c3 100644 --- a/ui-ngx/src/app/core/http/device.service.ts +++ b/ui-ngx/src/app/core/http/device.service.ts @@ -27,7 +27,8 @@ import { DeviceCredentials, DeviceInfo, DeviceInfoQuery, - DeviceSearchQuery, PublishLaunchCommand, + DeviceSearchQuery, + PublishLaunchCommand, PublishTelemetryCommand } from '@app/shared/models/device.models'; import { EntitySubtype } from '@app/shared/models/entity-type.models'; diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 200f8963c1..b67609c47e 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -812,3 +812,8 @@ export const getOS = (): string => { return os; }; + + +export const camelCase = (str: string): string => { + return _.camelCase(str); +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html index 63f121c22d..f1a0430fcf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
{{ 'gateway.docker-label' | translate }}
@@ -29,14 +29,9 @@ Windows -
- -
+ +
@@ -45,14 +40,9 @@ Linux -
- -
+ +
@@ -61,15 +51,22 @@ MacOS -
- -
+ +
+ + +
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts index 0a20292058..d5516dd59b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts @@ -15,15 +15,10 @@ /// import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { TranslateService } from '@ngx-translate/core'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; import { DeviceService } from '@core/http/device.service'; import { helpBaseUrl } from '@shared/models/constants'; import { getOS } from '@core/utils'; - +import { PublishLaunchCommand } from '@shared/models/device.models'; @Component({ selector: 'tb-gateway-command', @@ -39,17 +34,13 @@ export class DeviceGatewayCommandComponent implements OnInit { @Input() deviceId: string; - linuxCode: string; - windowsCode: string; + commands: PublishLaunchCommand; helpLink: string = helpBaseUrl + '/docs/iot-gateway/install/docker-installation/'; tabIndex = 0; - constructor(protected router: Router, - protected store: Store, - private translate: TranslateService, - private cd: ChangeDetectorRef, + constructor(private cd: ChangeDetectorRef, private deviceService: DeviceService) { } @@ -57,7 +48,7 @@ export class DeviceGatewayCommandComponent implements OnInit { ngOnInit(): void { if (this.deviceId) { this.deviceService.getDevicePublishLaunchCommands(this.deviceId).subscribe(commands => { - this.createRunCode(commands.mqtt); + this.commands = commands; this.cd.detectChanges(); }); } @@ -78,21 +69,4 @@ export class DeviceGatewayCommandComponent implements OnInit { this.tabIndex = 1; } } - - createRunCode(commands) { - this.linuxCode = commands.linux; - this.windowsCode = commands.windows; - } - - onDockerCodeCopied() { - this.store.dispatch(new ActionNotificationShow( - { - message: this.translate.instant('gateway.command-copied-message'), - type: 'success', - target: 'dockerCommandDialogContent', - duration: 1200, - verticalPosition: 'bottom', - horizontalPosition: 'left' - })); - } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html index cfa03bd97a..6f8c675e6f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html @@ -15,819 +15,715 @@ limitations under the License. --> -
- -

gateway.gateway-configuration

- - -
- - -
-
-
- - {{ 'gateway.remote-configuration' | translate }} - info - - - - {{ 'gateway.remote-shell' | translate }} - info - - - - gateway.thingsboard-host - - info_outlined - - - {{ 'gateway.thingsboard-host-required' | translate }} - - - - gateway.thingsboard-port - - - {{'gateway.thingsboard-port-required' | translate }} - - - {{'gateway.thingsboard-port-min' | translate }} - - - {{'gateway.thingsboard-port-max' | translate }} - - - {{'gateway.thingsboard-port-pattern' | translate }} - - info_outlined - - - -
-
-
-
security.security
-
- - {{ securityType.value | translate }} - -
- - security.access-token - - - {{'security.access-token-required' | translate}} - - info_outlined - - - - security.clientId - - - {{'security.clientId-required' | translate}} - - info_outlined - - - - security.username - - - {{'security.username-required' | translate}} - - info_outlined - - - - security.password - - info_outlined - - - - - - - - - - -
-
-
+ +

gateway.gateway-configuration

+ + +
+ + +
+
+
+ + {{ 'gateway.remote-configuration' | translate }} + +
+
+ + {{ 'gateway.remote-shell' | translate }} + +
+
+ + gateway.thingsboard-host + + info_outlined + + + {{ 'gateway.thingsboard-host-required' | translate }} + + + + gateway.thingsboard-port + + + {{ 'gateway.thingsboard-port-required' | translate }} + + + {{ 'gateway.thingsboard-port-min' | translate }} + + + {{ 'gateway.thingsboard-port-max' | translate }} + + + {{ 'gateway.thingsboard-port-pattern' | translate }} + + info_outlined + + +
- - -
-
-
- - gateway.logs.date-format - - - {{'gateway.logs.date-format-required' | translate }} - - info_outlined - - - - gateway.logs.log-format - - - {{'gateway.logs.log-format-required' | translate }} - - info_outlined - - -
-
-
-
gateway.logs.remote
- - {{ 'gateway.logs.remote-logs' | translate }} - info +
+
security.security
+ + + {{ securityType.value | translate }} + + + security.access-token + + + {{ 'security.access-token-required' | translate }} + + info_outlined + + +
+ + security.clientId + + + {{ 'security.clientId-required' | translate }} + + info_outlined + + + + security.username + + + {{ 'security.username-required' | translate }} + + info_outlined - - - gateway.logs.level - - {{logLevel}} -
-
-
gateway.logs.local
-
- - {{ logConfig.value }} - -
- - gateway.logs.level - - {{logLevel}} - - - - gateway.logs.file-path - - - {{'gateway.logs.file-path-required' | translate }} - - -
- - gateway.logs.saving-period - - - {{'gateway.logs.saving-period-required' | translate }} - - - {{'gateway.logs.saving-period-min' | translate }} - - - - - - - {{period.value | translate}} - - - -
- - gateway.logs.backup-count - - - {{'gateway.logs.backup-count-required' | translate }} - - - {{'gateway.logs.backup-count-min' | translate }} - - info_outlined - - -
-
-
+ + security.password + + info_outlined + + + + + +
- - -
-
-
gateway.storage - {{'gateway.hints.storage' | translate}} -
-
- - - {{ storageType.value | translate }} - - -
- - {{'gateway.hints.' + gatewayConfigGroup.get('storage.type').value | translate}} -
- - gateway.storage-read-record-count - - - {{'gateway.storage-read-record-count-required' | translate}} - - - {{'gateway.storage-read-record-count-min' | translate}} - - - {{'gateway.storage-read-record-count-pattern' | translate}} - - info_outlined - - - - gateway.storage-max-records - - - {{'gateway.storage-max-records-required' | translate}} - - - {{'gateway.storage-max-records-min' | translate}} - - - {{'gateway.storage-max-records-pattern' | translate}} - - info_outlined - - - - gateway.storage-data-folder-path - - - {{'gateway.storage-data-folder-path-required' | translate}} - - info_outlined - - - - gateway.storage-max-files - - - {{'gateway.storage-max-files-required' | translate}} - - - {{'gateway.storage-max-files-min' | translate}} - - - {{'gateway.storage-max-files-pattern' | translate}} - - info_outlined - - - - gateway.storage-max-read-record-count - - - {{'gateway.storage-max-read-record-count-required' | translate}} - - - {{'gateway.storage-max-read-record-count-min' | translate}} - - - {{'gateway.storage-max-read-record-count-pattern' | translate}} - - info_outlined - - - - gateway.storage-max-file-records - - - {{'gateway.storage-max-records-required' | translate}} - - - {{'gateway.storage-max-records-min' | translate}} - - - {{'gateway.storage-max-records-pattern' | translate}} - - info_outlined - - - - gateway.storage-path - - - {{'gateway.storage-path-required' | translate}} - - info_outlined - - - - gateway.messages-ttl-check-in-hours - - - {{'gateway.messages-ttl-check-in-hours-required' | translate}} - - - {{'gateway.messages-ttl-check-in-hours-min' | translate}} - - - {{'gateway.messages-ttl-check-in-hours-pattern' | translate}} - - info_outlined - - - - gateway.messages-ttl-in-days - - - {{'gateway.messages-ttl-in-days-required' | translate}} - - - {{'gateway.messages-ttl-in-days-min' | translate}} - - - {{'gateway.messages-ttl-in-days-pattern' | translate}} - - info_outlined - - -
-
-
+
+
+ +
+
+
+ + gateway.logs.date-format + + + {{ 'gateway.logs.date-format-required' | translate }} + + info_outlined + + + + gateway.logs.log-format + + + {{ 'gateway.logs.log-format-required' | translate }} + + info_outlined + + +
- - -
-
-
- - {{ 'gateway.grpc' | translate }} - - - {{ 'gateway.permit-without-calls' | translate }} - info - - - - gateway.server-port - - info_outlined - - - {{'gateway.thingsboard-port-required' | translate }} - - - {{'gateway.thingsboard-port-min' | translate }} - - - {{'gateway.thingsboard-port-max' | translate }} - - - {{'gateway.thingsboard-port-pattern' | translate }} - - - - gateway.grpc-keep-alive-timeout - - info_outlined - - - {{'gateway.grpc-keep-alive-timeout-required' | translate }} - - - {{'gateway.grpc-keep-alive-timeout-min' | translate }} - - - {{'gateway.grpc-keep-alive-timeout-pattern' | translate }} - - - - gateway.grpc-keep-alive - - info_outlined - - - {{'gateway.grpc-keep-alive-required' | translate }} - - - {{'gateway.grpc-keep-alive-min' | translate }} - - - {{'gateway.grpc-keep-alive-pattern' | translate }} - - - - gateway.grpc-min-time-between-pings - - info_outlined - - - {{'gateway.grpc-min-time-between-pings-required' | translate }} - - - {{'gateway.grpc-min-time-between-pings-min' | translate }} - - - {{'gateway.grpc-min-time-between-pings-pattern' | translate }} - - - - gateway.grpc-max-pings-without-data - - info_outlined - - - {{'gateway.grpc-max-pings-without-data-required' | translate }} - - - {{'gateway.grpc-max-pings-without-data-min' | translate }} - - - {{'gateway.grpc-max-pings-without-data-pattern' | translate }} - - - - gateway.grpc-min-ping-interval-without-data - - info_outlined - - - {{'gateway.grpc-min-ping-interval-without-data-required' | translate }} - - - {{'gateway.grpc-min-ping-interval-without-data-min' | translate }} - - - {{'gateway.grpc-min-ping-interval-without-data-pattern' | translate }} - - -
-
+
+
gateway.logs.remote
+
+ + {{ 'gateway.logs.remote-logs' | translate }} + +
+ + gateway.logs.level + + {{ logLevel }} + +
- - -
-
-
- - {{ 'gateway.statistics.statistics' | translate }} - - - gateway.statistics.send-period - - - {{'gateway.statistics.send-period-required' | translate }} - +
+
gateway.logs.local
+ + {{ localLogsConfigTranslateMap.get(logConfig) }} + + +
+ + gateway.logs.level + + {{ logLevel }} + + + + gateway.logs.file-path + + + {{ 'gateway.logs.file-path-required' | translate }} + + +
+
+
+ + gateway.logs.saving-period + - {{'gateway.statistics.send-period-min' | translate }} + *ngIf="gatewayConfigGroup.get('logs.local.' + logSelector.value + '.savingTime').hasError('required')"> + {{ 'gateway.logs.saving-period-required' | translate }} - {{'gateway.statistics.send-period-pattern' | translate }} + *ngIf="gatewayConfigGroup.get('logs.local.' + logSelector.value + '.savingTime').hasError('min')"> + {{ 'gateway.logs.saving-period-min' | translate }} + + + + {{ period.value | translate }} + + +
+ + gateway.logs.backup-count + + + {{ 'gateway.logs.backup-count-required' | translate }} + + + {{ 'gateway.logs.backup-count-min' | translate }} + + info_outlined + +
-
-
{{"gateway.statistics.commands" |translate}} - - {{'gateway.hints.commands' | translate}}
-
-
- - - gateway.statistics.attribute-name - - - {{'gateway.statistics.attribute-name-required' | translate }} - - info_outlined - - - - gateway.statistics.timeout - - - {{'gateway.statistics.timeout-required' | translate }} - - - {{'gateway.statistics.timeout-min' | translate }} - - - {{'gateway.statistics.timeout-pattern' | translate }} - - info_outlined - - - - gateway.statistics.command - - - {{'gateway.statistics.command-required' | translate }} - - info_outlined - - - - -
- -
-
+
- - -
-
-
- - {{ 'gateway.checking-device-activity' | translate }} - - {{'gateway.hints.check-device-activity' | translate}} -
-
- - gateway.inactivity-timeout-seconds - - - {{'gateway.inactivity-timeout-seconds-required' | translate }} - - - {{'gateway.inactivity-timeout-seconds-min' | translate }} +
+ + +
+
+
gateway.storage
+
gateway.hints.storage
+ + + {{ storageTypesTranslationMap.get(storageType) | translate }} + + +
{{ 'gateway.hints.' + gatewayConfigGroup.get('storage.type').value | translate }}
+ +
+ + gateway.storage-read-record-count + + + {{ 'gateway.storage-read-record-count-required' | translate }} + + + {{ 'gateway.storage-read-record-count-min' | translate }} + + + {{ 'gateway.storage-read-record-count-pattern' | translate }} + + info_outlined + + + + gateway.storage-max-records + + + {{ 'gateway.storage-max-records-required' | translate }} + + + {{ 'gateway.storage-max-records-min' | translate }} + + + {{ 'gateway.storage-max-records-pattern' | translate }} + + info_outlined + + +
+
+
+ + gateway.storage-data-folder-path + + + {{ 'gateway.storage-data-folder-path-required' | translate }} info_outlined + matTooltip="{{ 'gateway.hints.data-folder' | translate }}">info_outlined - - gateway.inactivity-check-period-seconds - - - {{'gateway.inactivity-check-period-seconds-required' | translate }} + + gateway.storage-max-files + + + {{ 'gateway.storage-max-files-required' | translate }} - - {{'gateway.inactivity-check-period-seconds-min' | translate }} + + {{ 'gateway.storage-max-files-min' | translate }} - info_outlined + + {{ 'gateway.storage-max-files-pattern' | translate }} + + info_outlined
-
-
-
gateway.advanced
-
- - gateway.min-pack-send-delay - - - {{ 'gateway.min-pack-send-delay-required' | translate }} +
+ + gateway.storage-max-read-record-count + + + {{ 'gateway.storage-max-read-record-count-required' | translate }} - - {{ 'gateway.min-pack-send-delay-min' | translate }} + + {{ 'gateway.storage-max-read-record-count-min' | translate }} - info_outlined + + {{ 'gateway.storage-max-read-record-count-pattern' | translate }} + + info_outlined - - gateway.mqtt-qos - - - {{ 'gateway.mqtt-qos-required' | translate}} + + gateway.storage-max-file-records + + + {{ 'gateway.storage-max-records-required' | translate }} - - {{ 'gateway.mqtt-qos-range' | translate}} + + {{ 'gateway.storage-max-records-min' | translate }} - - {{ 'gateway.mqtt-qos-range' | translate}} + + {{ 'gateway.storage-max-records-pattern' | translate }} - info_outlined + info_outlined - - gateway.statistics.check-connectors-configuration - - - {{'gateway.statistics.check-connectors-configuration-required' | translate }} +
+ +
+
+ + gateway.storage-path + + + {{ 'gateway.storage-path-required' | translate }} + + info_outlined + + + + gateway.messages-ttl-check-in-hours + + + {{ 'gateway.messages-ttl-check-in-hours-required' | translate }} - - {{'gateway.statistics.check-connectors-configuration-min' | translate }} + + {{ 'gateway.messages-ttl-check-in-hours-min' | translate }} - - {{'gateway.statistics.check-connectors-configuration-pattern' | translate }} + + {{ 'gateway.messages-ttl-check-in-hours-pattern' | translate }} + info_outlined +
+ + gateway.messages-ttl-in-days + + + {{ 'gateway.messages-ttl-in-days-required' | translate }} + + + {{ 'gateway.messages-ttl-in-days-min' | translate }} + + + {{ 'gateway.messages-ttl-in-days-pattern' | translate }} + + info_outlined + + +
+ +
+
+ + +
+
+ + {{ 'gateway.grpc' | translate }} + +
+ + {{ 'gateway.permit-without-calls' | translate }} + +
+
+
+ + gateway.server-port + + info_outlined + + + {{ 'gateway.thingsboard-port-required' | translate }} + + + {{ 'gateway.thingsboard-port-min' | translate }} + + + {{ 'gateway.thingsboard-port-max' | translate }} + + + {{ 'gateway.thingsboard-port-pattern' | translate }} + + + + gateway.grpc-keep-alive-timeout + + info_outlined + + + {{ 'gateway.grpc-keep-alive-timeout-required' | translate }} + + + {{ 'gateway.grpc-keep-alive-timeout-min' | translate }} + + + {{ 'gateway.grpc-keep-alive-timeout-pattern' | translate }} + + +
+
+ + gateway.grpc-keep-alive + + info_outlined + + + {{ 'gateway.grpc-keep-alive-required' | translate }} + + + {{ 'gateway.grpc-keep-alive-min' | translate }} + + + {{ 'gateway.grpc-keep-alive-pattern' | translate }} + + + + gateway.grpc-min-time-between-pings + + info_outlined + + + {{ 'gateway.grpc-min-time-between-pings-required' | translate }} + + + {{ 'gateway.grpc-min-time-between-pings-min' | translate }} + + + {{ 'gateway.grpc-min-time-between-pings-pattern' | translate }} + + +
+
+ + gateway.grpc-max-pings-without-data + + info_outlined + + + {{ 'gateway.grpc-max-pings-without-data-required' | translate }} + + + {{ 'gateway.grpc-max-pings-without-data-min' | translate }} + + + {{ 'gateway.grpc-max-pings-without-data-pattern' | translate }} + + + + gateway.grpc-min-ping-interval-without-data + + info_outlined + + + {{ 'gateway.grpc-min-ping-interval-without-data-required' | translate }} + + + {{ 'gateway.grpc-min-ping-interval-without-data-min' | translate }} + + + {{ 'gateway.grpc-min-ping-interval-without-data-pattern' | translate }} + + +
+
+
+
+
+ +
+
+ + {{ 'gateway.statistics.statistics' | translate }} + + + gateway.statistics.send-period + + + {{ 'gateway.statistics.send-period-required' | translate }} + + + {{ 'gateway.statistics.send-period-min' | translate }} + + + {{ 'gateway.statistics.send-period-pattern' | translate }} + + +
+
+
gateway.statistics.commands
+
gateway.hints.commands
+ +
+
+
+ + gateway.statistics.attribute-name + + + {{ 'gateway.statistics.attribute-name-required' | translate }} + + info_outlined + + + + gateway.statistics.timeout + + + {{ 'gateway.statistics.timeout-required' | translate }} + + + {{ 'gateway.statistics.timeout-min' | translate }} + + + {{ 'gateway.statistics.timeout-pattern' | translate }} + + info_outlined + + +
+ + gateway.statistics.command + + + {{ 'gateway.statistics.command-required' | translate }} + + info_outlined + + +
+
- + +
- - -
- - - -
- +
+
+ +
+
+
+ + {{ 'gateway.checking-device-activity' | translate }} + +
+
+ + gateway.inactivity-timeout-seconds + + + {{ 'gateway.inactivity-timeout-seconds-required' | translate }} + + + {{ 'gateway.inactivity-timeout-seconds-min' | translate }} + + info_outlined + + + + gateway.inactivity-check-period-seconds + + + {{ 'gateway.inactivity-check-period-seconds-required' | translate }} + + + {{ 'gateway.inactivity-check-period-seconds-min' | translate }} + + info_outlined + + +
+
+
+
gateway.advanced
+
+ + gateway.min-pack-send-delay + + + {{ 'gateway.min-pack-send-delay-required' | translate }} + + + {{ 'gateway.min-pack-send-delay-min' | translate }} + + info_outlined + + + + gateway.mqtt-qos + + + {{ 'gateway.mqtt-qos-required' | translate }} + + + {{ 'gateway.mqtt-qos-range' | translate }} + + + {{ 'gateway.mqtt-qos-range' | translate }} + + info_outlined + + +
+ + gateway.statistics.check-connectors-configuration + + + {{ 'gateway.statistics.check-connectors-configuration-required' | translate }} + + + {{ 'gateway.statistics.check-connectors-configuration-min' | translate }} + + + {{ 'gateway.statistics.check-connectors-configuration-pattern' | translate }} + + +
+
+
+ +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss index 1bf599727d..803654734f 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss @@ -16,14 +16,13 @@ :host { width: 100%; height: 100%; - display: block; + display: flex; + flex-direction: column; - .mat-icon { - color: rgba(0, 0, 0, .12); - } - - .tb-form-panel { - margin-bottom: 20px; + .configuration-block { + display: flex; + flex-direction: column; + gap: 16px; } .mat-toolbar { @@ -31,113 +30,37 @@ color: rgba(0, 0, 0, .87) !important; } - .mat-content { - .expansion-panel-header { - font-weight: 600; - color: rgba(0, 0, 0, .87) !important; - } - - mat-slide-toggle { - margin-bottom: 16px; - } - - mat-form-field { - margin-right: 15px; - } - - .slider-icon { - position: absolute; - transform: translateY(-3px); - } - - .block-title { - font-size: 20px; - font-weight: 400; - padding-top: 16px; - } - - .hover-cursor { - cursor: pointer; - } - } - - .security-toggle-group { - margin-bottom: 15px; + .toggle-group { margin-right: auto; } - .logs-label { - font-weight: 500; - margin-bottom: 10px; - } - - .statistics-block { - margin-bottom: 15px; - padding-left: 15px; - padding-top: 15px; - } - .first-capital { text-transform: capitalize; } - mat-panel-title { - display: block; - padding-top: 20px; - } - - mat-panel-title span { - display: block; - padding-left: 0; - padding-top: 5px; - } - - .tb-hint { - font-size: 13px; - color: rgba(0, 0, 0, .54); - width: fit-content; - cursor: pointer; - text-transform: none !important; - } - - .line-break { - width: 100%; - } - textarea { resize: none; } -} - -:host ::ng-deep { - .mat-tab-label-active { - color: white; - opacity: 1; - } - - .mat-tab-label, .mat-tab-label-active{ - min-width: 50px !important; - padding: 3px !important; - margin: 3px !important; - flex-grow: 1; - } - .mat-ink-bar { - height: 100%; - z-index: -10; - border-radius: 5px; + .saving-period { + flex: 1; } +} +:host ::ng-deep { .pointer-event { pointer-events: all; } - .mat-mdc-form-field-icon-suffix { - z-index: 100; + .toggle-group span { + padding: 0 25px; } - .security-toggle-group span { - padding: 0 25px; + .mat-mdc-form-field-icon-suffix { + color: #E0E0E0; + &:hover { + color: #9E9E9E; + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts index a57bd6750e..bef0c2cc74 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts @@ -15,15 +15,11 @@ /// import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; import { FormArray, FormBuilder, FormControl, FormGroup, - UntypedFormGroup, ValidationErrors, ValidatorFn, Validators @@ -41,79 +37,17 @@ import { Observable, of } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; import { NULL_UUID } from '@shared/models/id/has-uuid'; - -export enum StorageTypes { - MEMORY = 'memory', - FILE = 'file', - SQLITE = 'sqlite' -} - -export enum GatewayLogLevel { - none = 'NONE', - critical = 'CRITICAL', - error = 'ERROR', - warning = 'WARNING', - info = 'INFO', - debug = 'DEBUG' -} - -export enum LogSavingPeriod { - days = 'D', - hours = 'H', - minutes = 'M', - seconds = 'S' -} - -export enum LocalLogsConfigs { - service = 'service', - connector = 'connector', - converter = 'converter', - tb_connection = 'tb_connection', - storage = 'storage', - extension = 'extension' -} - -export const localLogsConfigLabels = new Map([ - [LocalLogsConfigs.service, 'Service'], - [LocalLogsConfigs.connector, 'Connector'], - [LocalLogsConfigs.converter, 'Converter'], - [LocalLogsConfigs.tb_connection, 'TB Connection'], - [LocalLogsConfigs.storage, 'Storage'], - [LocalLogsConfigs.extension, 'Extension'] -]); - -export const logSavingPeriodTranslations = new Map( - [ - [LogSavingPeriod.days, 'gateway.logs.days'], - [LogSavingPeriod.hours, 'gateway.logs.hours'], - [LogSavingPeriod.minutes, 'gateway.logs.minutes'], - [LogSavingPeriod.seconds, 'gateway.logs.seconds'] - ] -); - -export const storageTypesTranslations = new Map( - [ - [StorageTypes.MEMORY, 'gateway.storage-types.memory-storage'], - [StorageTypes.FILE, 'gateway.storage-types.file-storage'], - [StorageTypes.SQLITE, 'gateway.storage-types.sqlite'] - ] -); - -export enum SecurityTypes { - ACCESS_TOKEN = 'accessToken', - USERNAME_PASSWORD = 'usernamePassword', - TLS_ACCESS_TOKEN = 'tlsAccessToken', - TLS_PRIVATE_KEY = 'tlsPrivateKey' -} - -export const securityTypesTranslationsMap = new Map( - [ - [SecurityTypes.ACCESS_TOKEN, 'gateway.security-types.access-token'], - [SecurityTypes.USERNAME_PASSWORD, 'gateway.security-types.username-password'], - [SecurityTypes.TLS_ACCESS_TOKEN, 'gateway.security-types.tls-access-token'], - // [SecurityTypes.TLS_PRIVATE_KEY, 'gateway.security-types.tls-private-key'], - ] -); +import { + GatewayLogLevel, + GecurityTypesTranslationsMap, + LocalLogsConfigTranslateMap, + LocalLogsConfigs, + LogSavingPeriod, + LogSavingPeriodTranslations, + SecurityTypes, + StorageTypes, + StorageTypesTranslationMap +} from './gateway-widget.models'; @Component({ selector: 'tb-gateway-configuration', @@ -124,13 +58,16 @@ export class GatewayConfigurationComponent implements OnInit { gatewayConfigGroup: FormGroup; - storageTypes = storageTypesTranslations; + StorageTypes = StorageTypes; + storageTypes = Object.values(StorageTypes) as StorageTypes[]; + storageTypesTranslationMap = StorageTypesTranslationMap; - logSavingPeriods = logSavingPeriodTranslations; + logSavingPeriods = LogSavingPeriodTranslations; - localLogsConfigLabels = localLogsConfigLabels; + localLogsConfigs = Object.keys(LocalLogsConfigs) as LocalLogsConfigs[]; + localLogsConfigTranslateMap = LocalLogsConfigTranslateMap; - securityTypes = securityTypesTranslationsMap; + securityTypes = GecurityTypesTranslationsMap; gatewayLogLevel = Object.values(GatewayLogLevel); @@ -142,18 +79,13 @@ export class GatewayConfigurationComponent implements OnInit { logSelector: FormControl; - securityType: SecurityTypes; + private initialCredentials: DeviceCredentials; - initialCredentials: DeviceCredentials; - - - constructor(protected router: Router, - protected store: Store, - protected fb: FormBuilder, - protected attributeService: AttributeService, - protected deviceService: DeviceService, + constructor(private fb: FormBuilder, + private attributeService: AttributeService, + private deviceService: DeviceService, private cd: ChangeDetectorRef, - public dialog: MatDialog) { + private dialog: MatDialog) { } ngOnInit() { @@ -221,7 +153,7 @@ export class GatewayConfigurationComponent implements OnInit { type: ['remote', [Validators.required]], remote: this.fb.group({ enabled: [false], - logLevel: [GatewayLogLevel.info, [Validators.required]], + logLevel: [GatewayLogLevel.INFO, [Validators.required]], }), local: this.fb.group({}) }) @@ -231,7 +163,7 @@ export class GatewayConfigurationComponent implements OnInit { if (password && password !== '') { this.gatewayConfigGroup.get('thingsboard.security.username').setValidators([Validators.required]); } else { - this.gatewayConfigGroup.get('thingsboard.security.username').setValidators([]); + this.gatewayConfigGroup.get('thingsboard.security.username').clearValidators(); } this.gatewayConfigGroup.get('thingsboard.security.username').updateValueAndValidity({emitEvent: false}); }); @@ -258,10 +190,10 @@ export class GatewayConfigurationComponent implements OnInit { checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').setValidators([Validators.min(1), Validators.required]); } else { checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').clearValidators(); - checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').setErrors(null); checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').clearValidators(); - checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').setErrors(null); } + checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').updateValueAndValidity({emitEvent: false}); + checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').updateValueAndValidity({emitEvent: false}); }); this.gatewayConfigGroup.get('grpc.enabled').valueChanges.subscribe(value => { @@ -287,16 +219,14 @@ export class GatewayConfigurationComponent implements OnInit { securityGroup.get('caCert').addValidators([Validators.required]); securityGroup.get('caCert').updateValueAndValidity(); } else if (type === SecurityTypes.USERNAME_PASSWORD) { - securityGroup.addValidators([this.atLeastOneRequired(Validators.required, ['clientId', 'username'])]) - // securityGroup.get('password').addValidators([Validators.required]); - // securityGroup.get('password').updateValueAndValidity(); + securityGroup.addValidators([this.atLeastOneRequired(Validators.required, ['clientId', 'username'])]); } securityGroup.updateValueAndValidity(); }); - securityGroup.get('caCert').valueChanges.subscribe(_ => this.cd.detectChanges()); - securityGroup.get('privateKey').valueChanges.subscribe(_ => this.cd.detectChanges()); - securityGroup.get('cert').valueChanges.subscribe(_ => this.cd.detectChanges()); + securityGroup.get('caCert').valueChanges.subscribe(() => this.cd.detectChanges()); + securityGroup.get('privateKey').valueChanges.subscribe(() => this.cd.detectChanges()); + securityGroup.get('cert').valueChanges.subscribe(() => this.cd.detectChanges()); const storageGroup = this.gatewayConfigGroup.get('storage') as FormGroup; storageGroup.get('type').valueChanges.subscribe(type => { @@ -306,6 +236,8 @@ export class GatewayConfigurationComponent implements OnInit { [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]); storageGroup.get('max_records_count').addValidators( [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('read_records_count').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_records_count').updateValueAndValidity({emitEvent: false}); } else if (type === StorageTypes.FILE) { storageGroup.get('data_folder_path').addValidators([Validators.required]); storageGroup.get('max_file_count').addValidators( @@ -314,20 +246,27 @@ export class GatewayConfigurationComponent implements OnInit { [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); storageGroup.get('max_records_per_file').addValidators( [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('data_folder_path').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_file_count').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_read_records_count').updateValueAndValidity({emitEvent: false}); + storageGroup.get('max_records_per_file').updateValueAndValidity({emitEvent: false}); } else if (type === StorageTypes.SQLITE) { storageGroup.get('data_file_path').addValidators([Validators.required]); storageGroup.get('messages_ttl_check_in_hours').addValidators( [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); storageGroup.get('messages_ttl_in_days').addValidators( [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); + storageGroup.get('data_file_path').updateValueAndValidity({emitEvent: false}); + storageGroup.get('messages_ttl_check_in_hours').updateValueAndValidity({emitEvent: false}); + storageGroup.get('messages_ttl_in_days').updateValueAndValidity({emitEvent: false}); } }); this.fetchConfigAttribute(this.device); } - atLeastOneRequired(validator: ValidatorFn, controls: string[] = null) { - return (group: UntypedFormGroup): ValidationErrors | null => { + private atLeastOneRequired(validator: ValidatorFn, controls: string[] = null) { + return (group: FormGroup): ValidationErrors | null => { if (!controls) { controls = Object.keys(group.controls); } @@ -337,22 +276,10 @@ export class GatewayConfigurationComponent implements OnInit { }; } - updateSecurityValidators(value: SecurityTypes) { - this.gatewayConfigGroup.get('thingsboard.security.type').setValue(value, {emitEvent: true}); - this.gatewayConfigGroup.get('thingsboard.security.type').markAsDirty(); - } - - updateLogType(value: LocalLogsConfigs) { - this.logSelector.setValue(value); - } - - updateStorageType(value: StorageTypes) { - this.gatewayConfigGroup.get('storage.type').setValue(value, {emitEvent: true}); - this.gatewayConfigGroup.get('storage.type').markAsDirty(); - } - - fetchConfigAttribute(entityId: EntityId) { - if (entityId.id === NULL_UUID) return; + private fetchConfigAttribute(entityId: EntityId) { + if (entityId.id === NULL_UUID) { + return; + } this.attributeService.getEntityAttributes(entityId, AttributeScope.CLIENT_SCOPE, ['general_configuration', 'grpc_configuration', 'logs_configuration', 'storage_configuration', 'RemoteLoggingLevel']).pipe( mergeMap(attributes => attributes.length ? of(attributes) : this.attributeService.getEntityAttributes( @@ -397,24 +324,26 @@ export class GatewayConfigurationComponent implements OnInit { if (remoteLoggingLevel) { const remoteLogsFormGroup = this.gatewayConfigGroup.get('logs.remote'); remoteLogsFormGroup.patchValue({ - enabled: remoteLoggingLevel !== GatewayLogLevel.none, + enabled: remoteLoggingLevel !== GatewayLogLevel.NONE, logLevel: remoteLoggingLevel }, {emitEvent: false}); remoteLogsFormGroup.markAsPristine(); } this.cd.detectChanges(); } else { - this.checkAndFetchCredentials({}); + this.checkAndFetchCredentials(); } }); } - checkAndFetchCredentials(security): void { + private checkAndFetchCredentials(security: any = {}): void { if (security.type !== SecurityTypes.TLS_PRIVATE_KEY) { this.deviceService.getDeviceCredentials(this.device.id).subscribe(credentials => { this.initialCredentials = credentials; if (credentials.credentialsType === DeviceCredentialsType.ACCESS_TOKEN || security.type === SecurityTypes.TLS_ACCESS_TOKEN) { - this.gatewayConfigGroup.get('thingsboard.security.type').setValue(security.type === SecurityTypes.TLS_ACCESS_TOKEN? SecurityTypes.TLS_ACCESS_TOKEN : SecurityTypes.ACCESS_TOKEN); + this.gatewayConfigGroup.get('thingsboard.security.type').setValue(security.type === SecurityTypes.TLS_ACCESS_TOKEN + ? SecurityTypes.TLS_ACCESS_TOKEN + : SecurityTypes.ACCESS_TOKEN); this.gatewayConfigGroup.get('thingsboard.security.accessToken').setValue(credentials.credentialsId); if(security.type === SecurityTypes.TLS_ACCESS_TOKEN) { this.gatewayConfigGroup.get('thingsboard.security.caCert').setValue(security.caCert); @@ -432,7 +361,7 @@ export class GatewayConfigurationComponent implements OnInit { } } - logsToObj(logsConfig) { + private logsToObj(logsConfig: any) { const logsObject = { local: {} }; @@ -453,43 +382,42 @@ export class GatewayConfigurationComponent implements OnInit { return {local: logsObject, logFormat, dateFormat}; } - toggleRpcFields(enable: boolean) { + private toggleRpcFields(enable: boolean) { const grpcGroup = this.gatewayConfigGroup.get('grpc') as FormGroup; if (enable) { - grpcGroup.get('serverPort').enable(); - grpcGroup.get('keepAliveTimeMs').enable(); - grpcGroup.get('keepAliveTimeoutMs').enable(); - grpcGroup.get('keepalivePermitWithoutCalls').enable(); - grpcGroup.get('maxPingsWithoutData').enable(); - grpcGroup.get('minTimeBetweenPingsMs').enable(); - grpcGroup.get('minPingIntervalWithoutDataMs').enable(); + grpcGroup.get('serverPort').enable({emitEvent: false}); + grpcGroup.get('keepAliveTimeMs').enable({emitEvent: false}); + grpcGroup.get('keepAliveTimeoutMs').enable({emitEvent: false}); + grpcGroup.get('keepalivePermitWithoutCalls').enable({emitEvent: false}); + grpcGroup.get('maxPingsWithoutData').enable({emitEvent: false}); + grpcGroup.get('minTimeBetweenPingsMs').enable({emitEvent: false}); + grpcGroup.get('minPingIntervalWithoutDataMs').enable({emitEvent: false}); } else { - grpcGroup.get('serverPort').disable(); - grpcGroup.get('keepAliveTimeMs').disable(); - grpcGroup.get('keepAliveTimeoutMs').disable(); - grpcGroup.get('keepalivePermitWithoutCalls').disable(); - grpcGroup.get('maxPingsWithoutData').disable(); - grpcGroup.get('minTimeBetweenPingsMs').disable(); - grpcGroup.get('minPingIntervalWithoutDataMs').disable(); + grpcGroup.get('serverPort').disable({emitEvent: false}); + grpcGroup.get('keepAliveTimeMs').disable({emitEvent: false}); + grpcGroup.get('keepAliveTimeoutMs').disable({emitEvent: false}); + grpcGroup.get('keepalivePermitWithoutCalls').disable({emitEvent: false}); + grpcGroup.get('maxPingsWithoutData').disable({emitEvent: false}); + grpcGroup.get('minTimeBetweenPingsMs').disable({emitEvent: false}); + grpcGroup.get('minPingIntervalWithoutDataMs').disable({emitEvent: false}); } } - addCommand(command?): void { - const data = command || {}; + addCommand(command: any = {}): void { const commandsFormArray = this.commandFormArray(); const commandFormGroup = this.fb.group({ - attributeOnGateway: [data.attributeOnGateway || null, [Validators.required]], - command: [data.command || null, [Validators.required]], - timeout: [data.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], + attributeOnGateway: [command.attributeOnGateway || null, [Validators.required]], + command: [command.command || null, [Validators.required]], + timeout: [command.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], }); commandsFormArray.push(commandFormGroup); } - addLocalLogConfig(name, config): void { + private addLocalLogConfig(name: string, config: any): void { const localLogsFormGroup = this.gatewayConfigGroup.get('logs.local') as FormGroup; const configGroup = this.fb.group({ - logLevel: [config.logLevel || GatewayLogLevel.info, [Validators.required]], + logLevel: [config.logLevel || GatewayLogLevel.INFO, [Validators.required]], filePath: [config.filePath || './logs', [Validators.required]], backupCount: [config.backupCount || 7, [Validators.required, Validators.min(0)]], savingTime: [config.savingTime || 3, [Validators.required, Validators.min(0)]], @@ -507,12 +435,14 @@ export class GatewayConfigurationComponent implements OnInit { } removeCommandControl(index: number, event: any): void { - if (event.pointerType === '') return; + if (event.pointerType === '') { + return; + } this.commandFormArray().removeAt(index); this.gatewayConfigGroup.markAsDirty(); } - removeAllSecurityValidators(): void { + private removeAllSecurityValidators(): void { const securityGroup = this.gatewayConfigGroup.get('thingsboard.security') as FormGroup; securityGroup.clearValidators(); for (const controlsKey in securityGroup.controls) { @@ -524,7 +454,7 @@ export class GatewayConfigurationComponent implements OnInit { } } - removeAllStorageValidators(): void { + private removeAllStorageValidators(): void { const storageGroup = this.gatewayConfigGroup.get('storage') as FormGroup; for (const storageKey in storageGroup.controls) { if (storageKey !== 'type') { @@ -535,7 +465,7 @@ export class GatewayConfigurationComponent implements OnInit { } } - removeEmpty(obj) { + private removeEmpty(obj: any) { return Object.fromEntries( Object.entries(obj) .filter(([_, v]) => v != null) @@ -543,7 +473,7 @@ export class GatewayConfigurationComponent implements OnInit { ); } - generateLogsFile(logsObj) { + private generateLogsFile(logsObj: any) { const logAttrObj = { version: 1, disable_existing_loggers: false, @@ -591,7 +521,7 @@ export class GatewayConfigurationComponent implements OnInit { return logAttrObj; } - createHandlerObj(logObj, key) { + private createHandlerObj(logObj: any, key: string) { return { class: 'thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler', formatter: 'LogFormatter', @@ -603,7 +533,7 @@ export class GatewayConfigurationComponent implements OnInit { }; } - createLoggerObj(logObj, key) { + private createLoggerObj(logObj: any, key: string) { return { handlers: [`${key}Handler`, 'consoleHandler'], level: logObj.logLevel, @@ -617,7 +547,7 @@ export class GatewayConfigurationComponent implements OnInit { const attributes = []; attributes.push({ key: 'RemoteLoggingLevel', - value: value.logs.remote.enabled ? value.logs.remote.logLevel : GatewayLogLevel.none + value: value.logs.remote.enabled ? value.logs.remote.logLevel : GatewayLogLevel.NONE }); delete value.connectors; attributes.push({ @@ -642,18 +572,18 @@ export class GatewayConfigurationComponent implements OnInit { this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, attributes).subscribe(_ => { - this.updateCredentials(value.thingsboard.security).subscribe(_ => { + this.updateCredentials(value.thingsboard.security).subscribe(() => { if (this.dialogRef) { this.dialogRef.close(); } else { this.gatewayConfigGroup.markAsPristine(); this.cd.detectChanges(); } - }) + }); }); } - updateCredentials(securityConfig): Observable { + private updateCredentials(securityConfig: any): Observable { let updateCredentials = false; let newCredentials = {}; if (securityConfig.type === SecurityTypes.USERNAME_PASSWORD) { @@ -661,14 +591,23 @@ export class GatewayConfigurationComponent implements OnInit { updateCredentials = true; } else { const parsedCredentials = JSON.parse(this.initialCredentials.credentialsValue); - updateCredentials = !(parsedCredentials.clientId === securityConfig.clientId && parsedCredentials.userName === securityConfig.username && parsedCredentials.password === securityConfig.password); + updateCredentials = !( + parsedCredentials.clientId === securityConfig.clientId && + parsedCredentials.userName === securityConfig.username && + parsedCredentials.password === securityConfig.password); } if (updateCredentials) { - let credentialsValue: { clientId?, userName?, password? } = {}; + const credentialsValue: { clientId?: string; userName?: string; password?: string } = {}; const credentialsType = DeviceCredentialsType.MQTT_BASIC; - if (securityConfig.clientId) credentialsValue.clientId = securityConfig.clientId; - if (securityConfig.username) credentialsValue.userName = securityConfig.username; - if (securityConfig.password) credentialsValue.password = securityConfig.password; + if (securityConfig.clientId) { + credentialsValue.clientId = securityConfig.clientId; + } + if (securityConfig.username) { + credentialsValue.userName = securityConfig.username; + } + if (securityConfig.password) { + credentialsValue.password = securityConfig.password; + } newCredentials = { credentialsType, credentialsValue: JSON.stringify(credentialsValue) @@ -684,12 +623,12 @@ export class GatewayConfigurationComponent implements OnInit { newCredentials = { credentialsType: DeviceCredentialsType.ACCESS_TOKEN, credentialsId: securityConfig.accessToken - } + }; } } if (updateCredentials) { - return this.deviceService.saveDeviceCredentials({...this.initialCredentials,...newCredentials}) + return this.deviceService.saveDeviceCredentials({...this.initialCredentials,...newCredentials}); } return of(null); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html index 6e4178fccd..9e852c0fcb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html @@ -15,82 +15,102 @@ limitations under the License. --> -
-
- - -

{{ 'gateway.connectors' | translate }}

- - -
- - - - {{ 'gateway.connectors-table-enabled' | translate }} - - - - - - - - {{ 'gateway.connectors-table-name' | translate }} - - {{ attribute.key }} - - - - - {{ 'gateway.connectors-table-type' | translate }} - - - {{ returnType(attribute) }} - - - - - {{ 'gateway.configuration' | translate }} - - -
{{isConnectorSynced(attribute)?'sync' : 'out of sync'}}
-
-
- - - {{ 'gateway.connectors-table-status' | translate }} - - - - - - - - {{ 'gateway.connectors-table-actions' | translate }} - - -
+
+
+ +

{{ 'gateway.connectors' | translate }}

+ + +
+
+ + + {{ 'gateway.connectors-table-enabled' | translate }} + + + + + + + + {{ 'gateway.connectors-table-name' | translate }} + + {{ attribute.key }} + + + + + {{ 'gateway.connectors-table-type' | translate }} + + + {{ returnType(attribute) }} + + + + + {{ 'gateway.configuration' | translate }} + + +
+ {{ isConnectorSynced(attribute) ? 'sync' : 'out of sync' }} +
+
+
+ + + {{ 'gateway.connectors-table-status' | translate }} + + + + + + + + {{ 'gateway.connectors-table-actions' | translate }} + + +
+ + + +
+
+ + -
-
- - - - - - -
-
-
- - -
- -
-
- - - {{ 'gateway.connectors-table-name' | translate }} - - - - {{ 'gateway.connectors-table-type' | translate }} - - {{type.value}} - - - - {{ 'gateway.connectors-table-key' | translate }} - - - - {{ 'gateway.connectors-table-class' | translate }} - - - - gateway.remote-logging-level - - {{logLevel}} - - - - - - -
- -
-
-
-
-
+ +
+ + + + + + +
+
+ + {{ 'gateway.connectors-table-name' | translate }} + + + + {{ 'gateway.connectors-table-type' | translate }} + + {{ type.value }} + + + + {{ 'gateway.connectors-table-key' | translate }} + + + + {{ 'gateway.connectors-table-class' | translate }} + + + + gateway.remote-logging-level + + {{ logLevel }} + + +
+
+ + +
+ +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss index 1edb40967c..f4326f6abc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import '../../../../../../../scss/constants'; + :host { width: 100%; height: 100%; @@ -21,25 +23,24 @@ padding: 0; .connector-container { + height: 100%; width: 100%; + flex-direction: row; + @media #{$mat-lt-lg} { + flex-direction: column; + } - & > mat-card, & > div { - min-width: calc(50% - 15px); + .table-container { + overflow: auto; + min-height: 35vh; } - mat-card { - margin: 10px; - padding: 10px; - max-width: 100%; + .flex { + flex: 1; } - } - .tb-entity-table { - .tb-entity-table-content { - width: 100%; - height: 100%; - background: #fff; - overflow: hidden; + .input-container { + height: auto; } } @@ -48,43 +49,28 @@ color: rgba(0, 0, 0, .87) !important; } - .mat-mdc-form-field { - flex-grow: 1; - } - - mat-card { - padding-left: 10px; - background: transparent; - } - - .mat-card-selected { - background-color: rgba(48, 86, 128, 0.1); - } - .mat-mdc-slide-toggle { - margin: 15px; + margin: 0 8px; } - .status-block { + .status { text-align: center; border-radius: 16px; font-weight: 500; width: fit-content; padding: 5px 15px; + &-sync { + background: rgba(25, 128, 56, .06); + color: rgb(25, 128, 56); + } + &-unsync { + background: rgba(203, 37, 48, .06); + color: rgb(203, 37, 48); + } } - .status-sync { - background: rgba(25, 128, 56, .06); - color: rgb(25, 128, 56); - } - - .status-unsync { - background: rgba(203, 37, 48, .06); - color: rgb(203, 37, 48); - } - - .action-btns { - margin: 10px 10px 0; + .action-button { + margin: 0 15px; } mat-row { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts index 14410c3b64..e1688357f5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -17,14 +17,11 @@ import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, NgZone, ViewChild } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; import { FormBuilder, FormGroup, UntypedFormControl, ValidatorFn, Validators } from '@angular/forms'; import { EntityId } from '@shared/models/id/entity-id'; -import { MatDialog } from '@angular/material/dialog'; import { AttributeService } from '@core/http/attribute.service'; -import { DeviceService } from '@core/http/device.service'; import { TranslateService } from '@ngx-translate/core'; -import { forkJoin } from 'rxjs'; +import { forkJoin, Observable } from 'rxjs'; import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { PageComponent } from '@shared/components/page.component'; import { PageLink } from '@shared/models/page/page-link'; @@ -33,47 +30,20 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MatSort } from '@angular/material/sort'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { MatTableDataSource } from '@angular/material/table'; -import { GatewayLogLevel } from '@home/components/widget/lib/gateway/gateway-configuration.component'; import { ActionNotificationShow } from '@core/notification/notification.actions'; import { DialogService } from '@core/services/dialog.service'; import { WidgetContext } from '@home/models/widget-component.models'; -import { deepClone } from '@core/utils'; +import { camelCase, deepClone, isString } from '@core/utils'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { DatasourceType, widgetType } from '@shared/models/widget.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityType } from '@shared/models/entity-type.models'; - - -export interface gatewayConnector { - name: string; - type: string; - configuration?: string; - configurationJson: string; - logLevel: string; - key?: string; -} - - -export const GatewayConnectorDefaultTypesTranslates = new Map([ - ['mqtt', 'MQTT'], - ['modbus', 'MODBUS'], - ['grpc', 'GRPC'], - ['opcua', 'OPCUA'], - ['opcua_asyncio', 'OPCUA ASYNCIO'], - ['ble', 'BLE'], - ['request', 'REQUEST'], - ['can', 'CAN'], - ['bacnet', 'BACNET'], - ['odbc', 'ODBC'], - ['rest', 'REST'], - ['snmp', 'SNMP'], - ['ftp', 'FTP'], - ['socket', 'SOCKET'], - ['xmpp', 'XMPP'], - ['ocpp', 'OCCP'], - ['custom', 'CUSTOM'] -]); +import { + GatewayConnector, + GatewayConnectorDefaultTypesTranslates, + GatewayLogLevel +} from './gateway-widget.models'; @Component({ selector: 'tb-gateway-connector', @@ -84,12 +54,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie pageLink: PageLink; - attributeDataSource: AttributeDatasource; - - inactiveConnectorsDataSource: AttributeDatasource; - - serverDataSource: AttributeDatasource; - dataSource: MatTableDataSource; displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions']; @@ -107,27 +71,29 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie connectorForm: FormGroup; - viewsInited = false; - textSearchMode: boolean; activeConnectors: Array; - inactiveConnectors: Array; + gatewayLogLevel = Object.values(GatewayLogLevel); - InitialActiveConnectors: Array; + private inactiveConnectors: Array; - gatewayLogLevel = Object.values(GatewayLogLevel); + private attributeDataSource: AttributeDatasource; + + private inactiveConnectorsDataSource: AttributeDatasource; - activeData: Array = []; + private serverDataSource: AttributeDatasource; - inactiveData: Array = []; + private activeData: Array = []; - sharedAttributeData: Array = []; + private inactiveData: Array = []; - initialConnector: gatewayConnector; + private sharedAttributeData: Array = []; - subscriptionOptions: WidgetSubscriptionOptions = { + private initialConnector: GatewayConnector; + + private subscriptionOptions: WidgetSubscriptionOptions = { callbacks: { onDataUpdated: () => this.ctx.ngZone.run(() => { this.onDataUpdated(); @@ -138,20 +104,17 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } }; - subscription: IWidgetSubscription; + private subscription: IWidgetSubscription; - constructor(protected router: Router, - protected store: Store, - protected fb: FormBuilder, - protected translate: TranslateService, - protected attributeService: AttributeService, - protected deviceService: DeviceService, - protected dialogService: DialogService, + constructor(protected store: Store, + private fb: FormBuilder, + private translate: TranslateService, + private attributeService: AttributeService, + private dialogService: DialogService, private telemetryWsService: TelemetryWebsocketService, private zone: NgZone, private utils: UtilsService, - private cd: ChangeDetectorRef, - public dialog: MatDialog) { + private cd: ChangeDetectorRef) { super(store); const sortOrder: SortOrder = {property: 'key', direction: Direction.ASC}; this.pageLink = new PageLink(1000, 0, null, sortOrder); @@ -186,16 +149,21 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie return data[sortHeaderId] || data.value[sortHeaderId]; }; - this.viewsInited = true; if (this.device) { - if (this.device.id === NULL_UUID) return; - forkJoin(this.attributeService.getEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, ['active_connectors']), - this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, ['inactive_connectors'])).subscribe(attributes => { + if (this.device.id === NULL_UUID) { + return; + } + forkJoin([ + this.attributeService.getEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, ['active_connectors']), + this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, ['inactive_connectors']) + ]).subscribe(attributes => { if (attributes.length) { this.activeConnectors = attributes[0].length ? attributes[0][0].value : []; - this.activeConnectors = typeof this.activeConnectors === 'string' ? JSON.parse(this.activeConnectors) : this.activeConnectors; + this.activeConnectors = isString(this.activeConnectors) ? JSON.parse(this.activeConnectors as any) : this.activeConnectors; this.inactiveConnectors = attributes[1].length ? attributes[1][0].value : []; - this.inactiveConnectors = typeof this.inactiveConnectors === 'string' ? JSON.parse(this.inactiveConnectors) : this.inactiveConnectors; + this.inactiveConnectors = isString(this.inactiveConnectors) + ? JSON.parse(this.inactiveConnectors as any) + : this.inactiveConnectors; this.updateData(true); } else { this.activeConnectors = []; @@ -206,7 +174,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } } - uniqNameRequired(): ValidatorFn { + private uniqNameRequired(): ValidatorFn { return (c: UntypedFormControl) => { const newName = c.value.trim().toLowerCase(); const found = this.dataSource.data.find((connectorAttr) => { @@ -229,7 +197,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie saveConnector(): void { const value = this.connectorForm.value; - value.configuration = this.camelize(value.name) + '.json'; + value.configuration = camelCase(value.name) + '.json'; if (value.type !== 'grpc') { delete value.key; } @@ -242,7 +210,9 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie value }]; const attributesToDelete = []; - const scope = (this.initialConnector && this.activeConnectors.includes(this.initialConnector.name)) ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; + const scope = (this.initialConnector && this.activeConnectors.includes(this.initialConnector.name)) + ? AttributeScope.SHARED_SCOPE + : AttributeScope.SERVER_SCOPE; let updateActiveConnectors = false; if (this.initialConnector && this.initialConnector.name !== value.name) { attributesToDelete.push({key: this.initialConnector.name}); @@ -256,19 +226,19 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.inactiveConnectors.splice(inactiveIndex, 1); } } - if (!this.activeConnectors.includes(value.name) && scope == AttributeScope.SHARED_SCOPE) { + if (!this.activeConnectors.includes(value.name) && scope === AttributeScope.SHARED_SCOPE) { this.activeConnectors.push(value.name); updateActiveConnectors = true; } - if (!this.inactiveConnectors.includes(value.name) && scope == AttributeScope.SERVER_SCOPE) { + if (!this.inactiveConnectors.includes(value.name) && scope === AttributeScope.SERVER_SCOPE) { this.inactiveConnectors.push(value.name); updateActiveConnectors = true; } const tasks = [this.attributeService.saveEntityAttributes(this.device, scope, attributesToSave)]; if (updateActiveConnectors) { tasks.push(this.attributeService.saveEntityAttributes(this.device, scope, [{ - key: scope == AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors', - value: scope == AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors + key: scope === AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors', + value: scope === AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors }])); } @@ -282,7 +252,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie }); } - updateData(reload: boolean = false) { + private updateData(reload: boolean = false) { this.pageLink.sortOrder.property = this.sort.active; this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; this.attributeDataSource.loadAttributes(this.device, AttributeScope.CLIENT_SCOPE, this.pageLink, reload).subscribe(data => { @@ -301,26 +271,30 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } isConnectorSynced(attribute: AttributeData) { - const connectorData = attribute.value; - if (!connectorData.ts) return false; + const connectorData = attribute.value; + if (!connectorData.ts) { + return false; + } const clientIndex = this.activeData.findIndex(data => { - const sharedData = data.value; + const sharedData = data.value; return sharedData.name === connectorData.name; - }) - if (clientIndex == -1) return false; + }); + if (clientIndex === -1) { + return false; + } const sharedIndex = this.sharedAttributeData.findIndex(data => { - const sharedData = data.value; + const sharedData = data.value; return sharedData.name === connectorData.name && sharedData.ts && sharedData.ts <= connectorData.ts; - }) + }); return sharedIndex !== -1; } - combineData() { + private combineData() { this.dataSource.data = [...this.activeData, ...this.inactiveData, ...this.sharedAttributeData].filter((item, index, self) => index === self.findIndex((t) => t.key === item.key) - ).map(attribute=>{ + ).map(attribute => { attribute.value = typeof attribute.value === 'string' ? JSON.parse(attribute.value) : attribute.value; - return attribute + return attribute; }); } @@ -330,14 +304,13 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } this.nameInput.nativeElement.focus(); this.clearOutConnectorForm(); - } - clearOutConnectorForm(): void { + private clearOutConnectorForm(): void { this.connectorForm.setValue({ name: '', type: 'mqtt', - logLevel: GatewayLogLevel.info, + logLevel: GatewayLogLevel.INFO, key: 'auto', class: '', configuration: '', @@ -347,7 +320,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.connectorForm.markAsPristine(); } - selectConnector(attribute): void { + selectConnector(attribute: AttributeData): void { if (this.connectorForm.disabled) { this.connectorForm.enable(); } @@ -363,8 +336,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.connectorForm.markAsPristine(); } - isSameConnector(attribute): boolean { - if (!this.initialConnector) return false; + isSameConnector(attribute: AttributeData): boolean { + if (!this.initialConnector) { + return false; + } const connector = attribute.value; return this.initialConnector.name === connector.name; } @@ -378,12 +353,11 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie verticalPosition: 'top', horizontalPosition: 'right', target: 'dashboardRoot', - // panelClass: this.widgetNamespace, forceDismiss: true })); } - returnType(attribute) { + returnType(attribute: AttributeData): string { const value = attribute.value; return this.gatewayConnectorDefaultTypes.get(value.type); } @@ -396,8 +370,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie const content = `All connector data will be deleted.`; this.dialogService.confirm(title, content, 'Cancel', 'Delete').subscribe(result => { if (result) { - const tasks = []; - const scope = (this.initialConnector && this.activeConnectors.includes(this.initialConnector.name)) ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; + const tasks: Array> = []; + const scope = (this.initialConnector && this.activeConnectors.includes(this.initialConnector.name)) + ? AttributeScope.SHARED_SCOPE + : AttributeScope.SERVER_SCOPE; tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, [attribute])); const activeIndex = this.activeConnectors.indexOf(attribute.key); const inactiveIndex = this.inactiveConnectors.indexOf(attribute.key); @@ -408,10 +384,10 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.inactiveConnectors.splice(inactiveIndex, 1); } tasks.push(this.attributeService.saveEntityAttributes(this.device, scope, [{ - key: scope == AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors', - value: scope == AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors + key: scope === AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors', + value: scope === AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors }])); - forkJoin(tasks).subscribe(_ => { + forkJoin(tasks).subscribe(() => { if (this.initialConnector ? this.initialConnector.name === attribute.key : true) { this.clearOutConnectorForm(); this.cd.detectChanges(); @@ -423,10 +399,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie }); } - camelize(str): string { - return str.toLowerCase().replace(/\s+/g, '_'); - } - connectorLogs(attribute: AttributeData, $event: Event): void { if ($event) { $event.stopPropagation(); @@ -448,7 +420,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } - enableConnector(attribute): void { + enableConnector(attribute: AttributeData): void { const wasEnabled = this.activeConnectors.includes(attribute.key); const scopeOld = wasEnabled ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; const scopeNew = !wasEnabled ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; @@ -475,7 +447,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie }); } - onDataUpdateError(e: any) { + private onDataUpdateError(e: any) { const exceptionData = this.utils.parseException(e); let errorText = exceptionData.name; if (exceptionData.message) { @@ -484,11 +456,11 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie console.error(errorText); } - onDataUpdated() { + private onDataUpdated() { this.cd.detectChanges(); } - generateSubscription() { + private generateSubscription() { if (this.subscription) { this.subscription.unsubscribe(); } @@ -497,21 +469,23 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie type: DatasourceType.entity, entityType: EntityType.DEVICE, entityId: this.device.id, - entityName: "Gateway", + entityName: 'Gateway', timeseries: [] }]; this.dataSource.data.forEach(value => { - subscriptionInfo[0].timeseries.push({name: `${value.key}_ERRORS_COUNT`, label: `${value.key}_ERRORS_COUNT`}) - }) - this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.latest, subscriptionInfo,this.subscriptionOptions, false, true).subscribe(subscription => { + subscriptionInfo[0].timeseries.push({name: `${value.key}_ERRORS_COUNT`, label: `${value.key}_ERRORS_COUNT`}); + }); + this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.latest, subscriptionInfo, this.subscriptionOptions, + false, true).subscribe(subscription => { this.subscription = subscription; }); } } - getErrorsCount(attribute) { + getErrorsCount(attribute: AttributeData): string { const connectorName = attribute.key; - const connector = this.subscription && this.subscription.data.find(data=>data && data.dataKey.name === `${connectorName}_ERRORS_COUNT`); - return (connector && this.activeConnectors.includes(connectorName))? connector.data[0][1]: 'Inactive'; + const connector = this.subscription && this.subscription.data + .find(data => data && data.dataKey.name === `${connectorName}_ERRORS_COUNT`); + return (connector && this.activeConnectors.includes(connectorName)) ? connector.data[0][1] : 'Inactive'; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html index bdf7d0d16e..973b3e02b7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html @@ -18,43 +18,39 @@ - Created time + {{ 'widgets.gateway.created-time' | translate }} - {{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss'}} + {{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss' }} - Status + {{ 'widgets.gateway.status' | translate }} - {{ attribute.status }} + {{ attribute.status }} - Message - + {{ 'widgets.gateway.message' | translate }} + {{ attribute.message }} - - + +
{{'attribute.no-telemetry-text' | translate}} + fxFlex fxLayoutAlign="center center" + class="no-data-found">{{ 'attribute.no-telemetry-text' | translate }} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts index f724e9dff9..6e0f20bdc3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts @@ -14,55 +14,27 @@ /// limitations under the License. /// -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; -import { FormBuilder, FormGroup } from '@angular/forms'; -import { MatDialog, MatDialogRef } from '@angular/material/dialog'; -import { AttributeService } from '@core/http/attribute.service'; -import { DeviceService } from '@core/http/device.service'; -import { TranslateService } from '@ngx-translate/core'; -import { AttributeData, DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { PageComponent } from '@shared/components/page.component'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { MatDialogRef } from '@angular/material/dialog'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { PageLink } from '@shared/models/page/page-link'; -import { AttributeDatasource } from "@home/models/datasource/attribute-datasource"; -import { Direction, SortOrder } from "@shared/models/page/sort-order"; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MatSort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; -import { GatewayLogLevel } from '@home/components/widget/lib/gateway/gateway-configuration.component'; -import { DialogService } from '@core/services/dialog.service'; import { WidgetContext } from '@home/models/widget-component.models'; import { MatPaginator } from '@angular/material/paginator'; - - -export interface GatewayConnector { - name: string; - type: string; - configuration?: string; - configurationJson: string; - logLevel: string; - key?: string; -} - -export interface LogLink { - name: string; - key: string; - filterFn?: Function; -} +import { GatewayLogData, GatewayStatus, LogLink } from './gateway-widget.models'; @Component({ selector: 'tb-gateway-logs', templateUrl: './gateway-logs.component.html', styleUrls: ['./gateway-logs.component.scss'] }) -export class GatewayLogsComponent extends PageComponent implements AfterViewInit { +export class GatewayLogsComponent implements AfterViewInit { pageLink: PageLink; - attributeDataSource: AttributeDatasource; - - dataSource: MatTableDataSource + dataSource: MatTableDataSource; displayedColumns = ['ts', 'status', 'message']; @@ -76,63 +48,39 @@ export class GatewayLogsComponent extends PageComponent implements AfterViewInit @ViewChild(MatSort) sort: MatSort; @ViewChild(MatPaginator) paginator: MatPaginator; - connectorForm: FormGroup; - - viewsInited = false; - textSearchMode: boolean; - activeConnectors: Array; - - inactiveConnectors: Array; - - InitialActiveConnectors: Array; - - gatewayLogLevel = Object.values(GatewayLogLevel); - logLinks: Array; - initialConnector: GatewayConnector; - activeLink: LogLink; gatewayLogLinks: Array = [ { - name: "General", - key: "LOGS" + name: 'General', + key: 'LOGS' }, { - name: "Service", - key: "SERVICE_LOGS" + name: 'Service', + key: 'SERVICE_LOGS' }, { - name: "Connection", - key: "CONNECTION_LOGS" + name: 'Connection', + key: 'CONNECTION_LOGS' }, { - name: "Storage", - key: "STORAGE_LOGS" + name: 'Storage', + key: 'STORAGE_LOGS' }, { key: 'EXTENSIONS_LOGS', - name: "Extension" - }] - - - constructor(protected router: Router, - protected store: Store, - protected fb: FormBuilder, - protected translate: TranslateService, - protected attributeService: AttributeService, - protected deviceService: DeviceService, - protected dialogService: DialogService, - public dialog: MatDialog) { - super(store); + name: 'Extension' + }]; + + + constructor() { const sortOrder: SortOrder = {property: 'ts', direction: Direction.DESC}; this.pageLink = new PageLink(10, 0, null, sortOrder); - this.dataSource = new MatTableDataSource([]); - + this.dataSource = new MatTableDataSource([]); } - ngAfterViewInit() { this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; @@ -140,22 +88,18 @@ export class GatewayLogsComponent extends PageComponent implements AfterViewInit this.ctx.defaultSubscription.options.timeWindowConfig = timewindow; this.ctx.defaultSubscription.updateDataSubscriptions(); return timewindow; - } + }; if (this.ctx.settings.isConnectorLog && this.ctx.settings.connectorLogState) { const connector = this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState]; this.logLinks = [{ key: `${connector.key}_LOGS`, - name: "Connector", - filterFn: (attrData)=>{ - return !attrData.message.includes(`_converter.py`) - } - },{ + name: 'Connector', + filterFn: (attrData) => !attrData.message.includes(`_converter.py`) + }, { key: `${connector.key}_LOGS`, - name: "Converter", - filterFn: (attrData)=>{ - return attrData.message.includes(`_converter.py`) - } - }] + name: 'Converter', + filterFn: (attrData) => attrData.message.includes(`_converter.py`) + }]; } else { this.logLinks = this.gatewayLogLinks; } @@ -164,20 +108,20 @@ export class GatewayLogsComponent extends PageComponent implements AfterViewInit } - updateData(sort?) { + private updateData() { if (this.ctx.defaultSubscription.data.length && this.ctx.defaultSubscription.data[0]) { let attrData = this.ctx.defaultSubscription.data[0].data.map(data => { - let result = { + const result = { ts: data[0], key: this.activeLink.key, message: /\[(.*)/.exec(data[1])[0], - status: 'INVALID LOG FORMAT' + status: 'INVALID LOG FORMAT' as GatewayStatus }; try { - result.status= data[1].match(/\|(\w+)\|/)[1]; + result.status = data[1].match(/\|(\w+)\|/)[1]; } catch (e) { - result.status = 'INVALID LOG FORMAT' + result.status = 'INVALID LOG FORMAT' as GatewayStatus; } return result; @@ -186,39 +130,35 @@ export class GatewayLogsComponent extends PageComponent implements AfterViewInit attrData = attrData.filter(data => this.activeLink.filterFn(data)); } this.dataSource.data = attrData; - if (sort) { - this.dataSource.sortData(this.dataSource.data, this.sort); - } } } - onTabChanged(link) { + onTabChanged(link: LogLink) { this.activeLink = link; this.changeSubscription(); } - statusClass(status) { + statusClass(status: GatewayStatus): string { switch (status) { - case GatewayLogLevel.debug: - return "status status-debug"; - case GatewayLogLevel.warning: - return "status status-warning"; - case GatewayLogLevel.error: - case "EXCEPTION": - return "status status-error"; - case GatewayLogLevel.info: + case GatewayStatus.DEBUG: + return 'status status-debug'; + case GatewayStatus.WARNING: + return 'status status-warning'; + case GatewayStatus.ERROR: + case GatewayStatus.EXCEPTION: + return 'status status-error'; default: - return "status status-info"; + return 'status status-info'; } } - statusClassMsg(status) { - if (status === "EXCEPTION") { + statusClassMsg(status?: GatewayStatus): string { + if (status === GatewayStatus.EXCEPTION) { return 'msg-status-exception'; } } - changeSubscription() { + private changeSubscription() { if (this.ctx.datasources && this.ctx.datasources[0].entity && this.ctx.defaultSubscription.options.datasources) { this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ name: this.activeLink.key, @@ -229,8 +169,7 @@ export class GatewayLogsComponent extends PageComponent implements AfterViewInit this.ctx.defaultSubscription.updateDataSubscriptions(); this.ctx.defaultSubscription.callbacks.onDataUpdated = () => { this.updateData(); - } - + }; } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html index 7a66cfd4fb..adb7d0b197 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html @@ -15,43 +15,38 @@ limitations under the License. --> -
- - warning -

gateway.configuration-delete-dialog-header

- - -
-
-
- - - gateway.configuration-delete-dialog-input - - - {{'gateway.configuration-delete-dialog-input-required' | translate }} - - -
-
-
- - -
-
+ + warning +

gateway.configuration-delete-dialog-header

+ + +
+
+ + + gateway.configuration-delete-dialog-input + + + {{ 'gateway.configuration-delete-dialog-input-required' | translate }} + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts index d5274eef97..68c19d83a5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -31,11 +31,10 @@ export interface GatewayRemoteConfigurationDialogData { templateUrl: './gateway-remote-configuration-dialog.html' }) -export class GatewayRemoteConfigurationDialogComponent extends DialogComponent implements OnInit { +export class GatewayRemoteConfigurationDialogComponent extends + DialogComponent{ gatewayName: string; - gatewayControl: FormControl; constructor(protected store: Store, @@ -45,10 +44,7 @@ export class GatewayRemoteConfigurationDialogComponent extends DialogComponent
- - {{'gateway.statistics.command' | translate}} - - - {{command}} - - - - - - {{'gateway.statistics.timeout-ms' | translate}} - - - {{'gateway.statistics.timeout-min' | translate }} - - - - {{'widget-config.datasource-parameters' | translate}} - - edit - - - + + + {{ 'gateway.statistics.command' | translate }} + + + {{ command }} + + + + + {{ 'gateway.statistics.timeout-ms' | translate }} + + + {{ 'gateway.statistics.timeout-min' | translate }} + + + + + + {{ 'gateway.statistics.command' | translate }} + + + + {{ 'widget-config.datasource-parameters' | translate }} + + edit + + + +
- - {{'gateway.rpc-command-result' | translate}} +
+ {{ 'gateway.rpc-command-result' | translate }} - - + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss index afdcf9b1f0..345688d451 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss @@ -22,24 +22,24 @@ padding: 0; .command-form { - width: 100%; flex-wrap: nowrap; - padding: 0 15px 5px; - margin-bottom: 5px; - + padding: 0 5px 5px; & > button { margin-top: 10px; } } .result-block { - padding: 0 15px; + padding: 0 5px; display: flex; flex-direction: column; - + flex: 1; & > span { font-weight: 600; } + tb-json-content { + flex: 1; + } } } @@ -47,9 +47,5 @@ .tb-json-content { height: 100%; } - - .mat-mdc-form-field-icon-suffix { - z-index: 100; - } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts index dbda5f4db2..0c2e8f8c55 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts @@ -14,17 +14,9 @@ /// limitations under the License. /// -import { AfterViewInit, Component, Input } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; +import { AfterViewInit, Component, Input } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; -import { AttributeService } from '@core/http/attribute.service'; -import { DeviceService } from '@core/http/device.service'; -import { TranslateService } from '@ngx-translate/core'; -import { PageComponent } from "@shared/components/page.component"; -import { DialogService } from '@core/services/dialog.service'; import { WidgetContext } from '@home/models/widget-component.models'; import { ContentType } from '@shared/models/constants'; import { @@ -32,13 +24,12 @@ import { JsonObjectEditDialogData } from '@shared/components/dialog/json-object-edit-dialog.component'; - @Component({ selector: 'tb-gateway-service-rpc', templateUrl: './gateway-service-rpc.component.html', styleUrls: ['./gateway-service-rpc.component.scss'] }) -export class GatewayServiceRPCComponent extends PageComponent implements AfterViewInit { +export class GatewayServiceRPCComponent implements AfterViewInit { @Input() ctx: WidgetContext; @@ -52,39 +43,28 @@ export class GatewayServiceRPCComponent extends PageComponent implements AfterVi isConnector: boolean; - connectorType: string; - RPCCommands: Array = [ - "Ping", - "Stats", - "Devices", - "Update", - "Version", - "Restart", - "Reboot" - ] - - - constructor(protected router: Router, - protected store: Store, - protected fb: FormBuilder, - protected translate: TranslateService, - protected attributeService: AttributeService, - protected deviceService: DeviceService, - protected dialogService: DialogService, - public dialog: MatDialog) { - super(store); + 'Ping', + 'Stats', + 'Devices', + 'Update', + 'Version', + 'Restart', + 'Reboot' + ]; + + private connectorType: string; + + constructor(private fb: FormBuilder, + private dialog: MatDialog) { this.commandForm = this.fb.group({ command: [null,[Validators.required]], time: [60, [Validators.required, Validators.min(1)]], - params: ["{}", [Validators.required]], + params: ['{}', [Validators.required]], result: [null] - }) - - + }); } - ngAfterViewInit() { this.isConnector = this.ctx.settings.isConnector; if (!this.isConnector) { @@ -94,16 +74,16 @@ export class GatewayServiceRPCComponent extends PageComponent implements AfterVi } } - sendCommand() { const formValues = this.commandForm.value; const commandPrefix = this.isConnector ? `${this.connectorType}_` : 'gateway_'; - this.ctx.controlApi.sendTwoWayCommand(commandPrefix+formValues.command.toLowerCase(), {},formValues.time).subscribe(resp=>{ - this.commandForm.get('result').setValue(JSON.stringify(resp)); - },error => { - console.log(error); - this.commandForm.get('result').setValue(JSON.stringify(error.error)); - }) + this.ctx.controlApi.sendTwoWayCommand(commandPrefix+formValues.command.toLowerCase(), {},formValues.time).subscribe({ + next: resp => this.commandForm.get('result').setValue(JSON.stringify(resp)), + error: error => { + console.log(error); + this.commandForm.get('result').setValue(JSON.stringify(error.error)); + } + }); } openEditJSONDialog($event: Event) { @@ -124,5 +104,4 @@ export class GatewayServiceRPCComponent extends PageComponent implements AfterVi } ); } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html index 8da9b2bce8..8671b58f46 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html @@ -19,13 +19,13 @@
- {{'gateway.statistics.statistic' | translate}} + {{ 'gateway.statistics.statistic' | translate }} - {{key}} + {{ key }} - {{command.attributeOnGateway}} + {{ command.attributeOnGateway }} @@ -34,44 +34,42 @@ {{'gateway.statistics.statistic-commands-empty' | translate }} - {{'gateway.statistics.command' | translate}} + {{ 'gateway.statistics.command' | translate }}
-
- -
+
- {{'audit-log.timestamp' | translate}} + {{ 'widgets.gateway.created-time' | translate }} {{row[0]| date:'yyyy-MM-dd HH:mm:ss' }} - {{"event.message" | translate}} + {{ 'widgets.gateway.message' | translate }} {{ row[1] }} - -
{{'attribute.no-telemetry-text' | translate}} + class="no-data-found">{{ 'attribute.no-telemetry-text' | translate }}
- +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts index 90e5541217..2e1deb900a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts @@ -14,18 +14,10 @@ /// limitations under the License. /// -import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, ViewChild } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { MatDialog } from '@angular/material/dialog'; import { AttributeService } from '@core/http/attribute.service'; -import { DeviceService } from '@core/http/device.service'; -import { TranslateService } from '@ngx-translate/core'; import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { PageComponent } from '@shared/components/page.component'; -import { DialogService } from '@core/services/dialog.service'; import { WidgetContext } from '@home/models/widget-component.models'; import { TbFlot } from '@home/components/widget/lib/flot-widget'; import { ResizeObserver } from '@juggle/resize-observer'; @@ -41,13 +33,12 @@ import { MatTableDataSource } from '@angular/material/table'; import { MatSort } from '@angular/material/sort'; import { NULL_UUID } from '@shared/models/id/has-uuid'; - @Component({ selector: 'tb-gateway-statistics', templateUrl: './gateway-statistics.component.html', styleUrls: ['./gateway-statistics.component.scss'] }) -export class GatewayStatisticsComponent extends PageComponent implements AfterViewInit { +export class GatewayStatisticsComponent implements AfterViewInit { @ViewChild(MatSort) sort: MatSort; @ViewChild('statisticChart') statisticChart: ElementRef; @@ -58,10 +49,10 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi @Input() public general: boolean; - public isNumericData: boolean = false; + public isNumericData = false; public chartInited: boolean; private flot: TbFlot; - private flotCtx; + private flotCtx: WidgetContext; public statisticForm: FormGroup; public statisticsKeys = []; public commands = []; @@ -89,70 +80,61 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi }; - constructor(protected router: Router, - protected store: Store, - protected fb: FormBuilder, - protected translate: TranslateService, - protected attributeService: AttributeService, - protected deviceService: DeviceService, - protected dialogService: DialogService, - private cd: ChangeDetectorRef, - private utils: UtilsService, - public dialog: MatDialog) { - super(store); + constructor(private fb: FormBuilder, + private attributeService: AttributeService, + private utils: UtilsService) { const sortOrder: SortOrder = {property: '0', direction: Direction.DESC}; this.pageLink = new PageLink(Number.POSITIVE_INFINITY, 0, null, sortOrder); this.displayedColumns = ['0', '1']; this.dataSource = new MatTableDataSource([]); this.statisticForm = this.fb.group({ statisticKey: [null, []] - }) + }); this.statisticForm.get('statisticKey').valueChanges.subscribe(value => { this.commandObj = null; if (this.commands.length) { this.commandObj = this.commands.find(command => command.attributeOnGateway === value); } - if (this.subscriptionInfo) this.createChartsSubscription(this.ctx.defaultSubscription.datasources[0].entity, value); - }) + if (this.subscriptionInfo) { + this.createChartsSubscription(this.ctx.defaultSubscription.datasources[0].entity, value); + } + }); } ngAfterViewInit() { this.dataSource.sort = this.sort; - this.sort.sortChange.subscribe(_=>{ - this.sortData(); - }) + this.sort.sortChange.subscribe(() => this.sortData()); this.init(); if (this.ctx.defaultSubscription.datasources.length) { const gateway = this.ctx.defaultSubscription.datasources[0].entity; - if (gateway.id.id === NULL_UUID) return; + if (gateway.id.id === NULL_UUID) { + return; + } if (!this.general) { - this.attributeService.getEntityAttributes(gateway.id, AttributeScope.SHARED_SCOPE, ["general_configuration"]).subscribe((resp: AttributeData[]) => { - if (resp && resp.length) { - this.commands = resp[0].value.statistics.commands; - if (!this.statisticForm.get('statisticKey').value && this.commands && this.commands.length) { - this.statisticForm.get('statisticKey').setValue(this.commands[0].attributeOnGateway); - this.createChartsSubscription(gateway, this.commands[0].attributeOnGateway); + this.attributeService.getEntityAttributes(gateway.id, AttributeScope.SHARED_SCOPE, ['general_configuration']) + .subscribe((resp: AttributeData[]) => { + if (resp && resp.length) { + this.commands = resp[0].value.statistics.commands; + if (!this.statisticForm.get('statisticKey').value && this.commands && this.commands.length) { + this.statisticForm.get('statisticKey').setValue(this.commands[0].attributeOnGateway); + this.createChartsSubscription(gateway, this.commands[0].attributeOnGateway); + } } - } - }) + }); } else { - let connectorsTs; this.attributeService.getEntityTimeseriesLatest(gateway.id).subscribe( data => { - connectorsTs = Object.keys(data) - .filter(el => el.includes( - 'ConnectorEventsProduced' - ) || el.includes( - 'ConnectorEventsSent')) + const connectorsTs = Object.keys(data) + .filter(el => el.includes('ConnectorEventsProduced') || el.includes('ConnectorEventsSent')); this.createGeneralChartsSubscription(gateway, connectorsTs); - }) + }); } } } - public sortData () { + public sortData() { this.dataSource.sortData(this.dataSource.data, this.sort); } @@ -162,7 +144,7 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi } private createChartsSubscription(gateway: BaseData, attr: string) { - let subscriptionInfo = [{ + const subscriptionInfo = [{ type: DatasourceType.entity, entityType: EntityType.DEVICE, entityId: gateway.id.id, @@ -175,8 +157,8 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi this.changeSubscription(subscriptionInfo); } - private createGeneralChartsSubscription(gateway: BaseData, attrData: [string]) { - let subscriptionInfo = [{ + private createGeneralChartsSubscription(gateway: BaseData, attrData: string[]) { + const subscriptionInfo = [{ type: DatasourceType.entity, entityType: EntityType.DEVICE, entityId: gateway.id.id, @@ -184,20 +166,20 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi timeseries: [] }]; subscriptionInfo[0].timeseries = []; - if (attrData && attrData.length) { + if (attrData?.length) { attrData.forEach(attr => { - subscriptionInfo[0].timeseries.push({name: attr, label: attr}) - }) + subscriptionInfo[0].timeseries.push({name: attr, label: attr}); + }); } this.ctx.defaultSubscription.datasources[0].dataKeys.forEach(dataKey => { - subscriptionInfo[0].timeseries.push({name: dataKey.name, label: dataKey.label}) - }) + subscriptionInfo[0].timeseries.push({name: dataKey.name, label: dataKey.label}); + }); this.subscriptionInfo = subscriptionInfo; this.changeSubscription(subscriptionInfo); } - init = () => { + private init = () => { this.flotCtx = { $scope: this.ctx.$scope, $injector: this.ctx.$injector, @@ -207,20 +189,20 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi subscriptionApi: this.ctx.subscriptionApi, detectChanges: this.ctx.detectChanges, settings: this.ctx.settings - }; - } + } as WidgetContext; + }; - updateChart = () => { + private updateChart = () => { if (this.flot && this.ctx.defaultSubscription.data.length) { this.flot.update(); } - } + }; - resize = () => { + private resize = () => { if (this.flot) { this.flot.resize(); } - } + }; private reset() { if (this.resize$) { @@ -260,7 +242,7 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi this.chartInited = true; this.flotCtx.$container = $(this.statisticChart.nativeElement); this.resize$.observe(this.statisticChart.nativeElement); - this.flot = new TbFlot(this.flotCtx as WidgetContext, "line"); + this.flot = new TbFlot(this.flotCtx as WidgetContext, 'line'); this.flot.update(); } @@ -270,16 +252,17 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi return; } this.dataSource.data = this.subscription.data.length ? this.subscription.data[0].data : []; - this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1]) ); + this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1])); } - changeSubscription(subscriptionInfo: SubscriptionInfo[]) { + private changeSubscription(subscriptionInfo: SubscriptionInfo[]) { if (this.subscription) { this.reset(); } if (this.ctx.datasources[0].entity) { - this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions, false, true).subscribe(subscription => { + this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions, + false, true).subscribe(subscription => { this.subscription = subscription; this.isDataOnlyNumbers(); this.legendData = this.subscription.legendData; @@ -291,9 +274,7 @@ export class GatewayStatisticsComponent extends PageComponent implements AfterVi if (this.isNumericData) { this.initChart(); } - }) - + }); } } - } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts new file mode 100644 index 0000000000..c08ee7dc89 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -0,0 +1,142 @@ +/// +/// Copyright © 2016-2023 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 enum StorageTypes { + MEMORY = 'memory', + FILE = 'file', + SQLITE = 'sqlite' +} + +export enum DeviceGatewayStatus { + EXCEPTION = 'EXCEPTION' +} + +export enum GatewayLogLevel { + NONE = 'NONE', + CRITICAL = 'CRITICAL', + ERROR = 'ERROR', + WARNING = 'WARNING', + INFO = 'INFO', + DEBUG = 'DEBUG' +} + +export const GatewayStatus = { + ...GatewayLogLevel, + ...DeviceGatewayStatus +}; + +export type GatewayStatus = DeviceGatewayStatus | GatewayLogLevel; + +export enum LogSavingPeriod { + days = 'D', + hours = 'H', + minutes = 'M', + seconds = 'S' +} + +export enum LocalLogsConfigs { + service = 'service', + connector = 'connector', + converter = 'converter', + tb_connection = 'tb_connection', + storage = 'storage', + extension = 'extension' +} + +export const LocalLogsConfigTranslateMap = new Map([ + [LocalLogsConfigs.service, 'Service'], + [LocalLogsConfigs.connector, 'Connector'], + [LocalLogsConfigs.converter, 'Converter'], + [LocalLogsConfigs.tb_connection, 'TB Connection'], + [LocalLogsConfigs.storage, 'Storage'], + [LocalLogsConfigs.extension, 'Extension'] +]); + +export const LogSavingPeriodTranslations = new Map( + [ + [LogSavingPeriod.days, 'gateway.logs.days'], + [LogSavingPeriod.hours, 'gateway.logs.hours'], + [LogSavingPeriod.minutes, 'gateway.logs.minutes'], + [LogSavingPeriod.seconds, 'gateway.logs.seconds'] + ] +); + +export const StorageTypesTranslationMap = new Map( + [ + [StorageTypes.MEMORY, 'gateway.storage-types.memory-storage'], + [StorageTypes.FILE, 'gateway.storage-types.file-storage'], + [StorageTypes.SQLITE, 'gateway.storage-types.sqlite'] + ] +); + +export enum SecurityTypes { + ACCESS_TOKEN = 'accessToken', + USERNAME_PASSWORD = 'usernamePassword', + TLS_ACCESS_TOKEN = 'tlsAccessToken', + TLS_PRIVATE_KEY = 'tlsPrivateKey' +} + +export const GecurityTypesTranslationsMap = new Map( + [ + [SecurityTypes.ACCESS_TOKEN, 'gateway.security-types.access-token'], + [SecurityTypes.USERNAME_PASSWORD, 'gateway.security-types.username-password'], + [SecurityTypes.TLS_ACCESS_TOKEN, 'gateway.security-types.tls-access-token'], + // [SecurityTypes.TLS_PRIVATE_KEY, 'gateway.security-types.tls-private-key'], + ] +); + +export interface GatewayConnector { + name: string; + type: string; + configuration?: string; + configurationJson: string; + logLevel: string; + key?: string; +} + + +export const GatewayConnectorDefaultTypesTranslates = new Map([ + ['mqtt', 'MQTT'], + ['modbus', 'MODBUS'], + ['grpc', 'GRPC'], + ['opcua', 'OPCUA'], + ['opcua_asyncio', 'OPCUA ASYNCIO'], + ['ble', 'BLE'], + ['request', 'REQUEST'], + ['can', 'CAN'], + ['bacnet', 'BACNET'], + ['odbc', 'ODBC'], + ['rest', 'REST'], + ['snmp', 'SNMP'], + ['ftp', 'FTP'], + ['socket', 'SOCKET'], + ['xmpp', 'XMPP'], + ['ocpp', 'OCCP'], + ['custom', 'CUSTOM'] +]); + +export interface LogLink { + name: string; + key: string; + filterFn?: (arg: any) => boolean; +} + +export interface GatewayLogData { + ts: number; + key: string; + message: string; + status: GatewayStatus; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html index 25d77badc7..a9e350eb95 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.html @@ -15,12 +15,13 @@ limitations under the License. --> -
- - {{"widgets.gateway.is-connector" | translate}} - - - {{"widgets.gateway.state-param-name" | translate}} - - -
+
+
+ + {{"widgets.gateway.is-connector" | translate}} + + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts index 74e214a3d9..5d3ea09447 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts @@ -16,7 +16,7 @@ import { Component } from '@angular/core'; import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -27,15 +27,15 @@ import { AppState } from '@core/core.state'; }) export class GatewayLogsSettingsComponent extends WidgetSettingsComponent { - GatewayLogSettingForm: UntypedFormGroup; + gatewayLogSettingForm: FormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: FormBuilder) { super(store); } - protected settingsForm(): UntypedFormGroup { - return this.GatewayLogSettingForm; + protected settingsForm(): FormGroup { + return this.gatewayLogSettingForm; } protected defaultSettings(): WidgetSettings { @@ -46,9 +46,22 @@ export class GatewayLogsSettingsComponent extends WidgetSettingsComponent { } protected onSettingsSet(settings: WidgetSettings) { - this.GatewayLogSettingForm = this.fb.group({ + this.gatewayLogSettingForm = this.fb.group({ isConnectorLog: [false, []], - connectorLogState: ['default', []] + connectorLogState: ['default', Validators.required] }); } + + protected validatorTriggers(): string[] { + return ['isConnectorLog']; + } + + protected updateValidators(emitEvent: boolean) { + const isConnectorLog: boolean = this.gatewayLogSettingForm.get('isConnectorLog').value; + if (isConnectorLog) { + this.gatewayLogSettingForm.get('connectorLogState').enable({emitEvent}); + } else { + this.gatewayLogSettingForm.get('connectorLogState').disable({emitEvent}); + } + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html index beeba7c2a7..391d7a9bc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.html @@ -15,8 +15,10 @@ limitations under the License. --> -
- - {{"widgets.gateway.is-connector" | translate}} - -
+
+
+ + {{"widgets.gateway.is-connector" | translate}} + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts index edafed90b8..5b0402236b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-service-rpc-settings.component.ts @@ -16,7 +16,7 @@ import { Component } from '@angular/core'; import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; -import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -27,15 +27,15 @@ import { AppState } from '@core/core.state'; }) export class GatewayServiceRPCSettingsComponent extends WidgetSettingsComponent { - GatewayLogSettingForm: UntypedFormGroup; + gatewayServiceRPCSettingForm: FormGroup; constructor(protected store: Store, - private fb: UntypedFormBuilder) { + private fb: FormBuilder) { super(store); } - protected settingsForm(): UntypedFormGroup { - return this.GatewayLogSettingForm; + protected settingsForm(): FormGroup { + return this.gatewayServiceRPCSettingForm; } protected defaultSettings(): WidgetSettings { @@ -45,7 +45,7 @@ export class GatewayServiceRPCSettingsComponent extends WidgetSettingsComponent } protected onSettingsSet(settings: WidgetSettings) { - this.GatewayLogSettingForm = this.fb.group({ + this.gatewayServiceRPCSettingForm = this.fb.group({ isConnector: [false, []] }); } diff --git a/ui-ngx/src/app/shared/components/file-input.component.html b/ui-ngx/src/app/shared/components/file-input.component.html index 308e9e59f7..c0c004d02c 100644 --- a/ui-ngx/src/app/shared/components/file-input.component.html +++ b/ui-ngx/src/app/shared/components/file-input.component.html @@ -16,12 +16,14 @@ -->
- + + + + + +
diff --git a/ui-ngx/src/app/shared/components/file-input.component.scss b/ui-ngx/src/app/shared/components/file-input.component.scss index a229a01bfb..45ed704336 100644 --- a/ui-ngx/src/app/shared/components/file-input.component.scss +++ b/ui-ngx/src/app/shared/components/file-input.component.scss @@ -22,8 +22,10 @@ $previewSize: 100px !default; .tb-container { margin-top: 0; label.tb-title { - display: block; padding-bottom: 8px; + &:not(.pointer-event) { + display: block; + } } } 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 560402c622..7397f82993 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2635,7 +2635,7 @@ "gateway": { "add-entry": "Add configuration", "advanced": "Advanced", - "checking-device-activity": "Checking Device Activity:", + "checking-device-activity": "Checking device activity", "command": "Docker commands", "command-copied-message": "Docker command has been copied to clipboard", "configuration": "Configuration", @@ -2888,7 +2888,7 @@ "log-format": "Log message format", "remote-log": "Enables remote logging and logs reading from the gateway", "backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted", - "storage": "Provides configuration for saving incoming data before it is sent to the ThingsBoard platform", + "storage": "Provides configuration for saving incoming data before it is sent to the platform", "file": "Received data saving to the hard drive", "memory": "Received data saving to the RAM memory", "sqlite": "Received data saving to the .db file", @@ -5330,7 +5330,10 @@ "events-filter": "Events filter", "event-key-contains": "Event key contains...", "is-connector": "Is Connector", - "state-param-name": "State parameter connector name" + "state-param-name": "State parameter connector name", + "status": "Status", + "message": "Message", + "created-time": "Created time" }, "gauge": { "default-color": "Default color", diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 141a8db6ea..63f126f405 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -65,6 +65,9 @@ box-shadow: none; border-radius: 0; } + &.no-gap { + gap: 0; + } &.tb-slide-toggle { padding: 0; gap: 0; @@ -218,7 +221,7 @@ } } - .tb-form-row .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field { + .tb-form-row:not(.tb-not-inline-field) .mat-mdc-form-field, .mat-mdc-form-field.tb-inline-field { &.mat-form-field-appearance-fill { .mdc-text-field--filled:not(.mdc-text-field--disabled) { &:before {