connector status and new gateway cards

This commit is contained in:
Maksym Dudnik 2023-07-31 12:24:07 +03:00
parent b94a55944c
commit 67031e43da
6 changed files with 100 additions and 5 deletions

View File

@ -479,6 +479,14 @@
"funcBody": null, "funcBody": null,
"usePostProcessing": true, "usePostProcessing": true,
"postFuncBody": "var newValue = value == 'true' ? \"Active\" : \"Inactive\";\nreturn newValue;" "postFuncBody": "var newValue = value == 'true' ? \"Active\" : \"Inactive\";\nreturn newValue;"
},
{
"name": "ALL_ERRORS_COUNT",
"type": "timeseries",
"label": "ALL_ERRORS_COUNT",
"color": "#ffeb3b",
"settings": {},
"_hash": 0.2770587478725004
} }
], ],
"alarmFilterConfig": { "alarmFilterConfig": {
@ -550,7 +558,7 @@
"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((data[1]?data[1].count:0) + \" | \" + (data[2]?data[2][\"count 2\"]:0), \"Devices\", '');\ncreateDataBlock((data[0].active_connectors?JSON.parse(data[0].active_connectors).length:0) + \" | \" + (data[0].inactive_connectors?JSON.parse(data[0].inactive_connectors).length:0), \"Connectors\", '');\nreturn `<div fxLayout=\"row wrap\" fxLayoutGap=\"8px\" class=\"cards-container\">${blockData}</div>`;", "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>`;",
"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}"
}, },

View File

@ -69,6 +69,20 @@
}">{{isConnectorSynced(attribute)?'sync' : 'out of sync'}}</div> }">{{isConnectorSynced(attribute)?'sync' : 'out of sync'}}</div>
</mat-cell> </mat-cell>
</ng-container> </ng-container>
<ng-container matColumnDef="errors">
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 30%">
{{ 'gateway.connectors-table-status' | translate }}
</mat-header-cell>
<mat-cell *matCellDef="let attribute" style="text-transform: uppercase">
<span class="dot"
matTooltip="{{getErrorsCount(attribute)}}"
matTooltipPosition="above"
[ngClass]="{
'hasErrors': +getErrorsCount(attribute) > 0,
'noErrors': +getErrorsCount(attribute) == 0 || getErrorsCount(attribute) == ''
}"></span>
</mat-cell>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd> <ng-container matColumnDef="actions" stickyEnd>
<mat-header-cell *matHeaderCellDef <mat-header-cell *matHeaderCellDef
[ngStyle.gt-md]="{ minWidth: '144px', maxWidth: '144px', width: '144px'}"> [ngStyle.gt-md]="{ minWidth: '144px', maxWidth: '144px', width: '144px'}">

View File

