266 changed files with 15639 additions and 83 deletions
@ -0,0 +1,35 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="assignCustomerConfigForm" class="flex flex-col"> |
||||
|
<div class="tb-form-panel no-padding no-border"> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>tb.rulenode.customer-name-pattern</mat-label> |
||||
|
<input required matInput formControlName="customerNamePattern"> |
||||
|
<mat-error *ngIf="assignCustomerConfigForm.get('customerNamePattern').hasError('required') || |
||||
|
assignCustomerConfigForm.get('customerNamePattern').hasError('pattern')"> |
||||
|
{{ 'tb.rulenode.customer-name-pattern-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint translate>tb.rulenode.customer-name-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
<div class="tb-form-row"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="createCustomerIfNotExists"> |
||||
|
{{ 'tb.rulenode.create-customer-if-not-exists' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,49 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-assign-to-customer-config', |
||||
|
templateUrl: './assign-customer-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class AssignCustomerConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
assignCustomerConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.assignCustomerConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.assignCustomerConfigForm = this.fb.group({ |
||||
|
customerNamePattern: [configuration ? configuration.customerNamePattern : null, [Validators.required, Validators.pattern(/.*\S.*/)]], |
||||
|
createCustomerIfNotExists: [configuration ? configuration.createCustomerIfNotExists : false, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
configuration.customerNamePattern = configuration.customerNamePattern.trim(); |
||||
|
return configuration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="attributesConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<tb-example-hint [hintText]="'tb.rulenode.attributes-scope-hint'"> |
||||
|
</tb-example-hint> |
||||
|
<div class="tb-form-row no-border no-padding tb-standard-fields"> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'tb.rulenode.attributes-scope' | translate }}</mat-label> |
||||
|
<mat-select required |
||||
|
class="tb-entity-type-select" matInput formControlName="scope"> |
||||
|
<mat-option *ngFor="let scope of attributeScopes" [value]="scope"> |
||||
|
{{ telemetryTypeTranslationsMap.get(scope) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'tb.rulenode.attributes-scope-value' | translate }}</mat-label> |
||||
|
<input type="text" matInput readonly disabled [ngModel]="attributesConfigForm.get('scope').value" [ngModelOptions]="{standalone: true}"> |
||||
|
<button type="button" |
||||
|
matSuffix |
||||
|
mat-icon-button |
||||
|
aria-label="Copy" |
||||
|
ngxClipboard |
||||
|
[cbContent]="attributesConfigForm.get('scope').value" |
||||
|
matTooltip="{{ 'tb.rulenode.attributes-scope-value-copy' | translate }}"> |
||||
|
<mat-icon aria-hidden="false" |
||||
|
aria-label="help-icon">content_copy |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<section class="tb-form-panel stroked"> |
||||
|
<mat-expansion-panel class="tb-settings"> |
||||
|
<mat-expansion-panel-header> |
||||
|
<mat-panel-title translate>tb.rulenode.advanced-settings</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<div tb-hint-tooltip-icon="{{ (attributesConfigForm.get('updateAttributesOnlyOnValueChange').value |
||||
|
? 'tb.rulenode.update-attributes-only-on-value-change-hint-enabled' |
||||
|
: 'tb.rulenode.update-attributes-only-on-value-change-hint') | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="updateAttributesOnlyOnValueChange"> |
||||
|
{{ 'tb.rulenode.update-attributes-only-on-value-change' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.send-attributes-updated-notification-hint' | translate }}" |
||||
|
*ngIf="attributesConfigForm.get('scope').value !== attributeScopeMap.CLIENT_SCOPE" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sendAttributesUpdatedNotification"> |
||||
|
{{ 'tb.rulenode.send-attributes-updated-notification' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.notify-device-on-update-hint' | translate }}" |
||||
|
*ngIf="attributesConfigForm.get('scope').value === attributeScopeMap.SHARED_SCOPE" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="notifyDevice"> |
||||
|
{{ 'tb.rulenode.notify-device' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,62 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
import { AttributeScope, telemetryTypeTranslations } from '@app/shared/models/telemetry/telemetry.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-attributes-config', |
||||
|
templateUrl: './attributes-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class AttributesConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
attributeScopeMap = AttributeScope; |
||||
|
attributeScopes = Object.keys(AttributeScope); |
||||
|
telemetryTypeTranslationsMap = telemetryTypeTranslations; |
||||
|
|
||||
|
attributesConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.attributesConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.attributesConfigForm = this.fb.group({ |
||||
|
scope: [configuration ? configuration.scope : null, [Validators.required]], |
||||
|
notifyDevice: [configuration ? configuration.notifyDevice : true, []], |
||||
|
sendAttributesUpdatedNotification: [configuration ? configuration.sendAttributesUpdatedNotification : false, []], |
||||
|
updateAttributesOnlyOnValueChange: [configuration ? configuration.updateAttributesOnlyOnValueChange : false, []] |
||||
|
}); |
||||
|
|
||||
|
this.attributesConfigForm.get('scope').valueChanges.subscribe((value) => { |
||||
|
if (value !== AttributeScope.SHARED_SCOPE) { |
||||
|
this.attributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false}); |
||||
|
} |
||||
|
if (value === AttributeScope.CLIENT_SCOPE) { |
||||
|
this.attributesConfigForm.get('sendAttributesUpdatedNotification').patchValue(false, {emitEvent: false}); |
||||
|
} |
||||
|
this.attributesConfigForm.get('updateAttributesOnlyOnValueChange').patchValue(false, {emitEvent: false}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="clearAlarmConfigForm" class="flex flex-col"> |
||||
|
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang> |
||||
|
<tb-js-func *ngIf="clearAlarmConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="alarmDetailsBuildJs" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
helpId="rulenode/clear_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="clearAlarmConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="alarmDetailsBuildTbel" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/clear_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row" style="padding-bottom: 16px;"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>tb.rulenode.alarm-type</mat-label> |
||||
|
<input required matInput formControlName="alarmType"> |
||||
|
<mat-error *ngIf="clearAlarmConfigForm.get('alarmType').hasError('required')"> |
||||
|
{{ 'tb.rulenode.alarm-type-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint translate>tb.rulenode.general-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
@ -0,0 +1,127 @@ |
|||||
|
///
|
||||
|
/// 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 { Component, EventEmitter, ViewChild } from '@angular/core'; |
||||
|
import { AppState, getCurrentAuthState, NodeScriptTestService } from '@core/public-api'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent, |
||||
|
ScriptLanguage |
||||
|
} from '@app/shared/models/rule-node.models'; |
||||
|
import type { JsFuncComponent } from '@app/shared/components/js-func.component'; |
||||
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-clear-alarm-config', |
||||
|
templateUrl: './clear-alarm-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class ClearAlarmConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; |
||||
|
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; |
||||
|
|
||||
|
clearAlarmConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; |
||||
|
|
||||
|
scriptLanguage = ScriptLanguage; |
||||
|
|
||||
|
changeScript: EventEmitter<void> = new EventEmitter<void>(); |
||||
|
|
||||
|
readonly hasScript = true; |
||||
|
|
||||
|
readonly testScriptLabel = 'tb.rulenode.test-details-function'; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: UntypedFormBuilder, |
||||
|
private nodeScriptTestService: NodeScriptTestService, |
||||
|
private translate: TranslateService) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.clearAlarmConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.clearAlarmConfigForm = this.fb.group({ |
||||
|
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], |
||||
|
alarmDetailsBuildJs: [configuration ? configuration.alarmDetailsBuildJs : null, []], |
||||
|
alarmDetailsBuildTbel: [configuration ? configuration.alarmDetailsBuildTbel : null, []], |
||||
|
alarmType: [configuration ? configuration.alarmType : null, [Validators.required]] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['scriptLang']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
let scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { |
||||
|
scriptLang = ScriptLanguage.JS; |
||||
|
this.clearAlarmConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); |
||||
|
setTimeout(() => {this.clearAlarmConfigForm.updateValueAndValidity({emitEvent: true});}); |
||||
|
} |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildJs').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildJs').updateValueAndValidity({emitEvent}); |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildTbel').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); |
||||
|
this.clearAlarmConfigForm.get('alarmDetailsBuildTbel').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
if (configuration) { |
||||
|
if (!configuration.scriptLang) { |
||||
|
configuration.scriptLang = ScriptLanguage.JS; |
||||
|
} |
||||
|
} |
||||
|
return configuration; |
||||
|
} |
||||
|
|
||||
|
testScript(debugEventBody?: DebugRuleNodeEventBody) { |
||||
|
const scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value; |
||||
|
const scriptField = scriptLang === ScriptLanguage.JS ? 'alarmDetailsBuildJs' : 'alarmDetailsBuildTbel'; |
||||
|
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/clear_alarm_node_script_fn' : 'rulenode/tbel/clear_alarm_node_script_fn'; |
||||
|
const script: string = this.clearAlarmConfigForm.get(scriptField).value; |
||||
|
this.nodeScriptTestService.testNodeScript( |
||||
|
script, |
||||
|
'json', |
||||
|
this.translate.instant('tb.rulenode.details'), |
||||
|
'Details', |
||||
|
['msg', 'metadata', 'msgType'], |
||||
|
this.ruleNodeId, |
||||
|
helpId, |
||||
|
scriptLang, |
||||
|
debugEventBody |
||||
|
).subscribe((theScript) => { |
||||
|
if (theScript) { |
||||
|
this.clearAlarmConfigForm.get(scriptField).setValue(theScript); |
||||
|
this.changeScript.emit(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected onValidate() { |
||||
|
const scriptLang: ScriptLanguage = this.clearAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.JS) { |
||||
|
this.jsFuncComponent.validateOnSubmit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,128 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="createAlarmConfigForm" class="flex flex-col"> |
||||
|
<mat-checkbox formControlName="useMessageAlarmData"> |
||||
|
{{ 'tb.rulenode.use-message-alarm-data' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<mat-checkbox formControlName="overwriteAlarmDetails" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === true"> |
||||
|
{{ 'tb.rulenode.overwrite-alarm-details' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<section class="flex flex-col" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === false || |
||||
|
createAlarmConfigForm.get('overwriteAlarmDetails').value === true"> |
||||
|
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang> |
||||
|
<tb-js-func *ngIf="createAlarmConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="alarmDetailsBuildJs" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
helpId="rulenode/create_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="createAlarmConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="alarmDetailsBuildTbel" |
||||
|
functionName="Details" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/create_alarm_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row" style="padding-bottom: 16px;"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</section> |
||||
|
<section class="flex flex-col" *ngIf="createAlarmConfigForm.get('useMessageAlarmData').value === false"> |
||||
|
<mat-form-field class="flex-1" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>tb.rulenode.alarm-type</mat-label> |
||||
|
<input required matInput formControlName="alarmType"> |
||||
|
<mat-error *ngIf="createAlarmConfigForm.get('alarmType').hasError('required')"> |
||||
|
{{ 'tb.rulenode.alarm-type-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint translate>tb.rulenode.general-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
<mat-checkbox formControlName="dynamicSeverity"> |
||||
|
{{ 'tb.rulenode.use-alarm-severity-pattern' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<mat-form-field class="flex-1" *ngIf="!createAlarmConfigForm.get('dynamicSeverity').value"> |
||||
|
<mat-label translate>tb.rulenode.alarm-severity</mat-label> |
||||
|
<mat-select formControlName="severity" required> |
||||
|
<mat-option *ngFor="let severity of alarmSeverities" [value]="severity"> |
||||
|
{{ alarmSeverityTranslationMap.get(severity) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
<mat-error *ngIf="createAlarmConfigForm.get('severity').hasError('required')"> |
||||
|
{{ 'tb.rulenode.alarm-severity-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex-1" *ngIf="createAlarmConfigForm.get('dynamicSeverity').value" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>tb.rulenode.alarm-severity-pattern</mat-label> |
||||
|
<input matInput formControlName="severity" required> |
||||
|
<mat-error *ngIf="createAlarmConfigForm.get('severity').hasError('required')"> |
||||
|
{{ 'tb.rulenode.alarm-severity-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint [innerHTML]="'tb.rulenode.alarm-severity-pattern-hint' | translate | safe: 'html'"></mat-hint> |
||||
|
</mat-form-field> |
||||
|
<mat-checkbox formControlName="propagate"> |
||||
|
{{ 'tb.rulenode.propagate' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<section *ngIf="createAlarmConfigForm.get('propagate').value === true"> |
||||
|
<mat-form-field floatLabel="always" class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>tb.rulenode.relation-types-list</mat-label> |
||||
|
<mat-chip-grid #relationTypesChipList> |
||||
|
<mat-chip-row |
||||
|
*ngFor="let key of createAlarmConfigForm.get('relationTypes').value;" |
||||
|
(removed)="removeKey(key, 'relationTypes')"> |
||||
|
{{key}} |
||||
|
<mat-icon matChipRemove>close</mat-icon> |
||||
|
</mat-chip-row> |
||||
|
<input matInput type="text" placeholder="{{'tb.rulenode.relation-types-list' | translate}}" |
||||
|
[matChipInputFor]="relationTypesChipList" |
||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" |
||||
|
(matChipInputTokenEnd)="addKey($event, 'relationTypes')" |
||||
|
[matChipInputAddOnBlur]="true"> |
||||
|
</mat-chip-grid> |
||||
|
<mat-hint translate>tb.rulenode.relation-types-list-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
|
<mat-checkbox formControlName="propagateToOwner"> |
||||
|
{{ 'tb.rulenode.propagate-to-owner' | translate }} |
||||
|
</mat-checkbox> |
||||
|
<mat-checkbox formControlName="propagateToTenant"> |
||||
|
{{ 'tb.rulenode.propagate-to-tenant' | translate }} |
||||
|
</mat-checkbox> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,199 @@ |
|||||
|
///
|
||||
|
/// 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 { Component, EventEmitter, ViewChild } from '@angular/core'; |
||||
|
import { AppState, getCurrentAuthState, NodeScriptTestService } from '@core/public-api'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; |
||||
|
import { MatChipInputEvent } from '@angular/material/chips'; |
||||
|
import { |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent, |
||||
|
ScriptLanguage |
||||
|
} from '@app/shared/models/rule-node.models'; |
||||
|
import type { JsFuncComponent } from '@app/shared/components/js-func.component'; |
||||
|
import { AlarmSeverity, alarmSeverityTranslations } from '@app/shared/models/alarm.models'; |
||||
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-create-alarm-config', |
||||
|
templateUrl: './create-alarm-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class CreateAlarmConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; |
||||
|
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; |
||||
|
|
||||
|
alarmSeverities = Object.keys(AlarmSeverity); |
||||
|
alarmSeverityTranslationMap = alarmSeverityTranslations; |
||||
|
createAlarmConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; |
||||
|
|
||||
|
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; |
||||
|
|
||||
|
scriptLanguage = ScriptLanguage; |
||||
|
|
||||
|
changeScript: EventEmitter<void> = new EventEmitter<void>(); |
||||
|
|
||||
|
readonly hasScript = true; |
||||
|
|
||||
|
readonly testScriptLabel = 'tb.rulenode.test-details-function'; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
private fb: UntypedFormBuilder, |
||||
|
private nodeScriptTestService: NodeScriptTestService, |
||||
|
private translate: TranslateService) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.createAlarmConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.createAlarmConfigForm = this.fb.group({ |
||||
|
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], |
||||
|
alarmDetailsBuildJs: [configuration ? configuration.alarmDetailsBuildJs : null, []], |
||||
|
alarmDetailsBuildTbel: [configuration ? configuration.alarmDetailsBuildTbel : null, []], |
||||
|
useMessageAlarmData: [configuration ? configuration.useMessageAlarmData : false, []], |
||||
|
overwriteAlarmDetails: [configuration ? configuration.overwriteAlarmDetails : false, []], |
||||
|
alarmType: [configuration ? configuration.alarmType : null, []], |
||||
|
severity: [configuration ? configuration.severity : null, []], |
||||
|
propagate: [configuration ? configuration.propagate : false, []], |
||||
|
relationTypes: [configuration ? configuration.relationTypes : null, []], |
||||
|
propagateToOwner: [configuration ? configuration.propagateToOwner : false, []], |
||||
|
propagateToTenant: [configuration ? configuration.propagateToTenant : false, []], |
||||
|
dynamicSeverity: false |
||||
|
}); |
||||
|
|
||||
|
this.createAlarmConfigForm.get('dynamicSeverity').valueChanges.subscribe((dynamicSeverity) => { |
||||
|
if(dynamicSeverity){ |
||||
|
this.createAlarmConfigForm.get('severity').patchValue('',{emitEvent:false}); |
||||
|
} else { |
||||
|
this.createAlarmConfigForm.get('severity').patchValue(this.alarmSeverities[0],{emitEvent:false}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['useMessageAlarmData', 'overwriteAlarmDetails', 'scriptLang']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const useMessageAlarmData: boolean = this.createAlarmConfigForm.get('useMessageAlarmData').value; |
||||
|
const overwriteAlarmDetails: boolean = this.createAlarmConfigForm.get('overwriteAlarmDetails').value; |
||||
|
if (useMessageAlarmData) { |
||||
|
this.createAlarmConfigForm.get('alarmType').setValidators([]); |
||||
|
this.createAlarmConfigForm.get('severity').setValidators([]); |
||||
|
} else { |
||||
|
this.createAlarmConfigForm.get('alarmType').setValidators([Validators.required]); |
||||
|
this.createAlarmConfigForm.get('severity').setValidators([Validators.required]); |
||||
|
} |
||||
|
this.createAlarmConfigForm.get('alarmType').updateValueAndValidity({emitEvent}); |
||||
|
this.createAlarmConfigForm.get('severity').updateValueAndValidity({emitEvent}); |
||||
|
|
||||
|
let scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { |
||||
|
scriptLang = ScriptLanguage.JS; |
||||
|
this.createAlarmConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); |
||||
|
setTimeout(() => {this.createAlarmConfigForm.updateValueAndValidity({emitEvent: true});}); |
||||
|
} |
||||
|
const useAlarmDetailsBuildScript = useMessageAlarmData === false || overwriteAlarmDetails === true; |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildJs') |
||||
|
.setValidators(useAlarmDetailsBuildScript && scriptLang === ScriptLanguage.JS ? [Validators.required] : []); |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildTbel') |
||||
|
.setValidators(useAlarmDetailsBuildScript && scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildJs').updateValueAndValidity({emitEvent}); |
||||
|
this.createAlarmConfigForm.get('alarmDetailsBuildTbel').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
if (configuration) { |
||||
|
if (!configuration.scriptLang) { |
||||
|
configuration.scriptLang = ScriptLanguage.JS; |
||||
|
} |
||||
|
} |
||||
|
return configuration; |
||||
|
} |
||||
|
|
||||
|
testScript(debugEventBody?: DebugRuleNodeEventBody) { |
||||
|
const scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value; |
||||
|
const scriptField = scriptLang === ScriptLanguage.JS ? 'alarmDetailsBuildJs' : 'alarmDetailsBuildTbel'; |
||||
|
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/create_alarm_node_script_fn' : 'rulenode/tbel/create_alarm_node_script_fn'; |
||||
|
const script: string = this.createAlarmConfigForm.get(scriptField).value; |
||||
|
this.nodeScriptTestService.testNodeScript( |
||||
|
script, |
||||
|
'json', |
||||
|
this.translate.instant('tb.rulenode.details'), |
||||
|
'Details', |
||||
|
['msg', 'metadata', 'msgType'], |
||||
|
this.ruleNodeId, |
||||
|
helpId, |
||||
|
scriptLang, |
||||
|
debugEventBody |
||||
|
).subscribe((theScript) => { |
||||
|
if (theScript) { |
||||
|
this.createAlarmConfigForm.get(scriptField).setValue(theScript); |
||||
|
this.changeScript.emit(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeKey(key: string, keysField: string): void { |
||||
|
const keys: string[] = this.createAlarmConfigForm.get(keysField).value; |
||||
|
const index = keys.indexOf(key); |
||||
|
if (index >= 0) { |
||||
|
keys.splice(index, 1); |
||||
|
this.createAlarmConfigForm.get(keysField).setValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addKey(event: MatChipInputEvent, keysField: string): void { |
||||
|
const input = event.input; |
||||
|
let value = event.value; |
||||
|
if ((value || '').trim()) { |
||||
|
value = value.trim(); |
||||
|
let keys: string[] = this.createAlarmConfigForm.get(keysField).value; |
||||
|
if (!keys || keys.indexOf(value) === -1) { |
||||
|
if (!keys) { |
||||
|
keys = []; |
||||
|
} |
||||
|
keys.push(value); |
||||
|
this.createAlarmConfigForm.get(keysField).setValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
if (input) { |
||||
|
input.value = ''; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected onValidate() { |
||||
|
const useMessageAlarmData: boolean = this.createAlarmConfigForm.get('useMessageAlarmData').value; |
||||
|
const overwriteAlarmDetails: boolean = this.createAlarmConfigForm.get('overwriteAlarmDetails').value; |
||||
|
if (!useMessageAlarmData || overwriteAlarmDetails) { |
||||
|
const scriptLang: ScriptLanguage = this.createAlarmConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.JS) { |
||||
|
this.jsFuncComponent.validateOnSubmit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="createRelationConfigForm" class="tb-form-panel no-padding no-border"> |
||||
|
<div class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.relation-parameters</div> |
||||
|
<div class="flex flex-col"> |
||||
|
<mat-form-field class="mat-block" hideRequiredMarker> |
||||
|
<mat-label translate>relation.direction</mat-label> |
||||
|
<mat-select required matInput formControlName="direction"> |
||||
|
<mat-option *ngFor="let type of directionTypes" [value]="type"> |
||||
|
{{ directionTypeTranslations.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<tb-relation-type-autocomplete |
||||
|
required |
||||
|
formControlName="relationType"> |
||||
|
</tb-relation-type-autocomplete> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.target-entity</div> |
||||
|
<div class="flex flex-row gap-4"> |
||||
|
<tb-entity-type-select |
||||
|
class="flex-1" |
||||
|
showLabel |
||||
|
required |
||||
|
[allowedEntityTypes]="allowedEntityTypes" |
||||
|
formControlName="entityType"> |
||||
|
</tb-entity-type-select> |
||||
|
|
||||
|
<mat-form-field *ngIf="createRelationConfigForm.get('entityType').value && |
||||
|
createRelationConfigForm.get('entityType').value !== entityType.TENANT" |
||||
|
class="mat-block flex-1"> |
||||
|
<mat-label>{{ entityTypeNamePatternTranslation.get(createRelationConfigForm.get('entityType').value) | translate }}</mat-label> |
||||
|
<input required matInput formControlName="entityNamePattern"> |
||||
|
</mat-form-field> |
||||
|
|
||||
|
<mat-form-field *ngIf="createRelationConfigForm.get('entityType').value === entityType.DEVICE || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.ASSET" |
||||
|
class="mat-block flex-1"> |
||||
|
<mat-label translate>tb.rulenode.profile-name</mat-label> |
||||
|
<input matInput formControlName="entityTypePattern"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
|
||||
|
<tb-example-hint [hintText]="'tb.rulenode.kv-map-pattern-hint'" |
||||
|
*ngIf="createRelationConfigForm.get('entityType').value === entityType.CUSTOMER || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.DEVICE || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.ASSET"></tb-example-hint> |
||||
|
|
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.create-entity-if-not-exists-hint' | translate }}" |
||||
|
*ngIf="createRelationConfigForm.get('entityType').value === entityType.CUSTOMER || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.DEVICE || |
||||
|
createRelationConfigForm.get('entityType').value === entityType.ASSET" |
||||
|
style="margin-bottom: 18px" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="createEntityIfNotExists"> |
||||
|
{{ 'tb.rulenode.create-entity-if-not-exists' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
<section class="tb-form-panel stroked no-padding"> |
||||
|
<mat-expansion-panel class="tb-settings"> |
||||
|
<mat-expansion-panel-header style="padding: 16px"> |
||||
|
<mat-panel-title translate>tb.rulenode.advanced-settings</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<div class="tb-form-panel no-border no-padding-top"> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.remove-current-relations-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="removeCurrentRelations"> |
||||
|
{{ 'tb.rulenode.remove-current-relations' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.change-originator-to-related-entity-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="changeOriginatorToRelatedEntity"> |
||||
|
{{ 'tb.rulenode.change-originator-to-related-entity' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,107 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; |
||||
|
import { EntitySearchDirection } from '@app/shared/models/relation.models'; |
||||
|
import { EntityType } from '@app/shared/models/entity-type.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-create-relation-config', |
||||
|
templateUrl: './create-relation-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class CreateRelationConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
directionTypes = Object.keys(EntitySearchDirection); |
||||
|
directionTypeTranslations = new Map<EntitySearchDirection, string>( |
||||
|
[ |
||||
|
[EntitySearchDirection.FROM, 'tb.rulenode.search-direction-from'], |
||||
|
[EntitySearchDirection.TO, 'tb.rulenode.search-direction-to'], |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
entityType = EntityType; |
||||
|
|
||||
|
entityTypeNamePatternTranslation = new Map<EntityType, string>( |
||||
|
[ |
||||
|
[EntityType.DEVICE, 'tb.rulenode.device-name-pattern'], |
||||
|
[EntityType.ASSET, 'tb.rulenode.asset-name-pattern'], |
||||
|
[EntityType.ENTITY_VIEW, 'tb.rulenode.entity-view-name-pattern'], |
||||
|
[EntityType.CUSTOMER, 'tb.rulenode.customer-title-pattern'], |
||||
|
[EntityType.USER, 'tb.rulenode.user-name-pattern'], |
||||
|
[EntityType.DASHBOARD, 'tb.rulenode.dashboard-name-pattern'], |
||||
|
[EntityType.EDGE, 'tb.rulenode.edge-name-pattern'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.TENANT, |
||||
|
EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.EDGE]; |
||||
|
|
||||
|
createRelationConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.createRelationConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.createRelationConfigForm = this.fb.group({ |
||||
|
direction: [configuration ? configuration.direction : null, [Validators.required]], |
||||
|
entityType: [configuration ? configuration.entityType : null, [Validators.required]], |
||||
|
entityNamePattern: [configuration ? configuration.entityNamePattern : null, []], |
||||
|
entityTypePattern: [configuration ? configuration.entityTypePattern : null, []], |
||||
|
relationType: [configuration ? configuration.relationType : null, [Validators.required]], |
||||
|
createEntityIfNotExists: [configuration ? configuration.createEntityIfNotExists : false, []], |
||||
|
removeCurrentRelations: [configuration ? configuration.removeCurrentRelations : false, []], |
||||
|
changeOriginatorToRelatedEntity: [configuration ? configuration.changeOriginatorToRelatedEntity : false, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['entityType', 'createEntityIfNotExists']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const entityType: EntityType = this.createRelationConfigForm.get('entityType').value; |
||||
|
if (entityType) { |
||||
|
this.createRelationConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]); |
||||
|
} else { |
||||
|
this.createRelationConfigForm.get('entityNamePattern').setValidators([]); |
||||
|
} |
||||
|
if (entityType && (entityType === EntityType.DEVICE || entityType === EntityType.ASSET)) { |
||||
|
const validators = [Validators.pattern(/.*\S.*/)] |
||||
|
if (this.createRelationConfigForm.get('createEntityIfNotExists').value) { |
||||
|
validators.push(Validators.required); |
||||
|
} |
||||
|
this.createRelationConfigForm.get('entityTypePattern').setValidators(validators); |
||||
|
} else { |
||||
|
this.createRelationConfigForm.get('entityTypePattern').setValidators([]); |
||||
|
} |
||||
|
this.createRelationConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent}); |
||||
|
this.createRelationConfigForm.get('entityTypePattern').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
configuration.entityNamePattern = configuration.entityNamePattern ? configuration.entityNamePattern.trim() : null; |
||||
|
configuration.entityTypePattern = configuration.entityTypePattern ? configuration.entityTypePattern.trim() : null; |
||||
|
return configuration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,89 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deleteAttributesConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<tb-example-hint [hintText]="'tb.rulenode.attributes-scope-hint'"> |
||||
|
</tb-example-hint> |
||||
|
<div class="tb-form-row no-border no-padding tb-standard-fields"> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'tb.rulenode.attributes-scope' | translate }}</mat-label> |
||||
|
<mat-select required |
||||
|
class="tb-entity-type-select" matInput formControlName="scope"> |
||||
|
<mat-option *ngFor="let scope of attributeScopes" [value]="scope"> |
||||
|
{{ telemetryTypeTranslationsMap.get(scope) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label>{{ 'tb.rulenode.attributes-scope-value' | translate }}</mat-label> |
||||
|
<input type="text" matInput readonly disabled [ngModel]="deleteAttributesConfigForm.get('scope').value" [ngModelOptions]="{standalone: true}"> |
||||
|
<button type="button" |
||||
|
matSuffix |
||||
|
mat-icon-button |
||||
|
aria-label="Copy" |
||||
|
ngxClipboard |
||||
|
[cbContent]="deleteAttributesConfigForm.get('scope').value" |
||||
|
matTooltip="{{ 'tb.rulenode.attributes-scope-value-copy' | translate }}"> |
||||
|
<mat-icon aria-hidden="false" |
||||
|
aria-label="help-icon">content_copy |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label>{{ 'tb.rulenode.attributes-keys' | translate }}</mat-label> |
||||
|
<mat-chip-grid formControlName="keys" #attributeChipList> |
||||
|
<mat-chip-row |
||||
|
*ngFor="let key of deleteAttributesConfigForm.get('keys').value;" |
||||
|
(removed)="removeKey(key)"> |
||||
|
{{key}} |
||||
|
<mat-icon matChipRemove>close</mat-icon> |
||||
|
</mat-chip-row> |
||||
|
<input matInput type="text" |
||||
|
[matChipInputFor]="attributeChipList" |
||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" |
||||
|
(matChipInputTokenEnd)="addKey($event)" |
||||
|
[matChipInputAddOnBlur]="true"> |
||||
|
</mat-chip-grid> |
||||
|
<mat-error *ngIf="deleteAttributesConfigForm.get('keys').hasError('required')">{{ 'tb.rulenode.attributes-keys-required' | translate }}</mat-error> |
||||
|
<mat-hint translate>tb.rulenode.general-pattern-hint</mat-hint> |
||||
|
</mat-form-field> |
||||
|
|
||||
|
<section class="tb-form-panel stroked"> |
||||
|
<mat-expansion-panel class="tb-settings"> |
||||
|
<mat-expansion-panel-header> |
||||
|
<mat-panel-title translate>tb.rulenode.advanced-settings</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.send-attributes-deleted-notification-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="sendAttributesDeletedNotification"> |
||||
|
{{ 'tb.rulenode.send-attributes-deleted-notification' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.notify-device-on-delete-hint' | translate }}" |
||||
|
*ngIf="deleteAttributesConfigForm.get('scope').value === attributeScopeMap.SHARED_SCOPE" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="notifyDevice"> |
||||
|
{{ 'tb.rulenode.notify-device' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,88 @@ |
|||||
|
///
|
||||
|
/// 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 { Component, ViewChild } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips'; |
||||
|
import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; |
||||
|
import { AttributeScope, telemetryTypeTranslations } from '@shared/models/telemetry/telemetry.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-delete-attributes-config', |
||||
|
templateUrl: './delete-attributes-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeleteAttributesConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
@ViewChild('attributeChipList') attributeChipList: MatChipGrid; |
||||
|
|
||||
|
deleteAttributesConfigForm: UntypedFormGroup; |
||||
|
attributeScopeMap = AttributeScope; |
||||
|
attributeScopes = Object.keys(AttributeScope); |
||||
|
telemetryTypeTranslationsMap = telemetryTypeTranslations; |
||||
|
separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.deleteAttributesConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deleteAttributesConfigForm = this.fb.group({ |
||||
|
scope: [configuration ? configuration.scope : null, [Validators.required]], |
||||
|
keys: [configuration ? configuration.keys : null, [Validators.required]], |
||||
|
sendAttributesDeletedNotification: [configuration ? configuration.sendAttributesDeletedNotification : false, []], |
||||
|
notifyDevice: [configuration ? configuration.notifyDevice : false, []] |
||||
|
}); |
||||
|
|
||||
|
this.deleteAttributesConfigForm.get('scope').valueChanges.subscribe((value) => { |
||||
|
if (value !== AttributeScope.SHARED_SCOPE) { |
||||
|
this.deleteAttributesConfigForm.get('notifyDevice').patchValue(false, {emitEvent: false}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
removeKey(key: string): void { |
||||
|
const keys: string[] = this.deleteAttributesConfigForm.get('keys').value; |
||||
|
const index = keys.indexOf(key); |
||||
|
if (index >= 0) { |
||||
|
keys.splice(index, 1); |
||||
|
this.deleteAttributesConfigForm.get('keys').patchValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
addKey(event: MatChipInputEvent): void { |
||||
|
const input = event.input; |
||||
|
let value = event.value; |
||||
|
if ((value || '').trim()) { |
||||
|
value = value.trim(); |
||||
|
let keys: string[] = this.deleteAttributesConfigForm.get('keys').value; |
||||
|
if (!keys || keys.indexOf(value) === -1) { |
||||
|
if (!keys) { |
||||
|
keys = []; |
||||
|
} |
||||
|
keys.push(value); |
||||
|
this.deleteAttributesConfigForm.get('keys').patchValue(keys, {emitEvent: true}); |
||||
|
} |
||||
|
} |
||||
|
if (input) { |
||||
|
input.value = ''; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deleteRelationConfigForm" class="tb-form-panel no-padding no-border"> |
||||
|
<div class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.relation-parameters</div> |
||||
|
<div class="flex flex-col"> |
||||
|
<mat-form-field class="mat-block" hideRequiredMarker> |
||||
|
<mat-label translate>relation.direction</mat-label> |
||||
|
<mat-select required matInput formControlName="direction"> |
||||
|
<mat-option *ngFor="let type of directionTypes" [value]="type"> |
||||
|
{{ directionTypeTranslations.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<tb-relation-type-autocomplete |
||||
|
required |
||||
|
formControlName="relationType"> |
||||
|
</tb-relation-type-autocomplete> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<div tb-hint-tooltip-icon="{{ 'tb.rulenode.delete-relation-with-specific-entity-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="deleteForSingleEntity"> |
||||
|
{{ 'tb.rulenode.delete-relation-with-specific-entity' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div *ngIf="deleteRelationConfigForm.get('deleteForSingleEntity').value"> |
||||
|
<div class="flex flex-row gap-2.5"> |
||||
|
<tb-entity-type-select |
||||
|
class="flex-1" |
||||
|
showLabel |
||||
|
required |
||||
|
[allowedEntityTypes]="allowedEntityTypes" |
||||
|
formControlName="entityType"> |
||||
|
</tb-entity-type-select> |
||||
|
<mat-form-field *ngIf="deleteRelationConfigForm.get('entityType').value && deleteRelationConfigForm.get('entityType').value !== entityType.TENANT" |
||||
|
class="mat-block flex-1"> |
||||
|
<mat-label>{{ entityTypeNamePatternTranslation.get(deleteRelationConfigForm.get('entityType').value) | translate }}</mat-label> |
||||
|
<input required matInput formControlName="entityNamePattern"> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<tb-example-hint *ngIf="deleteRelationConfigForm.get('entityType').value && deleteRelationConfigForm.get('entityType').value !== entityType.TENANT" |
||||
|
[hintText]="'tb.rulenode.kv-map-single-pattern-hint'"></tb-example-hint> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,101 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { EntityType } from '@app/shared/models/entity-type.models'; |
||||
|
import { EntitySearchDirection } from '@app/shared/models/relation.models'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@app/shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-delete-relation-config', |
||||
|
templateUrl: './delete-relation-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeleteRelationConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
directionTypes = Object.keys(EntitySearchDirection); |
||||
|
|
||||
|
directionTypeTranslations = new Map<EntitySearchDirection, string>( |
||||
|
[ |
||||
|
[EntitySearchDirection.FROM, 'tb.rulenode.del-relation-direction-from'], |
||||
|
[EntitySearchDirection.TO, 'tb.rulenode.del-relation-direction-to'], |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
entityTypeNamePatternTranslation = new Map<EntityType, string>( |
||||
|
[ |
||||
|
[EntityType.DEVICE, 'tb.rulenode.device-name-pattern'], |
||||
|
[EntityType.ASSET, 'tb.rulenode.asset-name-pattern'], |
||||
|
[EntityType.ENTITY_VIEW, 'tb.rulenode.entity-view-name-pattern'], |
||||
|
[EntityType.CUSTOMER, 'tb.rulenode.customer-title-pattern'], |
||||
|
[EntityType.USER, 'tb.rulenode.user-name-pattern'], |
||||
|
[EntityType.DASHBOARD, 'tb.rulenode.dashboard-name-pattern'], |
||||
|
[EntityType.EDGE, 'tb.rulenode.edge-name-pattern'] |
||||
|
] |
||||
|
); |
||||
|
|
||||
|
entityType = EntityType; |
||||
|
|
||||
|
allowedEntityTypes = [EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.TENANT, |
||||
|
EntityType.CUSTOMER, EntityType.USER, EntityType.DASHBOARD, EntityType.EDGE]; |
||||
|
|
||||
|
deleteRelationConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.deleteRelationConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deleteRelationConfigForm = this.fb.group({ |
||||
|
deleteForSingleEntity: [configuration ? configuration.deleteForSingleEntity : false, []], |
||||
|
direction: [configuration ? configuration.direction : null, [Validators.required]], |
||||
|
entityType: [configuration ? configuration.entityType : null, []], |
||||
|
entityNamePattern: [configuration ? configuration.entityNamePattern : null, []], |
||||
|
relationType: [configuration ? configuration.relationType : null, [Validators.required]] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['deleteForSingleEntity', 'entityType']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const deleteForSingleEntity: boolean = this.deleteRelationConfigForm.get('deleteForSingleEntity').value; |
||||
|
const entityType: EntityType = this.deleteRelationConfigForm.get('entityType').value; |
||||
|
if (deleteForSingleEntity) { |
||||
|
this.deleteRelationConfigForm.get('entityType').setValidators([Validators.required]); |
||||
|
} else { |
||||
|
this.deleteRelationConfigForm.get('entityType').setValidators([]); |
||||
|
} |
||||
|
if (deleteForSingleEntity && entityType && entityType !== EntityType.TENANT) { |
||||
|
this.deleteRelationConfigForm.get('entityNamePattern').setValidators([Validators.required, Validators.pattern(/.*\S.*/)]); |
||||
|
} else { |
||||
|
this.deleteRelationConfigForm.get('entityNamePattern').setValidators([]); |
||||
|
} |
||||
|
this.deleteRelationConfigForm.get('entityType').updateValueAndValidity({emitEvent: false}); |
||||
|
this.deleteRelationConfigForm.get('entityNamePattern').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
configuration.entityNamePattern = configuration.entityNamePattern ? configuration.entityNamePattern.trim() : null; |
||||
|
return configuration; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deviceProfile" class="tb-form-panel stroked"> |
||||
|
<div class="tb-form-hint tb-primary-fill" translate>tb.rulenode.device-profile-node-hint</div> |
||||
|
<div tb-hint-tooltip-icon="{{'tb.rulenode.persist-alarm-rules-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding slide-toggle"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="persistAlarmRulesState"> |
||||
|
{{ 'tb.rulenode.persist-alarm-rules' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<div tb-hint-tooltip-icon="{{'tb.rulenode.fetch-alarm-rules-hint' | translate }}" |
||||
|
class="tb-form-row no-border no-padding slide-toggle"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="fetchAlarmRulesStateOnStart"> |
||||
|
{{ 'tb.rulenode.fetch-alarm-rules' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,59 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-device-profile-config', |
||||
|
templateUrl: './device-profile-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeviceProfileConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
deviceProfile: UntypedFormGroup; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.deviceProfile; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deviceProfile = this.fb.group({ |
||||
|
persistAlarmRulesState: [configuration ? configuration.persistAlarmRulesState : false], |
||||
|
fetchAlarmRulesStateOnStart: [configuration ? configuration.fetchAlarmRulesStateOnStart : false] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['persistAlarmRulesState']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
if (this.deviceProfile.get('persistAlarmRulesState').value) { |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').enable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').setValue(false, {emitEvent: false}); |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').disable({emitEvent: false}); |
||||
|
} |
||||
|
this.deviceProfile.get('fetchAlarmRulesStateOnStart').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="deviceState"> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label>{{ 'tb.rulenode.select-device-connectivity-event' | translate }}</mat-label> |
||||
|
<mat-select formControlName="event"> |
||||
|
<mat-option *ngFor="let eventOption of eventOptions" [value]="eventOption"> |
||||
|
{{ messageTypeNames.get(eventOption) }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</section> |
||||
@ -0,0 +1,59 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { isDefinedAndNotNull } from '@core/public-api'; |
||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
||||
|
import { MessageType, messageTypeNames, RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-device-state-config', |
||||
|
templateUrl: './device-state-config.component.html', |
||||
|
styleUrls: [] |
||||
|
}) |
||||
|
export class DeviceStateConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
deviceState: FormGroup; |
||||
|
|
||||
|
public messageTypeNames = messageTypeNames; |
||||
|
public eventOptions: MessageType[] = [ |
||||
|
MessageType.CONNECT_EVENT, |
||||
|
MessageType.ACTIVITY_EVENT, |
||||
|
MessageType.DISCONNECT_EVENT, |
||||
|
MessageType.INACTIVITY_EVENT |
||||
|
]; |
||||
|
|
||||
|
constructor(private fb: FormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): FormGroup { |
||||
|
return this.deviceState; |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
return { |
||||
|
event: isDefinedAndNotNull(configuration?.event) ? configuration.event : MessageType.ACTIVITY_EVENT |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.deviceState = this.fb.group({ |
||||
|
event: [configuration.event, [Validators.required]] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="generatorConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<div class="tb-form-panel no-padding-bottom stroked"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.generation-parameters</div> |
||||
|
<div class="tb-form-row no-border no-padding tb-standard-fields column-xs"> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label translate>tb.rulenode.message-count</mat-label> |
||||
|
<input required type="number" min="0" step="1" matInput formControlName="msgCount"> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('msgCount').hasError('required')"> |
||||
|
{{ 'tb.rulenode.message-count-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('msgCount').hasError('min')"> |
||||
|
{{ 'tb.rulenode.min-message-count-message' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex"> |
||||
|
<mat-label translate>tb.rulenode.generation-frequency-seconds</mat-label> |
||||
|
<input required type="number" min="1" step="1" matInput formControlName="periodInSeconds"> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('periodInSeconds').hasError('required')"> |
||||
|
{{ 'tb.rulenode.generation-frequency-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="generatorConfigForm.get('periodInSeconds').hasError('min')"> |
||||
|
{{ 'tb.rulenode.min-generation-frequency-message' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="tb-form-panel no-padding-bottom stroked"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.originator</div> |
||||
|
<tb-entity-select class="flex-1" |
||||
|
required="true" |
||||
|
useAliasEntityTypes="true" |
||||
|
[allowedEntityTypes]="allowedEntityTypes" |
||||
|
[additionEntityTypes]="additionEntityTypes" |
||||
|
formControlName="originator"> |
||||
|
</tb-entity-select> |
||||
|
</div> |
||||
|
<div class="tb-form-panel stroked"> |
||||
|
<mat-expansion-panel class="tb-settings" expanded> |
||||
|
<mat-expansion-panel-header> |
||||
|
<mat-panel-title translate>tb.rulenode.generator-function</mat-panel-title> |
||||
|
</mat-expansion-panel-header> |
||||
|
<tb-js-func *ngIf="generatorConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="jsScript" |
||||
|
functionName="Generate" |
||||
|
[functionArgs]="['prevMsg', 'prevMetadata', 'prevMsgType']" |
||||
|
helpId="rulenode/generator_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<tb-toggle-select toolbarPrefixButton formControlName="scriptLang" appearance="fill" *ngIf="tbelEnabled"> |
||||
|
<tb-toggle-option [value]="scriptLanguage.TBEL"> |
||||
|
{{ 'tb.rulenode.script-lang-tbel' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
<tb-toggle-option [value]="scriptLanguage.JS"> |
||||
|
{{ 'tb.rulenode.script-lang-js' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
</tb-toggle-select> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="generatorConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="tbelScript" |
||||
|
functionName="Generate" |
||||
|
[functionArgs]="['prevMsg', 'prevMetadata', 'prevMsgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/generator_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<tb-toggle-select toolbarPrefixButton formControlName="scriptLang" appearance="fill"> |
||||
|
<tb-toggle-option [value]="scriptLanguage.TBEL"> |
||||
|
{{ 'tb.rulenode.script-lang-tbel' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
<tb-toggle-option [value]="scriptLanguage.JS"> |
||||
|
{{ 'tb.rulenode.script-lang-js' | translate }} |
||||
|
</tb-toggle-option> |
||||
|
</tb-toggle-select> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row" style="padding-bottom: 16px;"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</mat-expansion-panel> |
||||
|
</div> |
||||
|
</section> |
||||
@ -0,0 +1,51 @@ |
|||||
|
/** |
||||
|
* 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. |
||||
|
*/ |
||||
|
:host { |
||||
|
::ng-deep { |
||||
|
.mat-button-toggle-group { |
||||
|
min-width: 120px; |
||||
|
height: 24px !important; |
||||
|
.mat-button-toggle { |
||||
|
font-size: 0; |
||||
|
.mat-button-toggle-button { |
||||
|
height: 20px!important; |
||||
|
line-height: 20px !important; |
||||
|
border: none !important; |
||||
|
.mat-button-toggle-label-content { |
||||
|
font-size: 14px !important; |
||||
|
line-height: 20px !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.tb-entity-select { |
||||
|
@media screen and (min-width: 599px) { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
gap: 16px; |
||||
|
} |
||||
|
tb-entity-type-select { |
||||
|
flex: 1; |
||||
|
} |
||||
|
tb-entity-autocomplete { |
||||
|
flex: 1; |
||||
|
mat-form-field { |
||||
|
width: 100% !important; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
///
|
||||
|
/// 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 { Component, EventEmitter, ViewChild } from '@angular/core'; |
||||
|
import { getCurrentAuthState, isDefinedAndNotNull, NodeScriptTestService } from '@core/public-api'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { |
||||
|
RuleNodeConfiguration, |
||||
|
RuleNodeConfigurationComponent, |
||||
|
ScriptLanguage |
||||
|
} from '@app/shared/models/rule-node.models'; |
||||
|
import type { JsFuncComponent } from '@app/shared/components/js-func.component'; |
||||
|
import { EntityType } from '@app/shared/models/entity-type.models'; |
||||
|
import { DebugRuleNodeEventBody } from '@shared/models/event.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-generator-config', |
||||
|
templateUrl: './generator-config.component.html', |
||||
|
styleUrls: ['generator-config.component.scss'] |
||||
|
}) |
||||
|
export class GeneratorConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
@ViewChild('jsFuncComponent', {static: false}) jsFuncComponent: JsFuncComponent; |
||||
|
@ViewChild('tbelFuncComponent', {static: false}) tbelFuncComponent: JsFuncComponent; |
||||
|
|
||||
|
generatorConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
tbelEnabled = getCurrentAuthState(this.store).tbelEnabled; |
||||
|
|
||||
|
scriptLanguage = ScriptLanguage; |
||||
|
|
||||
|
changeScript: EventEmitter<void> = new EventEmitter<void>(); |
||||
|
|
||||
|
allowedEntityTypes = [ |
||||
|
EntityType.DEVICE, EntityType.ASSET, EntityType.ENTITY_VIEW, EntityType.CUSTOMER, |
||||
|
EntityType.USER, EntityType.DASHBOARD |
||||
|
]; |
||||
|
|
||||
|
additionEntityTypes = { |
||||
|
TENANT: this.translate.instant('tb.rulenode.current-tenant'), |
||||
|
RULE_NODE: this.translate.instant('tb.rulenode.current-rule-node') |
||||
|
}; |
||||
|
|
||||
|
readonly hasScript = true; |
||||
|
|
||||
|
readonly testScriptLabel = 'tb.rulenode.test-generator-function'; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder, |
||||
|
private nodeScriptTestService: NodeScriptTestService, |
||||
|
private translate: TranslateService) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.generatorConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.generatorConfigForm = this.fb.group({ |
||||
|
msgCount: [configuration ? configuration.msgCount : null, [Validators.required, Validators.min(0)]], |
||||
|
periodInSeconds: [configuration ? configuration.periodInSeconds : null, [Validators.required, Validators.min(1)]], |
||||
|
originator: [configuration ? configuration.originator : {id: null, entityType: EntityType.RULE_NODE}, []], |
||||
|
scriptLang: [configuration ? configuration.scriptLang : ScriptLanguage.JS, [Validators.required]], |
||||
|
jsScript: [configuration ? configuration.jsScript : null, []], |
||||
|
tbelScript: [configuration ? configuration.tbelScript : null, []] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['scriptLang']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
let scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.TBEL && !this.tbelEnabled) { |
||||
|
scriptLang = ScriptLanguage.JS; |
||||
|
this.generatorConfigForm.get('scriptLang').patchValue(scriptLang, {emitEvent: false}); |
||||
|
setTimeout(() => {this.generatorConfigForm.updateValueAndValidity({emitEvent: true});}); |
||||
|
} |
||||
|
this.generatorConfigForm.get('jsScript').setValidators(scriptLang === ScriptLanguage.JS ? [Validators.required] : []); |
||||
|
this.generatorConfigForm.get('jsScript').updateValueAndValidity({emitEvent}); |
||||
|
this.generatorConfigForm.get('tbelScript').setValidators(scriptLang === ScriptLanguage.TBEL ? [Validators.required] : []); |
||||
|
this.generatorConfigForm.get('tbelScript').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
|
||||
|
protected prepareInputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
return { |
||||
|
msgCount: isDefinedAndNotNull(configuration?.msgCount) ? configuration?.msgCount : 0, |
||||
|
periodInSeconds: isDefinedAndNotNull(configuration?.periodInSeconds) ? configuration?.periodInSeconds : 1, |
||||
|
originator: { |
||||
|
id: isDefinedAndNotNull(configuration?.originatorId) ? configuration?.originatorId : null, |
||||
|
entityType: isDefinedAndNotNull(configuration?.originatorType) ? configuration?.originatorType : EntityType.RULE_NODE |
||||
|
}, |
||||
|
scriptLang: isDefinedAndNotNull(configuration?.scriptLang) ? configuration?.scriptLang : ScriptLanguage.JS, |
||||
|
tbelScript: isDefinedAndNotNull(configuration?.tbelScript) ? configuration?.tbelScript : null, |
||||
|
jsScript: isDefinedAndNotNull(configuration?.jsScript) ? configuration?.jsScript : null, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected prepareOutputConfig(configuration: RuleNodeConfiguration): RuleNodeConfiguration { |
||||
|
if (configuration.originator) { |
||||
|
configuration.originatorId = configuration.originator.id; |
||||
|
configuration.originatorType = configuration.originator.entityType; |
||||
|
} else { |
||||
|
configuration.originatorId = null; |
||||
|
configuration.originatorType = null; |
||||
|
} |
||||
|
delete configuration.originator; |
||||
|
return configuration; |
||||
|
} |
||||
|
|
||||
|
testScript(debugEventBody?: DebugRuleNodeEventBody) { |
||||
|
const scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value; |
||||
|
const scriptField = scriptLang === ScriptLanguage.JS ? 'jsScript' : 'tbelScript'; |
||||
|
const helpId = scriptLang === ScriptLanguage.JS ? 'rulenode/generator_node_script_fn' : 'rulenode/tbel/generator_node_script_fn'; |
||||
|
const script: string = this.generatorConfigForm.get(scriptField).value; |
||||
|
this.nodeScriptTestService.testNodeScript( |
||||
|
script, |
||||
|
'generate', |
||||
|
this.translate.instant('tb.rulenode.generator'), |
||||
|
'Generate', |
||||
|
['prevMsg', 'prevMetadata', 'prevMsgType'], |
||||
|
this.ruleNodeId, |
||||
|
helpId, |
||||
|
scriptLang, |
||||
|
debugEventBody |
||||
|
).subscribe((theScript) => { |
||||
|
if (theScript) { |
||||
|
this.generatorConfigForm.get(scriptField).setValue(theScript); |
||||
|
this.changeScript.emit(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected onValidate() { |
||||
|
const scriptLang: ScriptLanguage = this.generatorConfigForm.get('scriptLang').value; |
||||
|
if (scriptLang === ScriptLanguage.JS) { |
||||
|
this.jsFuncComponent.validateOnSubmit(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,195 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="geoActionConfigForm" class="tb-form-panel no-border no-padding"> |
||||
|
<section class="tb-form-panel stroked"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.coordinate-field-names</div> |
||||
|
<section> |
||||
|
<div class="flex flex-row gap-4"> |
||||
|
<mat-form-field class="mat-block max-w-50% flex-full"> |
||||
|
<mat-label>{{ 'tb.rulenode.latitude-field-name' | translate }}</mat-label> |
||||
|
<input matInput formControlName="latitudeKeyName" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('latitudeKeyName').hasError('required')"> |
||||
|
{{ 'tb.rulenode.latitude-field-name-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="mat-block max-w-50% flex-full"> |
||||
|
<mat-label>{{ 'tb.rulenode.longitude-field-name' | translate }}</mat-label> |
||||
|
<input matInput formControlName="longitudeKeyName" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('longitudeKeyName').hasError('required')"> |
||||
|
{{ 'tb.rulenode.longitude-field-name-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="tb-form-hint tb-primary-fill" translate>tb.rulenode.coordinate-field-hint</div> |
||||
|
</section> |
||||
|
</section> |
||||
|
<section class="tb-form-panel stroked" [class.no-padding-bottom]="!defaultPaddingEnable"> |
||||
|
<div class="tb-form-panel-title" translate>tb.rulenode.geofence-configuration</div> |
||||
|
<section class="flex flex-col"> |
||||
|
<mat-form-field class="mat-block flex-1" hideRequiredMarker> |
||||
|
<mat-label>{{ 'tb.rulenode.perimeter-type' | translate }}</mat-label> |
||||
|
<mat-select formControlName="perimeterType"> |
||||
|
<mat-option *ngFor="let type of perimeterTypes" [value]="type"> |
||||
|
{{ perimeterTypeTranslationMap.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
<div tb-hint-tooltip-icon="{{ geoActionConfigForm.get('perimeterType').value === perimeterType.CIRCLE |
||||
|
? ('tb.rulenode.fetch-circle-parameter-info-from-metadata-hint' |
||||
|
| translate : { perimeterKeyName: geoActionConfigForm.get('perimeterKeyName').valid |
||||
|
? geoActionConfigForm.get('perimeterKeyName').value |
||||
|
: 'ss_perimeter'}) |
||||
|
: ('tb.rulenode.fetch-poligon-parameter-info-from-metadata-hint' |
||||
|
| translate : { perimeterKeyName: geoActionConfigForm.get('perimeterKeyName').valid |
||||
|
? geoActionConfigForm.get('perimeterKeyName').value |
||||
|
: 'ss_perimeter'}) }}" |
||||
|
class="tb-form-row no-border no-padding slide-toggle"> |
||||
|
<mat-slide-toggle class="mat-slide" formControlName="fetchPerimeterInfoFromMessageMetadata"> |
||||
|
{{ 'tb.rulenode.fetch-perimeter-info-from-metadata' | translate }} |
||||
|
</mat-slide-toggle> |
||||
|
</div> |
||||
|
<mat-form-field *ngIf="geoActionConfigForm.get('fetchPerimeterInfoFromMessageMetadata').value" class="mat-block"> |
||||
|
<mat-label>{{ 'tb.rulenode.perimeter-key-name' | translate }}</mat-label> |
||||
|
<input matInput formControlName="perimeterKeyName" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('perimeterKeyName').hasError('required')"> |
||||
|
{{ 'tb.rulenode.perimeter-key-name-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-hint>{{ 'tb.rulenode.perimeter-key-name-hint' | translate }}</mat-hint> |
||||
|
</mat-form-field> |
||||
|
<div class="flex flex-col" |
||||
|
*ngIf="geoActionConfigForm.get('perimeterType').value === perimeterType.CIRCLE && |
||||
|
!geoActionConfigForm.get('fetchPerimeterInfoFromMessageMetadata').value"> |
||||
|
<div class="flex flex-row gap-4"> |
||||
|
<mat-form-field class="flex-1"> |
||||
|
<mat-label>{{ 'tb.rulenode.circle-center-latitude' | translate }}</mat-label> |
||||
|
<input type="number" min="-90" max="90" step="0.1" matInput formControlName="centerLatitude" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('centerLatitude').hasError('required')"> |
||||
|
{{ 'tb.rulenode.circle-center-latitude-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex-1"> |
||||
|
<mat-label>{{ 'tb.rulenode.circle-center-longitude' | translate }}</mat-label> |
||||
|
<input type="number" min="-180" max="180" step="0.1" matInput formControlName="centerLongitude" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('centerLongitude').hasError('required')"> |
||||
|
{{ 'tb.rulenode.circle-center-longitude-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="flex flex-row gap-4"> |
||||
|
<mat-form-field class="flex-1"> |
||||
|
<mat-label>{{ 'tb.rulenode.range' | translate }}</mat-label> |
||||
|
<input type="number" min="0" step="0.1" matInput formControlName="range" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('range').hasError('required')"> |
||||
|
{{ 'tb.rulenode.range-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="flex-1"> |
||||
|
<mat-label>{{ 'tb.rulenode.range-units' | translate }}</mat-label> |
||||
|
<mat-select formControlName="rangeUnit" required> |
||||
|
<mat-option *ngFor="let type of rangeUnits" [value]="type"> |
||||
|
{{ rangeUnitTranslationMap.get(type) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('rangeUnit').hasError('required')"> |
||||
|
{{ 'tb.rulenode.range-units-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="flex flex-col" *ngIf="geoActionConfigForm.get('perimeterType').value === perimeterType.POLYGON && |
||||
|
!geoActionConfigForm.get('fetchPerimeterInfoFromMessageMetadata').value"> |
||||
|
<mat-form-field class="mat-block" subscriptSizing="dynamic"> |
||||
|
<mat-label translate>tb.rulenode.polygon-definition</mat-label> |
||||
|
<input matInput formControlName="polygonsDefinition" required> |
||||
|
<mat-icon matSuffix class="margin-8 cursor-pointer" aria-hidden="false" aria-label="help-icon" |
||||
|
color="primary" |
||||
|
matTooltip="{{ 'tb.rulenode.polygon-definition-hint' | translate }}"> |
||||
|
help |
||||
|
</mat-icon> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('polygonsDefinition').hasError('required')"> |
||||
|
{{ 'tb.rulenode.polygon-definition-required' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</section> |
||||
|
</section> |
||||
|
<section class="tb-form-panel stroked no-padding-bottom"> |
||||
|
<div class="flex flex-col items-stretch justify-between gt-sm:flex-row lt-md:gap-4"> |
||||
|
<div class="tb-form-panel-title">{{ 'tb.rulenode.presence-monitoring-strategy' | translate }}</div> |
||||
|
<tb-toggle-select class="fetch-to-data-toggle" formControlName="reportPresenceStatusOnEachMessage" appearance="fill" > |
||||
|
<tb-toggle-option *ngFor="let strategy of presenceMonitoringStrategyKeys" [value]="presenceMonitoringStrategies.get(strategy).value"> |
||||
|
{{ presenceMonitoringStrategies.get(strategy).name | translate }} |
||||
|
</tb-toggle-option> |
||||
|
</tb-toggle-select> |
||||
|
</div> |
||||
|
<div class="tb-form-hint tb-primary-fill"> {{ geoActionConfigForm.get('reportPresenceStatusOnEachMessage').value === false ? |
||||
|
('tb.rulenode.presence-monitoring-strategy-on-first-message-hint' | translate) : |
||||
|
('tb.rulenode.presence-monitoring-strategy-on-each-message-hint' | translate) }} |
||||
|
</div> |
||||
|
<section class="flex flex-col"> |
||||
|
<div *ngIf="geoActionConfigForm.get('reportPresenceStatusOnEachMessage').value === false"> |
||||
|
<div class="flex flex-col gt-sm:flex-row gt-sm:gap-2"> |
||||
|
<mat-form-field class="mat-block flex-1"> |
||||
|
<mat-label translate>tb.rulenode.min-inside-duration</mat-label> |
||||
|
<input type="number" step="1" min="1" max="2147483647" matInput formControlName="minInsideDuration" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('minInsideDuration').hasError('required')"> |
||||
|
{{ 'tb.rulenode.min-inside-duration-value-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('minInsideDuration').hasError('min')"> |
||||
|
{{ 'tb.rulenode.time-value-range' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('minInsideDuration').hasError('max')"> |
||||
|
{{ 'tb.rulenode.time-value-range' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="mat-block flex-1"> |
||||
|
<mat-label translate>tb.rulenode.min-inside-duration-time-unit</mat-label> |
||||
|
<mat-select formControlName="minInsideDurationTimeUnit" required> |
||||
|
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit"> |
||||
|
{{ timeUnitsTranslationMap.get(timeUnit) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div class="flex flex-col gt-sm:flex-row gt-sm:gap-2"> |
||||
|
<mat-form-field class="mat-block flex-1"> |
||||
|
<mat-label translate>tb.rulenode.min-outside-duration</mat-label> |
||||
|
<input type="number" step="1" min="1" max="2147483647" matInput formControlName="minOutsideDuration" required> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('minOutsideDuration').hasError('required')"> |
||||
|
{{ 'tb.rulenode.min-outside-duration-value-required' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('minOutsideDuration').hasError('min')"> |
||||
|
{{ 'tb.rulenode.time-value-range' | translate }} |
||||
|
</mat-error> |
||||
|
<mat-error *ngIf="geoActionConfigForm.get('minOutsideDuration').hasError('max')"> |
||||
|
{{ 'tb.rulenode.time-value-range' | translate }} |
||||
|
</mat-error> |
||||
|
</mat-form-field> |
||||
|
<mat-form-field class="mat-block flex-1"> |
||||
|
<mat-label translate>tb.rulenode.min-outside-duration-time-unit</mat-label> |
||||
|
<mat-select formControlName="minOutsideDurationTimeUnit" required> |
||||
|
<mat-option *ngFor="let timeUnit of timeUnits" [value]="timeUnit"> |
||||
|
{{ timeUnitsTranslationMap.get(timeUnit) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
</section> |
||||
|
</section> |
||||
@ -0,0 +1,20 @@ |
|||||
|
/** |
||||
|
* 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. |
||||
|
*/ |
||||
|
:host { |
||||
|
.slide-toggle { |
||||
|
margin-bottom: 18px; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,126 @@ |
|||||
|
///
|
||||
|
/// 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 { Component } from '@angular/core'; |
||||
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
||||
|
import { RuleNodeConfiguration, RuleNodeConfigurationComponent } from '@shared/models/rule-node.models'; |
||||
|
import { |
||||
|
PerimeterType, |
||||
|
perimeterTypeTranslations, |
||||
|
PresenceMonitoringStrategiesData, |
||||
|
RangeUnit, |
||||
|
rangeUnitTranslations, |
||||
|
TimeUnit, |
||||
|
timeUnitTranslations |
||||
|
} from '../rule-node-config.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-action-node-gps-geofencing-config', |
||||
|
templateUrl: './gps-geo-action-config.component.html', |
||||
|
styleUrls: ['./gps-geo-action-config.component.scss'] |
||||
|
}) |
||||
|
export class GpsGeoActionConfigComponent extends RuleNodeConfigurationComponent { |
||||
|
|
||||
|
geoActionConfigForm: UntypedFormGroup; |
||||
|
|
||||
|
perimeterType = PerimeterType; |
||||
|
perimeterTypes = Object.keys(PerimeterType); |
||||
|
perimeterTypeTranslationMap = perimeterTypeTranslations; |
||||
|
|
||||
|
rangeUnits = Object.keys(RangeUnit); |
||||
|
rangeUnitTranslationMap = rangeUnitTranslations; |
||||
|
|
||||
|
presenceMonitoringStrategies = PresenceMonitoringStrategiesData; |
||||
|
presenceMonitoringStrategyKeys = Array.from(this.presenceMonitoringStrategies.keys()); |
||||
|
|
||||
|
timeUnits = Object.keys(TimeUnit); |
||||
|
timeUnitsTranslationMap = timeUnitTranslations; |
||||
|
|
||||
|
public defaultPaddingEnable = true; |
||||
|
|
||||
|
constructor(private fb: UntypedFormBuilder) { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
protected configForm(): UntypedFormGroup { |
||||
|
return this.geoActionConfigForm; |
||||
|
} |
||||
|
|
||||
|
protected onConfigurationSet(configuration: RuleNodeConfiguration) { |
||||
|
this.geoActionConfigForm = this.fb.group({ |
||||
|
reportPresenceStatusOnEachMessage: [configuration ? configuration.reportPresenceStatusOnEachMessage : true, |
||||
|
[Validators.required]], |
||||
|
latitudeKeyName: [configuration ? configuration.latitudeKeyName : null, [Validators.required]], |
||||
|
longitudeKeyName: [configuration ? configuration.longitudeKeyName : null, [Validators.required]], |
||||
|
perimeterType: [configuration ? configuration.perimeterType : null, [Validators.required]], |
||||
|
fetchPerimeterInfoFromMessageMetadata: [configuration ? configuration.fetchPerimeterInfoFromMessageMetadata : false, []], |
||||
|
perimeterKeyName: [configuration ? configuration.perimeterKeyName : null, []], |
||||
|
centerLatitude: [configuration ? configuration.centerLatitude : null, []], |
||||
|
centerLongitude: [configuration ? configuration.centerLatitude : null, []], |
||||
|
range: [configuration ? configuration.range : null, []], |
||||
|
rangeUnit: [configuration ? configuration.rangeUnit : null, []], |
||||
|
polygonsDefinition: [configuration ? configuration.polygonsDefinition : null, []], |
||||
|
minInsideDuration: [configuration ? configuration.minInsideDuration : null, |
||||
|
[Validators.required, Validators.min(1), Validators.max(2147483647)]], |
||||
|
minInsideDurationTimeUnit: [configuration ? configuration.minInsideDurationTimeUnit : null, [Validators.required]], |
||||
|
minOutsideDuration: [configuration ? configuration.minOutsideDuration : null, |
||||
|
[Validators.required, Validators.min(1), Validators.max(2147483647)]], |
||||
|
minOutsideDurationTimeUnit: [configuration ? configuration.minOutsideDurationTimeUnit : null, [Validators.required]], |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected validatorTriggers(): string[] { |
||||
|
return ['fetchPerimeterInfoFromMessageMetadata', 'perimeterType']; |
||||
|
} |
||||
|
|
||||
|
protected updateValidators(emitEvent: boolean) { |
||||
|
const fetchPerimeterInfoFromMessageMetadata: boolean = this.geoActionConfigForm.get('fetchPerimeterInfoFromMessageMetadata').value; |
||||
|
const perimeterType: PerimeterType = this.geoActionConfigForm.get('perimeterType').value; |
||||
|
if (fetchPerimeterInfoFromMessageMetadata) { |
||||
|
this.geoActionConfigForm.get('perimeterKeyName').setValidators([Validators.required]); |
||||
|
} else { |
||||
|
this.geoActionConfigForm.get('perimeterKeyName').setValidators([]); |
||||
|
} |
||||
|
if (!fetchPerimeterInfoFromMessageMetadata && perimeterType === PerimeterType.CIRCLE) { |
||||
|
this.geoActionConfigForm.get('centerLatitude').setValidators([Validators.required, |
||||
|
Validators.min(-90), Validators.max(90)]); |
||||
|
this.geoActionConfigForm.get('centerLongitude').setValidators([Validators.required, |
||||
|
Validators.min(-180), Validators.max(180)]); |
||||
|
this.geoActionConfigForm.get('range').setValidators([Validators.required, Validators.min(0)]); |
||||
|
this.geoActionConfigForm.get('rangeUnit').setValidators([Validators.required]); |
||||
|
|
||||
|
this.defaultPaddingEnable = false; |
||||
|
} else { |
||||
|
this.geoActionConfigForm.get('centerLatitude').setValidators([]); |
||||
|
this.geoActionConfigForm.get('centerLongitude').setValidators([]); |
||||
|
this.geoActionConfigForm.get('range').setValidators([]); |
||||
|
this.geoActionConfigForm.get('rangeUnit').setValidators([]); |
||||
|
|
||||
|
this.defaultPaddingEnable = true; |
||||
|
} |
||||
|
if (!fetchPerimeterInfoFromMessageMetadata && perimeterType === PerimeterType.POLYGON) { |
||||
|
this.geoActionConfigForm.get('polygonsDefinition').setValidators([Validators.required]); |
||||
|
} else { |
||||
|
this.geoActionConfigForm.get('polygonsDefinition').setValidators([]); |
||||
|
} |
||||
|
this.geoActionConfigForm.get('perimeterKeyName').updateValueAndValidity({emitEvent}); |
||||
|
this.geoActionConfigForm.get('centerLatitude').updateValueAndValidity({emitEvent}); |
||||
|
this.geoActionConfigForm.get('centerLongitude').updateValueAndValidity({emitEvent}); |
||||
|
this.geoActionConfigForm.get('range').updateValueAndValidity({emitEvent}); |
||||
|
this.geoActionConfigForm.get('rangeUnit').updateValueAndValidity({emitEvent}); |
||||
|
this.geoActionConfigForm.get('polygonsDefinition').updateValueAndValidity({emitEvent}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<section [formGroup]="logConfigForm" class="flex flex-col"> |
||||
|
<tb-script-lang formControlName="scriptLang" *ngIf="tbelEnabled"></tb-script-lang> |
||||
|
<tb-js-func *ngIf="logConfigForm.get('scriptLang').value === scriptLanguage.JS" |
||||
|
#jsFuncComponent |
||||
|
formControlName="jsScript" |
||||
|
functionName="ToString" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
helpId="rulenode/log_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<tb-js-func *ngIf="logConfigForm.get('scriptLang').value === scriptLanguage.TBEL" |
||||
|
#tbelFuncComponent |
||||
|
formControlName="tbelScript" |
||||
|
functionName="ToString" |
||||
|
[functionArgs]="['msg', 'metadata', 'msgType']" |
||||
|
[disableUndefinedCheck]="true" |
||||
|
[scriptLanguage]="scriptLanguage.TBEL" |
||||
|
helpId="rulenode/tbel/log_node_script_fn" |
||||
|
noValidate="true"> |
||||
|
<button toolbarSuffixButton |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ testScriptLabel | translate }}" |
||||
|
matTooltipPosition="above" |
||||
|
class="tb-mat-32" |
||||
|
(click)="testScript()"> |
||||
|
<mat-icon class="material-icons" color="primary">bug_report</mat-icon> |
||||
|
</button> |
||||
|
</tb-js-func> |
||||
|
<div class="flex flex-row"> |
||||
|
<button mat-button mat-raised-button color="primary" (click)="testScript()"> |
||||
|
{{ testScriptLabel | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</section> |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue