Browse Source

Merge pull request #5 from MrKartoshka/improvement/gateway-dashboard/change

qa fixes
pull/9242/head
Vladyslav 3 years ago
committed by GitHub
parent
commit
9382a13923
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 47
      application/src/main/data/json/demo/dashboards/gateway_list.json
  2. 2
      application/src/main/data/json/system/widget_types/gateway_general_statistics.json
  3. 2
      application/src/main/data/json/system/widget_types/gateway_statistics.json
  4. 6
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html
  5. 8
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss
  6. 53
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts
  7. 4
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html
  8. 10
      ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts
  9. 4
      ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts
  10. 6
      ui-ngx/src/assets/locale/locale.constant-en_US.json

47
application/src/main/data/json/demo/dashboards/gateway_list.json

@ -44,7 +44,8 @@
"enableSearch": true, "enableSearch": true,
"enableSelectColumnDisplay": false, "enableSelectColumnDisplay": false,
"enableStickyHeader": true, "enableStickyHeader": true,
"enableStickyAction": true, "enableStickyAction": false,
"showCellActionsMenu": true,
"reserveSpaceForHiddenAction": "true", "reserveSpaceForHiddenAction": "true",
"displayEntityName": false, "displayEntityName": false,
"displayEntityLabel": false, "displayEntityLabel": false,
@ -570,9 +571,9 @@
"padding": "8px", "padding": "8px",
"settings": { "settings": {
"useMarkdownTextFunction": true, "useMarkdownTextFunction": true,
"markdownTextFunction": "var blockData = '';\nfunction createDataBlock(value, label, dividerStyle, mobile) {\n blockData += `\n <mat-card style=\"flex-grow: 1; width: ${mobile? '100%': 'auto'}; min-height: ${mobile? 'auto': '57px'}\" class=\" ${dividerStyle}\">\n <div class=\"divider\"></div>\n <mat-divider vertical style=\"height:100%\"></mat-divider>\n <mat-card-header>\n <mat-card-subtitle>${label}</mat-card-subtitle>\n </mat-card-header>\n <mat-card-content> ${value}</mat-card-content>\n </mat-card>`;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[1]?data[1].count:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[2]?data[2][\"count 2\"]:0)} </span>`\n , \"Devices\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} </span>`\n , \"Connectors\", '');\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red');\nreturn `<div fxLayout=\"row wrap\" fxLayoutGap=\"8px\" class=\"cards-container\">${blockData}</div>`;", "markdownTextFunction": "var blockData = '';\nvar connectorsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Connecotrs\");\nvar logsIndex = ctx.actionsApi.getActionDescriptors('elementClick').findIndex(action=>action.name==\"Logs\");\nfunction generateMatHeader(index) {\n if( index !== undefined && index > -1) {\n return `<mat-card-header class='tb-home-widget-link' (click)=\"ctx.actionsApi.handleWidgetAction($event, ctx.actionsApi.getActionDescriptors('elementClick')[${index}], ctx.datasources[0].entity.id)\">`\n } else {\n return \"<mat-card-header >\" \n }\n}\nfunction createDataBlock(value, label, dividerStyle, mobile, index) {\n blockData += `\n <mat-card style=\"flex-grow: 1; width: ${mobile? '100%': 'auto'}; min-height: ${mobile? 'auto': '57px'}\" class=\" ${dividerStyle}\">\n <div class=\"divider\"></div>\n <mat-divider vertical style=\"height:100%\"></mat-divider>\n ${generateMatHeader(index)}\n <mat-card-subtitle>${label}</mat-card-subtitle>\n </mat-card-header>\n <mat-card-content> ${value}</mat-card-content>\n </mat-card>`;\n}\ncreateDataBlock(data[0].Status, \"Status\", data[0].Status === \"Active\"? 'divider-green' : 'divider-red');\ncreateDataBlock(data[0].Name, \"Gateway Name\", '', ctx.isMobile);\ncreateDataBlock(data[0].Type, \"Gateway Type\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[1]?data[1].count:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[2]?data[2][\"count 2\"]:0)} </span>`\n , \"Devices <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '');\ncreateDataBlock(\n `<span style=\"color:rgb(25,128,56)\">${(data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0)} </span>`\n + \" | \" + \n `<span style=\"color:rgb(203,37,48)\">${(data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0)} </span>`\n , \"Connectors <span class='tb-hint' style='padding-left: 0'>(Active | Inactive)</span>\", '', '', connectorsIndex);\ncreateDataBlock(data[0].ALL_ERRORS_COUNT || 0, \"Errors\", (data[0].ALL_ERRORS_COUNT || 0) === 0 ? 'divider-green' : 'divider-red', '', logsIndex);\nreturn `<div fxLayout=\"row wrap\" fxLayoutGap=\"8px\" class=\"cards-container\">${blockData}</div>`;",
"applyDefaultMarkdownStyle": false, "applyDefaultMarkdownStyle": false,
"markdownCss": ".divider {\n position: absolute;\n width: 3px;\n top: 8px;\n border-radius: 2px;\n bottom: 8px;\n border: 1px solid rgba(31, 70, 144, 1);\n background-color: rgba(31, 70, 144, 1);\n left: 10px;\n}\n.divider-green .divider {\n border: 1px solid rgb(25,128,56);\n background-color: rgb(25,128,56);\n}\n\n.divider-green .mat-mdc-card-content {\n color: rgb(25,128,56);\n}\n\n.divider-red .divider {\n border: 1px solid rgb(203,37,48);\n background-color: rgb(203,37,48);\n}\n\n.divider-red .mat-mdc-card-content {\n color: rgb(203,37,48);\n}\n\n.mdc-card {\n position: relative;\n padding-left: 10px;\n margin-bottom: 1px;\n}\n\n.mat-mdc-card-subtitle {\n font-weight: 400;\n font-size: 12px;\n}\n\n.mat-mdc-card-header {\n padding: 8px 16px 0;\n}\n\n.mat-mdc-card-content:last-child {\n padding-bottom: 8px;\n font-size: 16px;\n}\n\n.cards-container {\n height: calc(100% - 1px);\n justify-content: stretch;\n align-items: center;\n margin-bottom: 1px;\n}" "markdownCss": ".divider {\n position: absolute;\n width: 3px;\n top: 8px;\n border-radius: 2px;\n bottom: 8px;\n border: 1px solid rgba(31, 70, 144, 1);\n background-color: rgba(31, 70, 144, 1);\n left: 10px;\n}\n.divider-green .divider {\n border: 1px solid rgb(25,128,56);\n background-color: rgb(25,128,56);\n}\n\n.divider-green .mat-mdc-card-content {\n color: rgb(25,128,56);\n}\n\n.divider-red .divider {\n border: 1px solid rgb(203,37,48);\n background-color: rgb(203,37,48);\n}\n\n.divider-red .mat-mdc-card-content {\n color: rgb(203,37,48);\n}\n\n.mdc-card {\n position: relative;\n padding-left: 10px;\n margin-bottom: 1px;\n}\n\n.mat-mdc-card-subtitle {\n font-weight: 400;\n font-size: 12px;\n}\n\n.mat-mdc-card-header {\n padding: 8px 16px 0;\n}\n\n.mat-mdc-card-content:last-child {\n padding-bottom: 8px;\n font-size: 16px;\n}\n\n.cards-container {\n height: calc(100% - 1px);\n justify-content: stretch;\n align-items: center;\n margin-bottom: 1px;\n}\n\n::ng-deep.tb-home-widget-link > div {\n flex-grow: 1;\n cursor: pointer;\n}\n\n .tb-home-widget-link {\n width: 100%;\n }\n\n .tb-home-widget-link:hover::after{\n color: inherit;\n }\n \n .tb-home-widget-link::after{\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px;\n}"
}, },
"title": "New Markdown/HTML Card", "title": "New Markdown/HTML Card",
"showTitleIcon": false, "showTitleIcon": false,
@ -594,7 +595,39 @@
"noDataDisplayMessage": "", "noDataDisplayMessage": "",
"enableDataExport": false, "enableDataExport": false,
"displayTimewindow": true, "displayTimewindow": true,
"borderRadius": "4px" "borderRadius": "4px",
"actions": {
"elementClick": [
{
"name": "Connecotrs",
"icon": "more_horiz",
"useShowWidgetActionFunction": null,
"showWidgetActionFunction": "return true;",
"type": "openDashboardState",
"targetDashboardStateId": "connectors",
"setEntityId": true,
"stateEntityParamName": null,
"openRightLayout": false,
"openInSeparateDialog": false,
"openInPopover": false,
"id": "ee7216d8-ad7a-20db-1abc-2531e221f24c"
},
{
"name": "Logs",
"icon": "more_horiz",
"useShowWidgetActionFunction": null,
"showWidgetActionFunction": "return true;",
"type": "openDashboardState",
"targetDashboardStateId": "logs",
"setEntityId": true,
"stateEntityParamName": null,
"openRightLayout": false,
"openInSeparateDialog": false,
"openInPopover": false,
"id": "ceec0446-c25f-2b9b-b1b1-58ed751476ff"
}
]
}
}, },
"row": 0, "row": 0,
"col": 0, "col": 0,
@ -784,7 +817,7 @@
"color": "rgba(0, 0, 0, 0.87)", "color": "rgba(0, 0, 0, 0.87)",
"padding": "8px", "padding": "8px",
"settings": {}, "settings": {},
"title": "Gateway Logs", "title": "Logs",
"showTitleIcon": false, "showTitleIcon": false,
"dropShadow": true, "dropShadow": true,
"enableFullscreen": true, "enableFullscreen": true,
@ -961,7 +994,7 @@
"customLegendEnabled": false, "customLegendEnabled": false,
"dataKeysListForLabels": [] "dataKeysListForLabels": []
}, },
"title": "Statistics", "title": "Gateway Custom Statistics",
"dropShadow": true, "dropShadow": true,
"enableFullscreen": true, "enableFullscreen": true,
"titleStyle": { "titleStyle": {
@ -1249,7 +1282,7 @@
"customLegendEnabled": false, "customLegendEnabled": false,
"dataKeysListForLabels": [] "dataKeysListForLabels": []
}, },
"title": "General Statistics", "title": "Gateway General Chart Statistics",
"dropShadow": true, "dropShadow": true,
"enableFullscreen": true, "enableFullscreen": true,
"titleStyle": { "titleStyle": {

2
application/src/main/data/json/system/widget_types/gateway_general_statistics.json

@ -20,4 +20,4 @@
"latestDataKeySettingsDirective": "tb-flot-latest-key-settings", "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Gateway general chart statistics\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Gateway general chart statistics\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
} }
} }

2
application/src/main/data/json/system/widget_types/gateway_statistics.json

@ -20,4 +20,4 @@
"latestDataKeySettingsDirective": "tb-flot-latest-key-settings", "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Gateway custom statistics \",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":false,\"tooltipIndividual\":false},\"title\":\"Gateway custom statistics \",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}"
} }
} }

6
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html

@ -549,9 +549,9 @@
<div class="tb-form-panel-title" translate>gateway.statistics.commands</div> <div class="tb-form-panel-title" translate>gateway.statistics.commands</div>
<div class="tb-form-panel-hint" translate>gateway.hints.commands</div> <div class="tb-form-panel-hint" translate>gateway.hints.commands</div>
<ng-container formGroupName="statistics"> <ng-container formGroupName="statistics">
<div fxLayout="row" formArrayName="commands" <div fxLayout="row" formArrayName="commands" class="statistics-container"
*ngFor="let commandControl of commandFormArray().controls; let $index = index"> *ngFor="let commandControl of commandFormArray().controls; let $index = index">
<section [formGroupName]="$index" class="tb-form-panel stroked no-padding-bottom no-gap"> <section [formGroupName]="$index" class="tb-form-panel stroked no-padding-bottom no-gap command-container">
<section class="tb-form-row no-border no-padding tb-not-inline-field column-xs"> <section class="tb-form-row no-border no-padding tb-not-inline-field column-xs">
<mat-form-field appearance="outline" class="flex"> <mat-form-field appearance="outline" class="flex">
<mat-label translate>gateway.statistics.attribute-name</mat-label> <mat-label translate>gateway.statistics.attribute-name</mat-label>
@ -612,7 +612,7 @@
<mat-tab label="{{ 'gateway.other' | translate }}"> <mat-tab label="{{ 'gateway.other' | translate }}">
<div formGroupName="thingsboard" class="mat-content mat-padding configuration-block"> <div formGroupName="thingsboard" class="mat-content mat-padding configuration-block">
<div class="tb-form-panel" formGroupName="checkingDeviceActivity" <div class="tb-form-panel" formGroupName="checkingDeviceActivity"
[class.no-padding-bottom]="gatewayConfigGroup.get('thingsboard.checkingDeviceActivity.checkDeviceInactivity').value"> [class.no-padding-bottom]="gatewayConfigGroup.get('thingsboard.checkingDeviceActivity.checkDeviceInactivity').value">
<div tb-hint-tooltip-icon="{{ 'gateway.hints.check-device-activity' | translate }}" <div tb-hint-tooltip-icon="{{ 'gateway.hints.check-device-activity' | translate }}"
class="tb-form-row no-border no-padding"> class="tb-form-row no-border no-padding">
<mat-slide-toggle class="mat-slide" color="primary" formControlName="checkDeviceInactivity"> <mat-slide-toggle class="mat-slide" color="primary" formControlName="checkDeviceInactivity">