@ -90,6 +90,22 @@
mat-row { mat-row {
cursor: pointer; cursor: pointer;
} }
.dot {
height: 25px;
width: 25px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
}
.hasErrors {
background-color: rgb(203, 37, 48);
}
.noErrors {
background-color: rgb(25, 128, 56);
}
} }
:host ::ng-deep tb-json-object-edit > div { :host ::ng-deep tb-json-object-edit > div {

View File

@ -39,6 +39,10 @@ import { DialogService } from '@core/services/dialog.service';
import { WidgetContext } from '@home/models/widget-component.models'; import { WidgetContext } from '@home/models/widget-component.models';
import { deepClone } from '@core/utils'; import { deepClone } from '@core/utils';
import { NULL_UUID } from '@shared/models/id/has-uuid'; 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 { export interface gatewayConnector {
@ -88,7 +92,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
dataSource: MatTableDataSource<AttributeData>; dataSource: MatTableDataSource<AttributeData>;
displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'actions']; displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions'];
gatewayConnectorDefaultTypes = GatewayConnectorDefaultTypesTranslates; gatewayConnectorDefaultTypes = GatewayConnectorDefaultTypesTranslates;
@ -123,6 +127,19 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
initialConnector: gatewayConnector; initialConnector: gatewayConnector;
subscriptionOptions: WidgetSubscriptionOptions = {
callbacks: {
onDataUpdated: () => this.ctx.ngZone.run(() => {
this.onDataUpdated();
}),
onDataUpdateError: (subscription, e) => this.ctx.ngZone.run(() => {
this.onDataUpdateError(e);
})
}
};
subscription: IWidgetSubscription;
constructor(protected router: Router, constructor(protected router: Router,
protected store: Store<AppState>, protected store: Store<AppState>,
protected fb: FormBuilder, protected fb: FormBuilder,
@ -132,6 +149,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
protected dialogService: DialogService, protected dialogService: DialogService,
private telemetryWsService: TelemetryWebsocketService, private telemetryWsService: TelemetryWebsocketService,
private zone: NgZone, private zone: NgZone,
private utils: UtilsService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
public dialog: MatDialog) { public dialog: MatDialog) {
super(store); super(store);
@ -270,6 +288,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
this.attributeDataSource.loadAttributes(this.device, AttributeScope.CLIENT_SCOPE, this.pageLink, reload).subscribe(data => { this.attributeDataSource.loadAttributes(this.device, AttributeScope.CLIENT_SCOPE, this.pageLink, reload).subscribe(data => {
this.activeData = data.data.filter(value => this.activeConnectors.includes(value.key)); this.activeData = data.data.filter(value => this.activeConnectors.includes(value.key));
this.combineData(); this.combineData();
this.generateSubscription();
}); });
this.inactiveConnectorsDataSource.loadAttributes(this.device, AttributeScope.SHARED_SCOPE, this.pageLink, reload).subscribe(data => { this.inactiveConnectorsDataSource.loadAttributes(this.device, AttributeScope.SHARED_SCOPE, this.pageLink, reload).subscribe(data => {
this.sharedAttributeData = data.data.filter(value => this.activeConnectors.includes(value.key)); this.sharedAttributeData = data.data.filter(value => this.activeConnectors.includes(value.key));
@ -279,7 +298,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
this.inactiveData = data.data.filter(value => this.inactiveConnectors.includes(value.key)); this.inactiveData = data.data.filter(value => this.inactiveConnectors.includes(value.key));
this.combineData(); this.combineData();
}); });
} }
isConnectorSynced(attribute: AttributeData) { isConnectorSynced(attribute: AttributeData) {
@ -457,4 +475,43 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
}); });
} }
onDataUpdateError(e: any) {
const exceptionData = this.utils.parseException(e);
let errorText = exceptionData.name;
if (exceptionData.message) {
errorText += ': ' + exceptionData.message;
}
console.error(errorText);
}
onDataUpdated() {
this.cd.detectChanges();
}
generateSubscription() {
if (this.subscription) {
this.subscription.unsubscribe();
}
if (this.device) {
const subscriptionInfo = [{
type: DatasourceType.entity,
entityType: EntityType.DEVICE,
entityId: this.device.id,
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 => {
this.subscription = subscription;
});
}
}
getErrorsCount(attribute) {
const connectorName = attribute.key;
const connector = this.subscription.data.find(data=>data && data.dataKey.name === `${connectorName}_ERRORS_COUNT`);
return (connector && this.activeConnectors.includes(connectorName))? connector.data[0][1]: 'inactive';
}
} }

View File

@ -219,7 +219,7 @@ export class GatewayLogsComponent extends PageComponent implements AfterViewInit
} }
changeSubscription() { changeSubscription() {
if (this.ctx.datasources[0].entity) { if (this.ctx.datasources && this.ctx.datasources[0].entity && this.ctx.defaultSubscription.options.datasources) {
this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{
name: this.activeLink.key, name: this.activeLink.key,
type: DataKeyType.timeseries, type: DataKeyType.timeseries,

View File

@ -2617,7 +2617,7 @@
"connectors-table-enabled": "Enabled", "connectors-table-enabled": "Enabled",
"connectors-table-name": "Name", "connectors-table-name": "Name",
"connectors-table-type": "Type", "connectors-table-type": "Type",
"connectors-table-status": "Sync status", "connectors-table-status": "Status",
"connectors-table-actions": "Actions", "connectors-table-actions": "Actions",
"connectors-table-key": "Key", "connectors-table-key": "Key",
"connectors-table-class": "Class", "connectors-table-class": "Class",