Fixed Issue of viewing Out of Sync Connector and gateway-connectors component refactoring
This commit is contained in:
parent
bce943cb5b
commit
4d992bff70
@ -154,15 +154,15 @@
|
|||||||
<section [formGroup]="connectorForm" class="tb-form-panel section-container flex">
|
<section [formGroup]="connectorForm" class="tb-form-panel section-container flex">
|
||||||
<div class="tb-form-panel-title tb-flex no-flex space-between align-center">
|
<div class="tb-form-panel-title tb-flex no-flex space-between align-center">
|
||||||
<div class="tb-form-panel-title">
|
<div class="tb-form-panel-title">
|
||||||
{{ initialConnector?.type ? gatewayConnectorDefaultTypes.get(initialConnector.type) : '' }}
|
{{ initialConnector?.type ? GatewayConnectorTypesTranslatesMap.get(initialConnector.type) : '' }}
|
||||||
{{ 'gateway.configuration' | translate }}
|
{{ 'gateway.configuration' | translate }}
|
||||||
</div>
|
</div>
|
||||||
<tb-toggle-select *ngIf="initialConnector && allowBasicConfig.has(initialConnector.type)"
|
<tb-toggle-select *ngIf="initialConnector && allowBasicConfig.has(initialConnector.type)"
|
||||||
formControlName="mode" appearance="fill">
|
formControlName="mode" appearance="fill">
|
||||||
<tb-toggle-option [value]="connectorConfigurationModes.BASIC">
|
<tb-toggle-option [value]="ConnectorConfigurationModes.BASIC">
|
||||||
{{ 'gateway.basic' | translate }}
|
{{ 'gateway.basic' | translate }}
|
||||||
</tb-toggle-option>
|
</tb-toggle-option>
|
||||||
<tb-toggle-option [value]="connectorConfigurationModes.ADVANCED">
|
<tb-toggle-option [value]="ConnectorConfigurationModes.ADVANCED">
|
||||||
{{ 'gateway.advanced' | translate }}
|
{{ 'gateway.advanced' | translate }}
|
||||||
</tb-toggle-option>
|
</tb-toggle-option>
|
||||||
</tb-toggle-select>
|
</tb-toggle-select>
|
||||||
@ -173,17 +173,17 @@
|
|||||||
gateway.select-connector
|
gateway.select-connector
|
||||||
</span>
|
</span>
|
||||||
<section class="tb-form-panel section-container no-border no-padding tb-flex space-between" *ngIf="initialConnector">
|
<section class="tb-form-panel section-container no-border no-padding tb-flex space-between" *ngIf="initialConnector">
|
||||||
<ng-container *ngIf="connectorForm.get('mode')?.value === connectorConfigurationModes.BASIC else defaultConfig">
|
<ng-container *ngIf="connectorForm.get('mode')?.value === ConnectorConfigurationModes.BASIC else defaultConfig">
|
||||||
<ng-container [ngSwitch]="initialConnector.type">
|
<ng-container [ngSwitch]="initialConnector.type">
|
||||||
<tb-mqtt-basic-config *ngSwitchCase="connectorType.MQTT"
|
<tb-mqtt-basic-config *ngSwitchCase="ConnectorType.MQTT"
|
||||||
formControlName="basicConfig"
|
formControlName="basicConfig"
|
||||||
[generalTabContent]="generalTabContent">
|
[generalTabContent]="generalTabContent">
|
||||||
</tb-mqtt-basic-config>
|
</tb-mqtt-basic-config>
|
||||||
<tb-opc-ua-basic-config *ngSwitchCase="connectorType.OPCUA"
|
<tb-opc-ua-basic-config *ngSwitchCase="ConnectorType.OPCUA"
|
||||||
formControlName="basicConfig"
|
formControlName="basicConfig"
|
||||||
[generalTabContent]="generalTabContent">
|
[generalTabContent]="generalTabContent">
|
||||||
</tb-opc-ua-basic-config>
|
</tb-opc-ua-basic-config>
|
||||||
<tb-modbus-basic-config *ngSwitchCase="connectorType.MODBUS"
|
<tb-modbus-basic-config *ngSwitchCase="ConnectorType.MODBUS"
|
||||||
formControlName="basicConfig"
|
formControlName="basicConfig"
|
||||||
[generalTabContent]="generalTabContent">
|
[generalTabContent]="generalTabContent">
|
||||||
</tb-modbus-basic-config>
|
</tb-modbus-basic-config>
|
||||||
@ -237,7 +237,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="connectorForm.get('type').value === connectorType.CUSTOM" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
<div *ngIf="connectorForm.get('type').value === ConnectorType.CUSTOM" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||||
<div class="fixed-title-width" translate>gateway.connectors-table-class</div>
|
<div class="fixed-title-width" translate>gateway.connectors-table-class</div>
|
||||||
<div class="tb-flex no-gap">
|
<div class="tb-flex no-gap">
|
||||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||||
@ -245,7 +245,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="connectorForm.get('type').value === connectorType.GRPC" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
<div *ngIf="connectorForm.get('type').value === ConnectorType.GRPC" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||||
<div class="fixed-title-width" translate>gateway.connectors-table-key</div>
|
<div class="fixed-title-width" translate>gateway.connectors-table-key</div>
|
||||||
<div class="tb-flex no-gap">
|
<div class="tb-flex no-gap">
|
||||||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic">
|
||||||
@ -273,7 +273,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="connectorForm.get('type').value === connectorType.MQTT" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
<div *ngIf="connectorForm.get('type').value === ConnectorType.MQTT" class="tb-form-row column-xs" fxLayoutAlign="space-between center">
|
||||||
<mat-slide-toggle class="mat-slide" formControlName="sendDataOnlyOnChange">
|
<mat-slide-toggle class="mat-slide" formControlName="sendDataOnlyOnChange">
|
||||||
<mat-label tb-hint-tooltip-icon="{{ 'gateway.send-change-data-hint' | translate }}">
|
<mat-label tb-hint-tooltip-icon="{{ 'gateway.send-change-data-hint' | translate }}">
|
||||||
{{ 'gateway.send-change-data' | translate }}
|
{{ 'gateway.send-change-data' | translate }}
|
||||||
|
|||||||
@ -81,61 +81,39 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
ctx: WidgetContext;
|
ctx: WidgetContext;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
device: EntityId;
|
device: EntityId;
|
||||||
|
|
||||||
@ViewChild('nameInput') nameInput: ElementRef;
|
@ViewChild('nameInput') nameInput: ElementRef;
|
||||||
@ViewChild(MatSort, {static: false}) sort: MatSort;
|
@ViewChild(MatSort, {static: false}) sort: MatSort;
|
||||||
|
|
||||||
pageLink: PageLink;
|
readonly ConnectorType = ConnectorType;
|
||||||
|
readonly allowBasicConfig = new Set<ConnectorType>([
|
||||||
connectorType = ConnectorType;
|
|
||||||
|
|
||||||
allowBasicConfig = new Set<ConnectorType>([
|
|
||||||
ConnectorType.MQTT,
|
ConnectorType.MQTT,
|
||||||
ConnectorType.OPCUA,
|
ConnectorType.OPCUA,
|
||||||
ConnectorType.MODBUS,
|
ConnectorType.MODBUS,
|
||||||
]);
|
]);
|
||||||
|
readonly gatewayLogLevel = Object.values(GatewayLogLevel);
|
||||||
|
readonly displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions'];
|
||||||
|
readonly GatewayConnectorTypesTranslatesMap = GatewayConnectorDefaultTypesTranslatesMap;
|
||||||
|
readonly ConnectorConfigurationModes = ConnectorConfigurationModes;
|
||||||
|
|
||||||
gatewayLogLevel = Object.values(GatewayLogLevel);
|
pageLink: PageLink;
|
||||||
|
|
||||||
dataSource: MatTableDataSource<AttributeData>;
|
dataSource: MatTableDataSource<AttributeData>;
|
||||||
|
|
||||||
displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions'];
|
|
||||||
|
|
||||||
gatewayConnectorDefaultTypes = GatewayConnectorDefaultTypesTranslatesMap;
|
|
||||||
|
|
||||||
connectorConfigurationModes = ConnectorConfigurationModes;
|
|
||||||
|
|
||||||
connectorForm: FormGroup;
|
connectorForm: FormGroup;
|
||||||
|
|
||||||
textSearchMode: boolean;
|
|
||||||
|
|
||||||
activeConnectors: Array<string>;
|
activeConnectors: Array<string>;
|
||||||
|
mode: ConnectorConfigurationModes = this.ConnectorConfigurationModes.BASIC;
|
||||||
mode: ConnectorConfigurationModes = this.connectorConfigurationModes.BASIC;
|
|
||||||
|
|
||||||
initialConnector: GatewayConnector;
|
initialConnector: GatewayConnector;
|
||||||
|
|
||||||
private inactiveConnectors: Array<string>;
|
private inactiveConnectors: Array<string>;
|
||||||
|
|
||||||
private attributeDataSource: AttributeDatasource;
|
private attributeDataSource: AttributeDatasource;
|
||||||
|
|
||||||
private inactiveConnectorsDataSource: AttributeDatasource;
|
private inactiveConnectorsDataSource: AttributeDatasource;
|
||||||
|
|
||||||
private serverDataSource: AttributeDatasource;
|
private serverDataSource: AttributeDatasource;
|
||||||
|
|
||||||
private activeData: Array<any> = [];
|
private activeData: Array<any> = [];
|
||||||
|
|
||||||
private inactiveData: Array<any> = [];
|
private inactiveData: Array<any> = [];
|
||||||
|
|
||||||
private sharedAttributeData: Array<AttributeData> = [];
|
private sharedAttributeData: Array<AttributeData> = [];
|
||||||
|
|
||||||
private basicConfigSub: Subscription;
|
private basicConfigSub: Subscription;
|
||||||
|
|
||||||
private jsonConfigSub: Subscription;
|
private jsonConfigSub: Subscription;
|
||||||
|
|
||||||
private subscriptionOptions: WidgetSubscriptionOptions = {
|
private subscriptionOptions: WidgetSubscriptionOptions = {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
onDataUpdated: () => this.ctx.ngZone.run(() => {
|
onDataUpdated: () => this.ctx.ngZone.run(() => {
|
||||||
@ -146,7 +124,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
private subscription: IWidgetSubscription;
|
private subscription: IWidgetSubscription;
|
||||||
private attributeUpdateSubject = new Subject<AttributeData>();
|
private attributeUpdateSubject = new Subject<AttributeData>();
|
||||||
@ -162,101 +139,19 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
private utils: UtilsService,
|
private utils: UtilsService,
|
||||||
private cd: ChangeDetectorRef) {
|
private cd: ChangeDetectorRef) {
|
||||||
super(store);
|
super(store);
|
||||||
const sortOrder: SortOrder = {property: 'key', direction: Direction.ASC};
|
|
||||||
this.pageLink = new PageLink(1000, 0, null, sortOrder);
|
|
||||||
this.attributeDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate);
|
|
||||||
this.inactiveConnectorsDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate);
|
|
||||||
this.serverDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate);
|
|
||||||
this.dataSource = new MatTableDataSource<AttributeData>([]);
|
|
||||||
this.connectorForm = this.fb.group({
|
|
||||||
mode: [ConnectorConfigurationModes.BASIC, []],
|
|
||||||
name: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(noLeadTrailSpacesRegex)]],
|
|
||||||
type: ['', [Validators.required]],
|
|
||||||
enableRemoteLogging: [false, []],
|
|
||||||
logLevel: ['', [Validators.required]],
|
|
||||||
sendDataOnlyOnChange: [false, []],
|
|
||||||
key: ['auto'],
|
|
||||||
class: [''],
|
|
||||||
configuration: [''],
|
|
||||||
configurationJson: [{}, [Validators.required]],
|
|
||||||
basicConfig: [{}]
|
|
||||||
});
|
|
||||||
this.connectorForm.disable();
|
|
||||||
|
|
||||||
|
this.initDataSources();
|
||||||
|
this.initConnectorForm();
|
||||||
this.observeAttributeChange();
|
this.observeAttributeChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.connectorForm.get('type').valueChanges.pipe(
|
this.observeName();
|
||||||
takeUntil(this.destroy$)
|
|
||||||
).subscribe(type => {
|
|
||||||
if (type && !this.initialConnector) {
|
|
||||||
this.attributeService.getEntityAttributes(this.device, AttributeScope.CLIENT_SCOPE,
|
|
||||||
[`${type.toUpperCase()}_DEFAULT_CONFIG`], {ignoreErrors: true}).subscribe(defaultConfig=>{
|
|
||||||
if (defaultConfig && defaultConfig.length) {
|
|
||||||
this.connectorForm.get('configurationJson').setValue(
|
|
||||||
isString(defaultConfig[0].value) ?
|
|
||||||
JSON.parse(defaultConfig[0].value) :
|
|
||||||
defaultConfig[0].value);
|
|
||||||
this.cd.detectChanges();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.connectorForm.get('name').valueChanges.pipe(
|
|
||||||
takeUntil(this.destroy$)
|
|
||||||
).subscribe((name) => {
|
|
||||||
if (this.connectorForm.get('type').value === ConnectorType.MQTT) {
|
|
||||||
this.connectorForm.get('basicConfig').get('broker.name')?.setValue(name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
this.dataSource.sortingDataAccessor = (data: AttributeData, sortHeaderId: string) => {
|
this.dataSource.sortingDataAccessor = this.getSortingDataAccessor();
|
||||||
switch (sortHeaderId) {
|
|
||||||
case 'syncStatus':
|
|
||||||
return this.isConnectorSynced(data) ? 1 : 0;
|
|
||||||
|
|
||||||
case 'enabled':
|
|
||||||
return this.activeConnectors.includes(data.key) ? 1 : 0;
|
|
||||||
|
|
||||||
case 'errors':
|
|
||||||
const errors = this.getErrorsCount(data);
|
|
||||||
if (typeof errors === 'string') {
|
|
||||||
return this.sort.direction.toUpperCase() === Direction.DESC ? -1 : Infinity;
|
|
||||||
}
|
|
||||||
return errors;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return data[sortHeaderId] || data.value[sortHeaderId];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (attributes.length) {
|
|
||||||
this.activeConnectors = attributes[0].length ? attributes[0][0].value : [];
|
|
||||||
this.activeConnectors = isString(this.activeConnectors) ? JSON.parse(this.activeConnectors as any) : this.activeConnectors;
|
|
||||||
this.inactiveConnectors = attributes[1].length ? attributes[1][0].value : [];
|
|
||||||
this.inactiveConnectors = isString(this.inactiveConnectors)
|
|
||||||
? JSON.parse(this.inactiveConnectors as any)
|
|
||||||
: this.inactiveConnectors;
|
|
||||||
this.updateData(true);
|
|
||||||
} else {
|
|
||||||
this.activeConnectors = [];
|
|
||||||
this.inactiveConnectors = [];
|
|
||||||
this.updateData(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.loadConnectors();
|
||||||
this.observeModeChange();
|
this.observeModeChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,57 +162,12 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveConnector(): void {
|
saveConnector(): void {
|
||||||
const value = { ...this.connectorForm.value };
|
const value = this.getConnectorData();
|
||||||
value.configuration = camelCase(value.name) + '.json';
|
|
||||||
delete value.basicConfig;
|
|
||||||
if (value.type !== ConnectorType.GRPC) {
|
|
||||||
delete value.key;
|
|
||||||
}
|
|
||||||
if (value.type !== ConnectorType.CUSTOM) {
|
|
||||||
delete value.class;
|
|
||||||
}
|
|
||||||
value.ts = new Date().getTime();
|
|
||||||
const attributesToSave = [{
|
|
||||||
key: value.name,
|
|
||||||
value
|
|
||||||
}];
|
|
||||||
const attributesToDelete = [];
|
|
||||||
const scope = (!this.initialConnector || this.activeConnectors.includes(this.initialConnector.name))
|
const scope = (!this.initialConnector || this.activeConnectors.includes(this.initialConnector.name))
|
||||||
? AttributeScope.SHARED_SCOPE
|
? AttributeScope.SHARED_SCOPE
|
||||||
: AttributeScope.SERVER_SCOPE;
|
: AttributeScope.SERVER_SCOPE;
|
||||||
let updateActiveConnectors = false;
|
|
||||||
if (this.initialConnector && this.initialConnector.name !== value.name) {
|
|
||||||
attributesToDelete.push({key: this.initialConnector.name});
|
|
||||||
updateActiveConnectors = true;
|
|
||||||
const activeIndex = this.activeConnectors.indexOf(this.initialConnector.name);
|
|
||||||
const inactiveIndex = this.inactiveConnectors.indexOf(this.initialConnector.name);
|
|
||||||
if (activeIndex !== -1) {
|
|
||||||
this.activeConnectors.splice(activeIndex, 1);
|
|
||||||
}
|
|
||||||
if (inactiveIndex !== -1) {
|
|
||||||
this.inactiveConnectors.splice(inactiveIndex, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
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
|
|
||||||
}]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attributesToDelete.length) {
|
forkJoin(this.getEntityAttributeTasks(value, scope)).pipe(take(1)).subscribe(_ => {
|
||||||
tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, attributesToDelete));
|
|
||||||
}
|
|
||||||
forkJoin(tasks).subscribe(_ => {
|
|
||||||
this.showToast(!this.initialConnector
|
this.showToast(!this.initialConnector
|
||||||
? this.translate.instant('gateway.connector-created')
|
? this.translate.instant('gateway.connector-created')
|
||||||
: this.translate.instant('gateway.connector-updated')
|
: this.translate.instant('gateway.connector-updated')
|
||||||
@ -328,6 +178,69 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getEntityAttributeTasks(value: GatewayConnector, scope: AttributeScope): Observable<any>[] {
|
||||||
|
const tasks = [];
|
||||||
|
const attributesToSave = [{ key: value.name, value }];
|
||||||
|
const attributesToDelete = [];
|
||||||
|
const shouldAddToConnectorsList = !this.activeConnectors.includes(value.name) && scope === AttributeScope.SHARED_SCOPE
|
||||||
|
|| !this.inactiveConnectors.includes(value.name) && scope === AttributeScope.SERVER_SCOPE;
|
||||||
|
const isNewConnector = this.initialConnector && this.initialConnector.name !== value.name;
|
||||||
|
|
||||||
|
if (isNewConnector) {
|
||||||
|
attributesToDelete.push({ key: this.initialConnector.name });
|
||||||
|
this.removeConnectorFromList(this.initialConnector.name, true);
|
||||||
|
this.removeConnectorFromList(this.initialConnector.name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldAddToConnectorsList) {
|
||||||
|
if (scope === AttributeScope.SHARED_SCOPE) {
|
||||||
|
this.activeConnectors.push(value.name);
|
||||||
|
} else {
|
||||||
|
this.inactiveConnectors.push(value.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewConnector || shouldAddToConnectorsList) {
|
||||||
|
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
|
||||||
|
}]));
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.push(this.attributeService.saveEntityAttributes(this.device, scope, attributesToSave));
|
||||||
|
|
||||||
|
if (attributesToDelete.length) {
|
||||||
|
tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, attributesToDelete));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeConnectorFromList(connectorName: string, isActive: boolean): void {
|
||||||
|
const list = isActive? this.activeConnectors : this.inactiveConnectors;
|
||||||
|
const index = list.indexOf(connectorName);
|
||||||
|
if (index !== -1) {
|
||||||
|
list.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getConnectorData(): GatewayConnector {
|
||||||
|
const value = { ...this.connectorForm.value };
|
||||||
|
value.configuration = `${camelCase(value.name)}.json`;
|
||||||
|
delete value.basicConfig;
|
||||||
|
|
||||||
|
if (value.type !== ConnectorType.GRPC) {
|
||||||
|
delete value.key;
|
||||||
|
}
|
||||||
|
if (value.type !== ConnectorType.CUSTOM) {
|
||||||
|
delete value.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.ts = Date.now();
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private updateData(reload: boolean = false): void {
|
private updateData(reload: boolean = false): void {
|
||||||
this.pageLink.sortOrder.property = this.sort.active;
|
this.pageLink.sortOrder.property = this.sort.active;
|
||||||
this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()];
|
this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()];
|
||||||
@ -349,11 +262,11 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
|
|
||||||
isConnectorSynced(attribute: AttributeData) {
|
isConnectorSynced(attribute: AttributeData) {
|
||||||
const connectorData = attribute.value;
|
const connectorData = attribute.value;
|
||||||
if (!connectorData.ts) {
|
if (!connectorData.ts || attribute.skipSync) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const clientIndex = this.activeData.findIndex(data => {
|
const clientIndex = this.activeData.findIndex(data => {
|
||||||
const sharedData = data.value;
|
const sharedData = typeof data.value === 'string' ? JSON.parse(data.value) : data.value;
|
||||||
return sharedData.name === connectorData.name;
|
return sharedData.name === connectorData.name;
|
||||||
});
|
});
|
||||||
if (clientIndex === -1) {
|
if (clientIndex === -1) {
|
||||||
@ -384,12 +297,31 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
private combineData(): void {
|
private combineData(): void {
|
||||||
this.dataSource.data = [...this.activeData, ...this.inactiveData, ...this.sharedAttributeData].filter((item, index, self) =>
|
const combinedData = [
|
||||||
index === self.findIndex((t) => t.key === item.key)
|
...this.activeData,
|
||||||
).map(attribute => {
|
...this.inactiveData,
|
||||||
attribute.value = typeof attribute.value === 'string' ? JSON.parse(attribute.value) : attribute.value;
|
...this.sharedAttributeData
|
||||||
return attribute;
|
];
|
||||||
});
|
|
||||||
|
const latestData = combinedData.reduce((acc, attribute) => {
|
||||||
|
const existingItemIndex = acc.findIndex(item => item.key === attribute.key);
|
||||||
|
|
||||||
|
if (existingItemIndex === -1) {
|
||||||
|
acc.push(attribute);
|
||||||
|
} else if (
|
||||||
|
attribute.lastUpdateTs > acc[existingItemIndex].lastUpdateTs &&
|
||||||
|
!this.isConnectorSynced(acc[existingItemIndex])
|
||||||
|
) {
|
||||||
|
acc[existingItemIndex] = { ...attribute, skipSync: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
this.dataSource.data = latestData.map(attribute => ({
|
||||||
|
...attribute,
|
||||||
|
value: typeof attribute.value === 'string' ? JSON.parse(attribute.value) : attribute.value
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearOutConnectorForm(): void {
|
private clearOutConnectorForm(): void {
|
||||||
@ -447,7 +379,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
|
|
||||||
returnType(attribute: AttributeData): string {
|
returnType(attribute: AttributeData): string {
|
||||||
const value = attribute.value;
|
const value = attribute.value;
|
||||||
return this.gatewayConnectorDefaultTypes.get(value.type);
|
return this.GatewayConnectorTypesTranslatesMap.get(value.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteConnector(attribute: AttributeData, $event: Event): void {
|
deleteConnector(attribute: AttributeData, $event: Event): void {
|
||||||
@ -560,24 +492,95 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
uniqNameRequired(): ValidatorFn {
|
uniqNameRequired(): ValidatorFn {
|
||||||
return (c: UntypedFormControl) => {
|
return (control: UntypedFormControl) => {
|
||||||
const newName = c.value.trim().toLowerCase();
|
const newName = control.value?.trim().toLowerCase();
|
||||||
const found = this.dataSource.data.find((connectorAttr) => {
|
const isDuplicate = this.dataSource.data.some(connectorAttr => connectorAttr.value.name.toLowerCase() === newName);
|
||||||
const connectorData = connectorAttr.value;
|
const isSameAsInitial = this.initialConnector?.name.toLowerCase() === newName;
|
||||||
return connectorData.name.toLowerCase() === newName;
|
|
||||||
|
if (isDuplicate && !isSameAsInitial) {
|
||||||
|
return { duplicateName: { valid: false } };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private initDataSources(): void {
|
||||||
|
const sortOrder: SortOrder = {property: 'key', direction: Direction.ASC};
|
||||||
|
this.pageLink = new PageLink(1000, 0, null, sortOrder);
|
||||||
|
this.attributeDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate);
|
||||||
|
this.inactiveConnectorsDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate);
|
||||||
|
this.serverDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate);
|
||||||
|
this.dataSource = new MatTableDataSource<AttributeData>([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private initConnectorForm(): void {
|
||||||
|
this.connectorForm = this.fb.group({
|
||||||
|
mode: [ConnectorConfigurationModes.BASIC],
|
||||||
|
name: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(noLeadTrailSpacesRegex)]],
|
||||||
|
type: ['', [Validators.required]],
|
||||||
|
enableRemoteLogging: [false],
|
||||||
|
logLevel: ['', [Validators.required]],
|
||||||
|
sendDataOnlyOnChange: [false],
|
||||||
|
key: ['auto'],
|
||||||
|
class: [''],
|
||||||
|
configuration: [''],
|
||||||
|
configurationJson: [{}, [Validators.required]],
|
||||||
|
basicConfig: [{}]
|
||||||
});
|
});
|
||||||
if (found) {
|
this.connectorForm.disable();
|
||||||
if (this.initialConnector && this.initialConnector.name.toLowerCase() === newName) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
duplicateName: {
|
private observeName(): void {
|
||||||
valid: false
|
this.connectorForm.get('name').valueChanges
|
||||||
|
.pipe(
|
||||||
|
filter(() => this.connectorForm.get('type').value === ConnectorType.MQTT),
|
||||||
|
takeUntil(this.destroy$)
|
||||||
|
)
|
||||||
|
.subscribe(name => this.connectorForm.get('basicConfig').get('broker.name')?.setValue(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSortingDataAccessor(): (data: AttributeData, sortHeaderId: string) => string | number {
|
||||||
|
return (data: AttributeData, sortHeaderId: string) => {
|
||||||
|
switch (sortHeaderId) {
|
||||||
|
case 'syncStatus':
|
||||||
|
return this.isConnectorSynced(data) ? 1 : 0;
|
||||||
|
|
||||||
|
case 'enabled':
|
||||||
|
return this.activeConnectors.includes(data.key) ? 1 : 0;
|
||||||
|
|
||||||
|
case 'errors':
|
||||||
|
const errors = this.getErrorsCount(data);
|
||||||
|
if (typeof errors === 'string') {
|
||||||
|
return this.sort.direction.toUpperCase() === Direction.DESC ? -1 : Infinity;
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return data[sortHeaderId] || data.value[sortHeaderId];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
};
|
private loadConnectors(): void {
|
||||||
|
if (!this.device || 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'])
|
||||||
|
]).pipe(takeUntil(this.destroy$)).subscribe(attributes => {
|
||||||
|
this.activeConnectors = this.parseConnectors(attributes[0]);
|
||||||
|
this.inactiveConnectors = this.parseConnectors(attributes[1]);
|
||||||
|
|
||||||
|
this.updateData(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseConnectors(attribute: AttributeData[]): string[] {
|
||||||
|
const connectors = attribute?.[0]?.value || [];
|
||||||
|
return isString(connectors) ? JSON.parse(connectors) : connectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private observeModeChange(): void {
|
private observeModeChange(): void {
|
||||||
@ -636,7 +639,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
value: this.inactiveConnectors
|
value: this.inactiveConnectors
|
||||||
}]),
|
}]),
|
||||||
this.attributeService.deleteEntityAttributes(this.device, scopeOld, [attribute]),
|
this.attributeService.deleteEntityAttributes(this.device, scopeOld, [attribute]),
|
||||||
this.attributeService.saveEntityAttributes(this.device, scopeNew, [attribute])];
|
this.attributeService.saveEntityAttributes(this.device, scopeNew, [attribute])
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDataUpdateError(e: any): void {
|
private onDataUpdateError(e: any): void {
|
||||||
@ -725,20 +729,17 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie
|
|||||||
if (this.connectorForm.disabled) {
|
if (this.connectorForm.disabled) {
|
||||||
this.connectorForm.enable();
|
this.connectorForm.enable();
|
||||||
}
|
}
|
||||||
if (!connector.configuration) {
|
|
||||||
connector.configuration = '';
|
|
||||||
}
|
|
||||||
if (!connector.key) {
|
|
||||||
connector.key = 'auto';
|
|
||||||
}
|
|
||||||
if (!connector.configurationJson) {
|
|
||||||
connector.configurationJson = {} as ConnectorBaseConfig;
|
|
||||||
}
|
|
||||||
connector.basicConfig = connector.configurationJson;
|
|
||||||
|
|
||||||
this.initialConnector = connector;
|
const connectorState = {
|
||||||
|
configuration: '',
|
||||||
|
key: 'auto',
|
||||||
|
configurationJson: {} as ConnectorBaseConfig,
|
||||||
|
...connector,
|
||||||
|
};
|
||||||
|
|
||||||
this.updateConnector(connector);
|
connectorState.basicConfig = connectorState.configurationJson;
|
||||||
|
this.initialConnector = connectorState;
|
||||||
|
this.updateConnector(connectorState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateConnector(connector: GatewayConnector): void {
|
private updateConnector(connector: GatewayConnector): void {
|
||||||
|
|||||||
@ -118,6 +118,7 @@ export const timeseriesDeleteStrategyTranslations = new Map<TimeseriesDeleteStra
|
|||||||
|
|
||||||
export interface AttributeData {
|
export interface AttributeData {
|
||||||
lastUpdateTs?: number;
|
lastUpdateTs?: number;
|
||||||
|
skipSync?: boolean;
|
||||||
key: string;
|
key: string;
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user