8
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss

@ -54,6 +54,14 @@
flex: 1; flex: 1;
} }
.statistics-container {
width: 50%;
.command-container {
width: 100%;
}
}
.actions { .actions {
grid-row: 3; grid-row: 3;
padding: 8px; padding: 8px;

53
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.ts

@ -48,6 +48,7 @@ import {
StorageTypes, StorageTypes,
StorageTypesTranslationMap StorageTypesTranslationMap
} from './gateway-widget.models'; } from './gateway-widget.models';
import { deepTrim } from '@core/utils';
@Component({ @Component({
selector: 'tb-gateway-configuration', selector: 'tb-gateway-configuration',
@ -91,7 +92,7 @@ export class GatewayConfigurationComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.gatewayConfigGroup = this.fb.group({ this.gatewayConfigGroup = this.fb.group({
thingsboard: this.fb.group({ thingsboard: this.fb.group({
host: [window.location.hostname, [Validators.required]], host: [window.location.hostname, [Validators.required, Validators.pattern(/^[^\s]+$/)]],
port: [1883, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]], port: [1883, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]],
remoteShell: [false, []], remoteShell: [false, []],
remoteConfiguration: [true, []], remoteConfiguration: [true, []],
@ -107,30 +108,30 @@ export class GatewayConfigurationComponent implements OnInit {
handleDeviceRenaming: [true, []], handleDeviceRenaming: [true, []],
checkingDeviceActivity: this.fb.group({ checkingDeviceActivity: this.fb.group({
checkDeviceInactivity: [false, []], checkDeviceInactivity: [false, []],
inactivityTimeoutSeconds: [200, [Validators.min(1)]], inactivityTimeoutSeconds: [200, [Validators.min(1), Validators.pattern(/^[^.\s]+$/)]],
inactivityCheckPeriodSeconds: [500, [Validators.min(1)]] inactivityCheckPeriodSeconds: [500, [Validators.min(1), Validators.pattern(/^[^.\s]+$/)]]
}), }),
security: this.fb.group({ security: this.fb.group({
type: [SecurityTypes.ACCESS_TOKEN, [Validators.required]], type: [SecurityTypes.ACCESS_TOKEN, [Validators.required]],
accessToken: [null, [Validators.required]], accessToken: [null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]],
clientId: [null, []], clientId: [null, [Validators.pattern(/^[^.\s]+$/)]],
username: [null, []], username: [null, [Validators.pattern(/^[^.\s]+$/)]],
password: [null, []], password: [null, [Validators.pattern(/^[^.\s]+$/)]],
caCert: [null, []], caCert: [null, []],
cert: [null, []], cert: [null, []],
privateKey: [null, []], privateKey: [null, []],
}), }),
qos: [1, [Validators.min(0), Validators.max(1), Validators.required]] qos: [1, [Validators.min(0), Validators.max(1), Validators.required, Validators.pattern(/^[^.\s]+$/)]]
}), }),
storage: this.fb.group({ storage: this.fb.group({
type: [StorageTypes.MEMORY, [Validators.required]], type: [StorageTypes.MEMORY, [Validators.required]],
read_records_count: [100, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]], read_records_count: [100, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]],
max_records_count: [100000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]], max_records_count: [100000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required, Validators.pattern(/^[^.\s]+$/)]],
data_folder_path: ['./data/', []], data_folder_path: ['./data/', [Validators.pattern(/^[^\s]+$/)]],
max_file_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], max_file_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
max_read_records_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], max_read_records_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
max_records_per_file: [10000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], max_records_per_file: [10000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
data_file_path: ['./data/data.db', []], data_file_path: ['./data/data.db', [Validators.pattern(/^[^\s]+$/)]],
messages_ttl_check_in_hours: [1, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], messages_ttl_check_in_hours: [1, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
messages_ttl_in_days: [7, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], messages_ttl_in_days: [7, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]],
@ -147,9 +148,9 @@ export class GatewayConfigurationComponent implements OnInit {
}), }),
connectors: this.fb.array([]), connectors: this.fb.array([]),
logs: this.fb.group({ logs: this.fb.group({
dateFormat: ['%Y-%m-%d %H:%M:%S', [Validators.required]], dateFormat: ['%Y-%m-%d %H:%M:%S', [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]],
logFormat: ['%(asctime)s - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s', logFormat: ['%(asctime)s - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s',
[Validators.required]], [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]],
type: ['remote', [Validators.required]], type: ['remote', [Validators.required]],
remote: this.fb.group({ remote: this.fb.group({
enabled: [false], enabled: [false],
@ -204,7 +205,7 @@ export class GatewayConfigurationComponent implements OnInit {
securityGroup.get('type').valueChanges.subscribe(type => { securityGroup.get('type').valueChanges.subscribe(type => {
this.removeAllSecurityValidators(); this.removeAllSecurityValidators();
if (type === SecurityTypes.ACCESS_TOKEN) { if (type === SecurityTypes.ACCESS_TOKEN) {
securityGroup.get('accessToken').addValidators([Validators.required]); securityGroup.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
securityGroup.get('accessToken').updateValueAndValidity(); securityGroup.get('accessToken').updateValueAndValidity();
} else if (type === SecurityTypes.TLS_PRIVATE_KEY) { } else if (type === SecurityTypes.TLS_PRIVATE_KEY) {
securityGroup.get('caCert').addValidators([Validators.required]); securityGroup.get('caCert').addValidators([Validators.required]);
@ -214,7 +215,7 @@ export class GatewayConfigurationComponent implements OnInit {
securityGroup.get('cert').addValidators([Validators.required]); securityGroup.get('cert').addValidators([Validators.required]);
securityGroup.get('cert').updateValueAndValidity(); securityGroup.get('cert').updateValueAndValidity();
} else if (type === SecurityTypes.TLS_ACCESS_TOKEN) { } else if (type === SecurityTypes.TLS_ACCESS_TOKEN) {
securityGroup.get('accessToken').addValidators([Validators.required]); securityGroup.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
securityGroup.get('accessToken').updateValueAndValidity(); securityGroup.get('accessToken').updateValueAndValidity();
securityGroup.get('caCert').addValidators([Validators.required]); securityGroup.get('caCert').addValidators([Validators.required]);
securityGroup.get('caCert').updateValueAndValidity(); securityGroup.get('caCert').updateValueAndValidity();
@ -239,7 +240,7 @@ export class GatewayConfigurationComponent implements OnInit {
storageGroup.get('read_records_count').updateValueAndValidity({emitEvent: false}); storageGroup.get('read_records_count').updateValueAndValidity({emitEvent: false});
storageGroup.get('max_records_count').updateValueAndValidity({emitEvent: false}); storageGroup.get('max_records_count').updateValueAndValidity({emitEvent: false});
} else if (type === StorageTypes.FILE) { } else if (type === StorageTypes.FILE) {
storageGroup.get('data_folder_path').addValidators([Validators.required]); storageGroup.get('data_folder_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
storageGroup.get('max_file_count').addValidators( storageGroup.get('max_file_count').addValidators(
[Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]);
storageGroup.get('max_read_records_count').addValidators( storageGroup.get('max_read_records_count').addValidators(
@ -251,7 +252,7 @@ export class GatewayConfigurationComponent implements OnInit {
storageGroup.get('max_read_records_count').updateValueAndValidity({emitEvent: false}); storageGroup.get('max_read_records_count').updateValueAndValidity({emitEvent: false});
storageGroup.get('max_records_per_file').updateValueAndValidity({emitEvent: false}); storageGroup.get('max_records_per_file').updateValueAndValidity({emitEvent: false});
} else if (type === StorageTypes.SQLITE) { } else if (type === StorageTypes.SQLITE) {
storageGroup.get('data_file_path').addValidators([Validators.required]); storageGroup.get('data_file_path').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]);
storageGroup.get('messages_ttl_check_in_hours').addValidators( storageGroup.get('messages_ttl_check_in_hours').addValidators(
[Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]); [Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.required]);
storageGroup.get('messages_ttl_in_days').addValidators( storageGroup.get('messages_ttl_in_days').addValidators(
@ -342,8 +343,8 @@ export class GatewayConfigurationComponent implements OnInit {
this.initialCredentials = credentials; this.initialCredentials = credentials;
if (credentials.credentialsType === DeviceCredentialsType.ACCESS_TOKEN || security.type === SecurityTypes.TLS_ACCESS_TOKEN) { 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 this.gatewayConfigGroup.get('thingsboard.security.type').setValue(security.type === SecurityTypes.TLS_ACCESS_TOKEN
? SecurityTypes.TLS_ACCESS_TOKEN ? SecurityTypes.TLS_ACCESS_TOKEN
: SecurityTypes.ACCESS_TOKEN); : SecurityTypes.ACCESS_TOKEN);
this.gatewayConfigGroup.get('thingsboard.security.accessToken').setValue(credentials.credentialsId); this.gatewayConfigGroup.get('thingsboard.security.accessToken').setValue(credentials.credentialsId);
if(security.type === SecurityTypes.TLS_ACCESS_TOKEN) { if(security.type === SecurityTypes.TLS_ACCESS_TOKEN) {
this.gatewayConfigGroup.get('thingsboard.security.caCert').setValue(security.caCert); this.gatewayConfigGroup.get('thingsboard.security.caCert').setValue(security.caCert);
@ -407,9 +408,9 @@ export class GatewayConfigurationComponent implements OnInit {
addCommand(command: any = {}): void { addCommand(command: any = {}): void {
const commandsFormArray = this.commandFormArray(); const commandsFormArray = this.commandFormArray();
const commandFormGroup = this.fb.group({ const commandFormGroup = this.fb.group({
attributeOnGateway: [command.attributeOnGateway || null, [Validators.required]], attributeOnGateway: [command.attributeOnGateway || null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]],
command: [command.command || null, [Validators.required]], command: [command.command || null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]],
timeout: [command.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], timeout: [command.timeout || null, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.pattern(/^[^.\s]+$/)]],
}); });
commandsFormArray.push(commandFormGroup); commandsFormArray.push(commandFormGroup);
} }
@ -542,7 +543,7 @@ export class GatewayConfigurationComponent implements OnInit {
} }
saveConfig(): void { saveConfig(): void {
const value = this.removeEmpty(this.gatewayConfigGroup.value); const value = deepTrim(this.removeEmpty(this.gatewayConfigGroup.value));
value.thingsboard.statistics.commands = Object.values(value.thingsboard.statistics.commands); value.thingsboard.statistics.commands = Object.values(value.thingsboard.statistics.commands);
const attributes = []; const attributes = [];
attributes.push({ attributes.push({
@ -609,8 +610,8 @@ export class GatewayConfigurationComponent implements OnInit {
credentialsValue.password = securityConfig.password; credentialsValue.password = securityConfig.password;
} }
newCredentials = { newCredentials = {
credentialsType, credentialsType,
credentialsValue: JSON.stringify(credentialsValue) credentialsValue: JSON.stringify(credentialsValue)
}; };
} }
} else if (securityConfig.type === SecurityTypes.ACCESS_TOKEN || securityConfig.type === SecurityTypes.TLS_ACCESS_TOKEN) { } else if (securityConfig.type === SecurityTypes.ACCESS_TOKEN || securityConfig.type === SecurityTypes.TLS_ACCESS_TOKEN) {

4
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html

@ -25,13 +25,13 @@
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()"
matSortDisableClear> matSortDisableClear>
<ng-container matColumnDef="ts"> <ng-container matColumnDef="ts">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'widgets.gateway.created-time' | translate }}</mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header style="width: 20%">{{ 'widgets.gateway.created-time' | translate }}</mat-header-cell>
<mat-cell *matCellDef="let attribute"> <mat-cell *matCellDef="let attribute">
{{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss' }} {{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss' }}
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<ng-container matColumnDef="status"> <ng-container matColumnDef="status">
<mat-header-cell *matHeaderCellDef mat-sort-header>{{ 'widgets.gateway.status' | translate }}</mat-header-cell> <mat-header-cell *matHeaderCellDef mat-sort-header style="width: 10%">{{ 'widgets.gateway.status' | translate }}</mat-header-cell>
<mat-cell *matCellDef="let attribute"> <mat-cell *matCellDef="let attribute">
<span [class]="statusClass(attribute.status)">{{ attribute.status }}</span> <span [class]="statusClass(attribute.status)">{{ attribute.status }}</span>
</mat-cell> </mat-cell>

10
ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts

@ -50,6 +50,7 @@ export class GatewayStatisticsComponent implements AfterViewInit {
public general: boolean; public general: boolean;
public isNumericData = false; public isNumericData = false;
public dataTypeDefined: boolean = false;
public chartInited: boolean; public chartInited: boolean;
private flot: TbFlot; private flot: TbFlot;
private flotCtx: WidgetContext; private flotCtx: WidgetContext;
@ -155,6 +156,7 @@ export class GatewayStatisticsComponent implements AfterViewInit {
subscriptionInfo[0].timeseries = [{name: attr, label: attr}]; subscriptionInfo[0].timeseries = [{name: attr, label: attr}];
this.subscriptionInfo = subscriptionInfo; this.subscriptionInfo = subscriptionInfo;
this.changeSubscription(subscriptionInfo); this.changeSubscription(subscriptionInfo);
this.ctx.defaultSubscription.unsubscribe();
} }
private createGeneralChartsSubscription(gateway: BaseData<EntityId>, attrData: string[]) { private createGeneralChartsSubscription(gateway: BaseData<EntityId>, attrData: string[]) {
@ -175,8 +177,8 @@ export class GatewayStatisticsComponent implements AfterViewInit {
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); this.changeSubscription(subscriptionInfo);
this.ctx.defaultSubscription.unsubscribe();
} }
private init = () => { private init = () => {
@ -252,7 +254,10 @@ export class GatewayStatisticsComponent implements AfterViewInit {
return; return;
} }
this.dataSource.data = this.subscription.data.length ? this.subscription.data[0].data : []; this.dataSource.data = this.subscription.data.length ? this.subscription.data[0].data : [];
this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1])); if (this.dataSource.data.length && !this.dataTypeDefined) {
this.dataTypeDefined = true;
this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1]));
}
} }
@ -263,6 +268,7 @@ export class GatewayStatisticsComponent implements AfterViewInit {
if (this.ctx.datasources[0].entity) { if (this.ctx.datasources[0].entity) {
this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions, this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions,
false, true).subscribe(subscription => { false, true).subscribe(subscription => {
this.dataTypeDefined = false;
this.subscription = subscription; this.subscription = subscription;
this.isDataOnlyNumbers(); this.isDataOnlyNumbers();
this.legendData = this.subscription.legendData; this.legendData = this.subscription.legendData;

4
ui-ngx/src/app/modules/home/components/widget/lib/settings/gateway/gateway-logs-settings.component.ts

@ -41,14 +41,14 @@ export class GatewayLogsSettingsComponent extends WidgetSettingsComponent {
protected defaultSettings(): WidgetSettings { protected defaultSettings(): WidgetSettings {
return { return {
isConnectorLog: false, isConnectorLog: false,
connectorLogState: 'default' connectorLogState: ''
}; };
} }
protected onSettingsSet(settings: WidgetSettings) { protected onSettingsSet(settings: WidgetSettings) {
this.gatewayLogSettingForm = this.fb.group({ this.gatewayLogSettingForm = this.fb.group({
isConnectorLog: [false, []], isConnectorLog: [false, []],
connectorLogState: ['default', Validators.required] connectorLogState: ['', Validators.required]
}); });
} }

6
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2683,7 +2683,7 @@
"rpc-command-result": "Result", "rpc-command-result": "Result",
"rpc-command-edit-params": "Edit parameters", "rpc-command-edit-params": "Edit parameters",
"select-connector": "Select connector", "select-connector": "Select connector",
"gateway-configuration": "Gateway Configuration", "gateway-configuration": "General Configuration",
"docker-label": "In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.", "docker-label": "In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.",
"copy-command": "Copy docker command", "copy-command": "Copy docker command",
"create-new-gateway": "Create a new gateway", "create-new-gateway": "Create a new gateway",
@ -5329,8 +5329,8 @@
"events-title": "Gateway events form title", "events-title": "Gateway events form title",
"events-filter": "Events filter", "events-filter": "Events filter",
"event-key-contains": "Event key contains...", "event-key-contains": "Event key contains...",
"is-connector": "Is Connector", "is-connector": "Show for the connector",
"state-param-name": "State parameter connector name", "state-param-name": "Connector state parameter key",
"status": "Status", "status": "Status",
"message": "Message", "message": "Message",
"created-time": "Created time" "created-time": "Created time"

Loading…
Cancel
Save