|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -1,44 +1,15 @@ |
|||
diff --git a/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs b/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs
|
|||
index cf4e220..4275d11 100644
|
|||
index cf4e220..df51c91 100644
|
|||
--- a/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs
|
|||
+++ b/node_modules/angular-gridster2/fesm2020/angular-gridster2.mjs
|
|||
@@ -208,6 +208,7 @@ const GridsterConfigService = {
|
|||
useTransformPositioning: true, |
|||
scrollSensitivity: 10, |
|||
scrollSpeed: 20, |
|||
+ colWidthUpdateCallback: undefined,
|
|||
initCallback: undefined, |
|||
destroyCallback: undefined, |
|||
gridSizeChangedCallback: undefined, |
|||
@@ -1243,6 +1244,9 @@ class GridsterComponent {
|
|||
this.renderer.setStyle(this.el, 'padding-right', this.$options.margin + 'px'); |
|||
} |
|||
this.curColWidth = (this.curWidth - marginWidth) / this.columns; |
|||
+ if (this.options.colWidthUpdateCallback) {
|
|||
+ this.curColWidth = this.options.colWidthUpdateCallback(this.curColWidth);
|
|||
+ }
|
|||
let marginHeight = -this.$options.margin; |
|||
if (this.$options.outerMarginTop !== null) { |
|||
marginHeight += this.$options.outerMarginTop; |
|||
@@ -1266,6 +1270,9 @@ class GridsterComponent {
|
|||
@@ -666,8 +666,8 @@ class GridsterRenderer {
|
|||
renderer.setStyle(el, DirTypes.LTR ? 'margin-right' : 'margin-left', ''); |
|||
} |
|||
else { |
|||
this.curColWidth = (this.curWidth + this.$options.margin) / this.columns; |
|||
+ if (this.options.colWidthUpdateCallback) {
|
|||
+ this.curColWidth = this.options.colWidthUpdateCallback(this.curColWidth);
|
|||
+ }
|
|||
this.curRowHeight = |
|||
((this.curHeight + this.$options.margin) / this.rows) * |
|||
this.$options.rowHeightRatio; |
|||
diff --git a/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts b/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts
|
|||
index 1d7cdf0..a712b35 100644
|
|||
--- a/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts
|
|||
+++ b/node_modules/angular-gridster2/lib/gridsterConfig.interface.d.ts
|
|||
@@ -73,6 +73,7 @@ export interface GridsterConfig {
|
|||
useTransformPositioning?: boolean; |
|||
scrollSensitivity?: number | null; |
|||
scrollSpeed?: number; |
|||
+ colWidthUpdateCallback?: (colWidth: number) => number;
|
|||
initCallback?: (gridster: GridsterComponentInterface) => void; |
|||
destroyCallback?: (gridster: GridsterComponentInterface) => void; |
|||
gridSizeChangedCallback?: (gridster: GridsterComponentInterface) => void; |
|||
- const x = Math.round(this.gridster.curColWidth * item.x);
|
|||
- const y = Math.round(this.gridster.curRowHeight * item.y);
|
|||
+ const x = this.gridster.curColWidth * item.x;
|
|||
+ const y = this.gridster.curRowHeight * item.y;
|
|||
const width = this.gridster.curColWidth * item.cols - this.gridster.$options.margin; |
|||
const height = this.gridster.curRowHeight * item.rows - this.gridster.$options.margin; |
|||
// set the cell style |
|||
|
|||
@ -0,0 +1,84 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 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.
|
|||
///
|
|||
|
|||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; |
|||
import { |
|||
FormBuilder, |
|||
} from '@angular/forms'; |
|||
import { |
|||
LegacySlaveConfig, |
|||
ModbusProtocolType, |
|||
ModbusSlaveInfo, |
|||
} from '@home/components/widget/lib/gateway/gateway-widget.models'; |
|||
import { SharedModule } from '@shared/shared.module'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { ModbusValuesComponent } from '../modbus-values/modbus-values.component'; |
|||
import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Router } from '@angular/router'; |
|||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
|||
import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; |
|||
import { |
|||
ReportStrategyComponent |
|||
} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component'; |
|||
import { |
|||
ModbusSlaveDialogAbstract |
|||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-modbus-legacy-slave-dialog', |
|||
templateUrl: './modbus-slave-dialog.component.html', |
|||
changeDetection: ChangeDetectionStrategy.OnPush, |
|||
standalone: true, |
|||
imports: [ |
|||
CommonModule, |
|||
SharedModule, |
|||
ModbusValuesComponent, |
|||
ModbusSecurityConfigComponent, |
|||
GatewayPortTooltipPipe, |
|||
ReportStrategyComponent, |
|||
], |
|||
styleUrls: ['./modbus-slave-dialog.component.scss'], |
|||
}) |
|||
export class ModbusLegacySlaveDialogComponent extends ModbusSlaveDialogAbstract<ModbusLegacySlaveDialogComponent, LegacySlaveConfig> { |
|||
|
|||
constructor( |
|||
protected fb: FormBuilder, |
|||
protected store: Store<AppState>, |
|||
protected router: Router, |
|||
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo, |
|||
public dialogRef: MatDialogRef<ModbusLegacySlaveDialogComponent, LegacySlaveConfig>, |
|||
) { |
|||
super(fb, store, router, data, dialogRef); |
|||
} |
|||
|
|||
protected override getSlaveResultData(): LegacySlaveConfig { |
|||
const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value; |
|||
const slaveResult = { ...rest, type, ...values }; |
|||
|
|||
if (type === ModbusProtocolType.Serial) { |
|||
slaveResult.port = serialPort; |
|||
} |
|||
|
|||
return slaveResult; |
|||
} |
|||
|
|||
|
|||
protected override addFieldsToFormGroup(): void { |
|||
this.slaveConfigFormGroup.addControl('sendDataOnlyOnChange', this.fb.control(false)); |
|||
} |
|||
} |
|||
@ -0,0 +1,207 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 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.
|
|||
///
|
|||
|
|||
import { Directive, Inject, OnDestroy } from '@angular/core'; |
|||
import { |
|||
FormBuilder, |
|||
FormControl, |
|||
UntypedFormGroup, |
|||
Validators, |
|||
} from '@angular/forms'; |
|||
import { |
|||
ModbusBaudrates, |
|||
ModbusByteSizes, |
|||
ModbusMethodLabelsMap, |
|||
ModbusMethodType, |
|||
ModbusOrderType, |
|||
ModbusParity, |
|||
ModbusParityLabelsMap, |
|||
ModbusProtocolLabelsMap, |
|||
ModbusProtocolType, |
|||
ModbusSerialMethodType, |
|||
ModbusSlaveInfo, |
|||
noLeadTrailSpacesRegex, |
|||
PortLimits, |
|||
} from '@home/components/widget/lib/gateway/gateway-widget.models'; |
|||
import { Subject } from 'rxjs'; |
|||
import { DialogComponent } from '@shared/components/dialog.component'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Router } from '@angular/router'; |
|||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
|||
import { takeUntil } from 'rxjs/operators'; |
|||
import { isEqual } from '@core/utils'; |
|||
import { helpBaseUrl } from '@shared/models/constants'; |
|||
|
|||
@Directive() |
|||
export abstract class ModbusSlaveDialogAbstract<Component, Config> extends DialogComponent<Component, Config> implements OnDestroy { |
|||
|
|||
slaveConfigFormGroup: UntypedFormGroup; |
|||
showSecurityControl: FormControl<boolean>; |
|||
portLimits = PortLimits; |
|||
|
|||
readonly modbusProtocolTypes = Object.values(ModbusProtocolType); |
|||
readonly modbusMethodTypes = Object.values(ModbusMethodType); |
|||
readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType); |
|||
readonly modbusParities = Object.values(ModbusParity); |
|||
readonly modbusByteSizes = ModbusByteSizes; |
|||
readonly modbusBaudrates = ModbusBaudrates; |
|||
readonly modbusOrderType = Object.values(ModbusOrderType); |
|||
readonly ModbusProtocolType = ModbusProtocolType; |
|||
readonly ModbusParityLabelsMap = ModbusParityLabelsMap; |
|||
readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; |
|||
readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap; |
|||
readonly modbusHelpLink = |
|||
helpBaseUrl + '/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters'; |
|||
|
|||
private readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict']; |
|||
private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host']; |
|||
|
|||
private destroy$ = new Subject<void>(); |
|||
|
|||
constructor( |
|||
protected fb: FormBuilder, |
|||
protected store: Store<AppState>, |
|||
protected router: Router, |
|||
@Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo, |
|||
public dialogRef: MatDialogRef<Component, Config>, |
|||
) { |
|||
super(store, router, dialogRef); |
|||
|
|||
this.showSecurityControl = this.fb.control(false); |
|||
this.initializeSlaveFormGroup(); |
|||
this.updateSlaveFormGroup(); |
|||
this.updateControlsEnabling(this.data.value.type); |
|||
this.observeTypeChange(); |
|||
this.observeShowSecurity(); |
|||
this.showSecurityControl.patchValue(!!this.data.value.security && !isEqual(this.data.value.security, {})); |
|||
} |
|||
|
|||
get protocolType(): ModbusProtocolType { |
|||
return this.slaveConfigFormGroup.get('type').value; |
|||
} |
|||
|
|||
ngOnDestroy(): void { |
|||
this.destroy$.next(); |
|||
this.destroy$.complete(); |
|||
} |
|||
|
|||
cancel(): void { |
|||
this.dialogRef.close(null); |
|||
} |
|||
|
|||
add(): void { |
|||
if (!this.slaveConfigFormGroup.valid) { |
|||
return; |
|||
} |
|||
|
|||
this.dialogRef.close(this.getSlaveResultData()); |
|||
} |
|||
|
|||
private initializeSlaveFormGroup(): void { |
|||
this.slaveConfigFormGroup = this.fb.group({ |
|||
name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], |
|||
type: [ModbusProtocolType.TCP], |
|||
host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], |
|||
port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]], |
|||
serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], |
|||
method: [ModbusMethodType.SOCKET, [Validators.required]], |
|||
baudrate: [this.modbusBaudrates[0]], |
|||
stopbits: [1], |
|||
bytesize: [ModbusByteSizes[0]], |
|||
parity: [ModbusParity.None], |
|||
strict: [true], |
|||
unitId: [null, [Validators.required]], |
|||
deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], |
|||
deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], |
|||
timeout: [35], |
|||
byteOrder: [ModbusOrderType.BIG], |
|||
wordOrder: [ModbusOrderType.BIG], |
|||
retries: [true], |
|||
retryOnEmpty: [true], |
|||
retryOnInvalid: [true], |
|||
pollPeriod: [5000, [Validators.required]], |
|||
connectAttemptTimeMs: [5000, [Validators.required]], |
|||
connectAttemptCount: [5, [Validators.required]], |
|||
waitAfterFailedAttemptsMs: [300000, [Validators.required]], |
|||
values: [{}], |
|||
security: [{}], |
|||
}); |
|||
this.addFieldsToFormGroup(); |
|||
} |
|||
|
|||
private updateSlaveFormGroup(): void { |
|||
this.slaveConfigFormGroup.patchValue({ |
|||
...this.data.value, |
|||
port: this.data.value.type === ModbusProtocolType.Serial ? null : this.data.value.port, |
|||
serialPort: this.data.value.type === ModbusProtocolType.Serial ? this.data.value.port : '', |
|||
values: { |
|||
attributes: this.data.value.attributes ?? [], |
|||
timeseries: this.data.value.timeseries ?? [], |
|||
attributeUpdates: this.data.value.attributeUpdates ?? [], |
|||
rpc: this.data.value.rpc ?? [], |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private observeTypeChange(): void { |
|||
this.slaveConfigFormGroup.get('type').valueChanges |
|||
.pipe(takeUntil(this.destroy$)) |
|||
.subscribe(type => { |
|||
this.updateControlsEnabling(type); |
|||
this.updateMethodType(type); |
|||
}); |
|||
} |
|||
|
|||
private updateMethodType(type: ModbusProtocolType): void { |
|||
if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) { |
|||
this.slaveConfigFormGroup.get('method').patchValue( |
|||
type === ModbusProtocolType.Serial |
|||
? ModbusSerialMethodType.ASCII |
|||
: ModbusMethodType.SOCKET, |
|||
{emitEvent: false} |
|||
); |
|||
} |
|||
} |
|||
|
|||
private updateControlsEnabling(type: ModbusProtocolType): void { |
|||
const [enableKeys, disableKeys] = type === ModbusProtocolType.Serial |
|||
? [this.serialSpecificControlKeys, this.tcpUdpSpecificControlKeys] |
|||
: [this.tcpUdpSpecificControlKeys, this.serialSpecificControlKeys]; |
|||
|
|||
enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false })); |
|||
disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false })); |
|||
|
|||
this.updateSecurityEnabling(this.showSecurityControl.value); |
|||
} |
|||
|
|||
private observeShowSecurity(): void { |
|||
this.showSecurityControl.valueChanges |
|||
.pipe(takeUntil(this.destroy$)) |
|||
.subscribe(value => this.updateSecurityEnabling(value)); |
|||
} |
|||
|
|||
private updateSecurityEnabling(isEnabled: boolean): void { |
|||
if (isEnabled && this.protocolType !== ModbusProtocolType.Serial) { |
|||
this.slaveConfigFormGroup.get('security').enable({emitEvent: false}); |
|||
} else { |
|||
this.slaveConfigFormGroup.get('security').disable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
protected abstract addFieldsToFormGroup(): void; |
|||
protected abstract getSlaveResultData(): Config; |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2024 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. |
|||
|
|||
--> |
|||
<div [formGroup]="reportStrategyFormGroup" class="tb-form-panel stroked"> |
|||
<mat-expansion-panel *ngIf="isExpansionMode else defaultMode" class="tb-settings" [expanded]="showStrategyControl.value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle fxLayoutAlign="center" [formControl]="showStrategyControl" class="mat-slide" (click)="$event.stopPropagation()"> |
|||
<mat-label> |
|||
{{ 'gateway.report-strategy.label' | translate }} |
|||
</mat-label> |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
<ng-container [ngTemplateOutlet]="strategyFields"></ng-container> |
|||
</mat-expansion-panel> |
|||
<ng-template #defaultMode> |
|||
<div class="tb-form-panel-title" translate>gateway.report-strategy.label</div> |
|||
<ng-container [ngTemplateOutlet]="strategyFields"></ng-container> |
|||
</ng-template> |
|||
<ng-template #strategyFields> |
|||
<div class="tb-form-row column-xs" fxLayoutAlign="space-between center"> |
|||
<div class="fixed-title-width">{{ 'gateway.type' | translate }}</div> |
|||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic"> |
|||
<mat-select formControlName="type"> |
|||
<mat-option *ngFor="let type of reportStrategyTypes" [value]="type">{{ ReportTypeTranslateMap.get(type) | translate }}</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
</div> |
|||
<div *ngIf="reportStrategyFormGroup.get('type').value !== ReportStrategyType.OnChange" class="tb-form-row column-xs" fxLayoutAlign="space-between center"> |
|||
<div class="fixed-title-width tb-required"> |
|||
<span tbTruncateWithTooltip translate> |
|||
gateway.report-strategy.report-period |
|||
</span> |
|||
</div> |
|||
<div class="tb-flex no-gap"> |
|||
<mat-form-field class="tb-flex no-gap" appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput type="number" min="0" name="value" formControlName="reportPeriod" placeholder="{{ 'gateway.set' | translate }}"/> |
|||
</mat-form-field> |
|||
</div> |
|||
</div> |
|||
</ng-template> |
|||
</div> |
|||
@ -0,0 +1,170 @@ |
|||
///
|
|||
/// Copyright © 2016-2024 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.
|
|||
///
|
|||
|
|||
import { |
|||
ChangeDetectionStrategy, |
|||
Component, |
|||
forwardRef, |
|||
Input, |
|||
OnDestroy, |
|||
} from '@angular/core'; |
|||
import { Subject } from 'rxjs'; |
|||
import { |
|||
ControlValueAccessor, |
|||
FormBuilder, |
|||
FormControl, |
|||
NG_VALIDATORS, |
|||
NG_VALUE_ACCESSOR, |
|||
UntypedFormGroup, |
|||
ValidationErrors, |
|||
Validators |
|||
} from '@angular/forms'; |
|||
import { |
|||
ReportStrategyConfig, |
|||
ReportStrategyType, |
|||
ReportStrategyTypeTranslationsMap |
|||
} from '@home/components/widget/lib/gateway/gateway-widget.models'; |
|||
import { filter, takeUntil } from 'rxjs/operators'; |
|||
import { SharedModule } from '@shared/shared.module'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { |
|||
ModbusSecurityConfigComponent |
|||
} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component'; |
|||
import { coerceBoolean } from '@shared/decorators/coercion'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-report-strategy', |
|||
templateUrl: './report-strategy.component.html', |
|||
changeDetection: ChangeDetectionStrategy.OnPush, |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => ReportStrategyComponent), |
|||
multi: true |
|||
}, |
|||
{ |
|||
provide: NG_VALIDATORS, |
|||
useExisting: forwardRef(() => ReportStrategyComponent), |
|||
multi: true |
|||
} |
|||
], |
|||
standalone: true, |
|||
imports: [ |
|||
CommonModule, |
|||
SharedModule, |
|||
ModbusSecurityConfigComponent, |
|||
] |
|||
}) |
|||
export class ReportStrategyComponent implements ControlValueAccessor, OnDestroy { |
|||
|
|||
@coerceBoolean() |
|||
@Input() isExpansionMode = false; |
|||
|
|||
reportStrategyFormGroup: UntypedFormGroup; |
|||
showStrategyControl: FormControl<boolean>; |
|||
|
|||
readonly reportStrategyTypes = Object.values(ReportStrategyType); |
|||
readonly ReportTypeTranslateMap = ReportStrategyTypeTranslationsMap; |
|||
readonly ReportStrategyType = ReportStrategyType; |
|||
|
|||
private onChange: (value: ReportStrategyConfig) => void; |
|||
private onTouched: () => void; |
|||
|
|||
private destroy$ = new Subject<void>(); |
|||
|
|||
constructor(private fb: FormBuilder) { |
|||
this.showStrategyControl = this.fb.control(false); |
|||
|
|||
this.reportStrategyFormGroup = this.fb.group({ |
|||
type: [{ value: ReportStrategyType.OnReportPeriod, disabled: true }, []], |
|||
reportPeriod: [{ value: 5000, disabled: true }, [Validators.required]], |
|||
}); |
|||
|
|||
this.observeStrategyFormChange(); |
|||
this.observeStrategyToggle(); |
|||
} |
|||
|
|||
ngOnDestroy(): void { |
|||
this.destroy$.next(); |
|||
this.destroy$.complete(); |
|||
} |
|||
|
|||
writeValue(reportStrategyConfig: ReportStrategyConfig): void { |
|||
if (this.isExpansionMode) { |
|||
this.showStrategyControl.setValue(!!reportStrategyConfig, {emitEvent: false}); |
|||
} |
|||
if (reportStrategyConfig) { |
|||
this.reportStrategyFormGroup.enable({emitEvent: false}); |
|||
} |
|||
const { type = ReportStrategyType.OnReportPeriod, reportPeriod = 5000 } = reportStrategyConfig ?? {}; |
|||
this.reportStrategyFormGroup.setValue({ type, reportPeriod }, {emitEvent: false}); |
|||
this.onTypeChange(type); |
|||
} |
|||
|
|||
validate(): ValidationErrors | null { |
|||
return this.reportStrategyFormGroup.valid || this.reportStrategyFormGroup.disabled ? null : { |
|||
reportStrategyForm: { valid: false } |
|||
}; |
|||
} |
|||
|
|||
registerOnChange(fn: (value: ReportStrategyConfig) => void): void { |
|||
this.onChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: () => void): void { |
|||
this.onTouched = fn; |
|||
} |
|||
|
|||
private observeStrategyFormChange(): void { |
|||
this.reportStrategyFormGroup.valueChanges.pipe( |
|||
takeUntil(this.destroy$) |
|||
).subscribe((value) => { |
|||
this.onChange(value); |
|||
this.onTouched(); |
|||
}); |
|||
|
|||
this.reportStrategyFormGroup.get('type').valueChanges |
|||
.pipe(takeUntil(this.destroy$)) |
|||
.subscribe(type => this.onTypeChange(type)); |
|||
} |
|||
|
|||
private observeStrategyToggle(): void { |
|||
this.showStrategyControl.valueChanges |
|||
.pipe(takeUntil(this.destroy$), filter(() => this.isExpansionMode)) |
|||
.subscribe(enable => { |
|||
if (enable) { |
|||
this.reportStrategyFormGroup.enable({emitEvent: false}); |
|||
this.reportStrategyFormGroup.get('reportPeriod').addValidators(Validators.required); |
|||
this.onChange(this.reportStrategyFormGroup.value); |
|||
} else { |
|||
this.reportStrategyFormGroup.disable({emitEvent: false}); |
|||
this.reportStrategyFormGroup.get('reportPeriod').removeValidators(Validators.required); |
|||
this.onChange(null); |
|||
} |
|||
this.reportStrategyFormGroup.updateValueAndValidity({emitEvent: false}); |
|||
}); |
|||
} |
|||
|
|||
private onTypeChange(type: ReportStrategyType): void { |
|||
const reportPeriodControl = this.reportStrategyFormGroup.get('reportPeriod'); |
|||
|
|||
if (type === ReportStrategyType.OnChange) { |
|||
reportPeriodControl.disable({emitEvent: false}); |
|||
} else if (!this.isExpansionMode || this.showStrategyControl.value) { |
|||
reportPeriodControl.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
} |
|||