Browse Source

UI implementation of Rule Node debug strategy

pull/11861/head
mpetrov 2 years ago
parent
commit
3c7dc0cc03
  1. 1
      ui-ngx/src/app/core/auth/auth.models.ts
  2. 3
      ui-ngx/src/app/core/auth/auth.reducer.ts
  3. 16
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html
  4. 1
      ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts
  5. 37
      ui-ngx/src/app/modules/home/pages/rulechain/debug-duration-left.pipe.ts
  6. 32
      ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-button.component.html
  7. 120
      ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-button.component.ts
  8. 50
      ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-panel.component.html
  9. 94
      ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-panel.component.ts
  10. 22
      ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html
  11. 7
      ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.scss
  12. 11
      ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts
  13. 14
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts
  14. 6
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts
  15. 4
      ui-ngx/src/app/shared/import-export/import-export.service.ts
  16. 13
      ui-ngx/src/app/shared/models/rule-node.models.ts
  17. 1
      ui-ngx/src/assets/locale/locale.constant-ar_AE.json
  18. 19
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  19. 1
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  20. 1
      ui-ngx/src/assets/locale/locale.constant-lt_LT.json
  21. 1
      ui-ngx/src/assets/locale/locale.constant-nl_BE.json
  22. 1
      ui-ngx/src/assets/locale/locale.constant-pl_PL.json
  23. 1
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  24. 17
      ui-ngx/src/styles.scss

1
ui-ngx/src/app/core/auth/auth.models.ts

@ -27,6 +27,7 @@ export interface SysParamsState {
mobileQrEnabled: boolean; mobileQrEnabled: boolean;
userSettings: UserSettings; userSettings: UserSettings;
maxResourceSize: number; maxResourceSize: number;
maxRuleNodeDebugDurationMinutes: number;
} }
export interface SysParams extends SysParamsState { export interface SysParams extends SysParamsState {

3
ui-ngx/src/app/core/auth/auth.reducer.ts

@ -31,7 +31,8 @@ const emptyUserAuthState: AuthPayload = {
persistDeviceStateToTelemetry: false, persistDeviceStateToTelemetry: false,
mobileQrEnabled: false, mobileQrEnabled: false,
maxResourceSize: 0, maxResourceSize: 0,
userSettings: initialUserSettings userSettings: initialUserSettings,
maxRuleNodeDebugDurationMinutes: 0,
}; };
export const initialState: AuthState = { export const initialState: AuthState = {

16
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html

@ -147,6 +147,22 @@
<mat-hint></mat-hint> <mat-hint></mat-hint>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="fields-element flex flex-1 flex-row xs:flex-col gt-xs:gap-4">
<mat-form-field class="mat-block flex-1" appearance="fill" subscriptSizing="dynamic">
<mat-label translate>tenant-profile.maximum-rule-node-debug-duration-min</mat-label>
<input matInput required min="0" step="1"
formControlName="maxRuleNodeDebugDurationMinutes"
type="number">
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleNodeDebugDurationMinutes').hasError('required')">
{{ 'tenant-profile.maximum-rule-node-debug-duration-min-range' | translate}}
</mat-error>
<mat-error *ngIf="defaultTenantProfileConfigurationFormGroup.get('maxRuleNodeDebugDurationMinutes').hasError('min')">
{{ 'tenant-profile.maximum-rule-node-debug-duration-min-required' | translate}}
</mat-error>
<mat-hint></mat-hint>
</mat-form-field>
<div class="flex-1"></div>
</div>
<mat-expansion-panel class="configuration-panel"> <mat-expansion-panel class="configuration-panel">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-description class="flex items-stretch justify-end" translate> <mat-panel-description class="flex items-stretch justify-end" translate>

1
ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts

@ -85,6 +85,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA
tenantNotificationRequestsRateLimit: [null, []], tenantNotificationRequestsRateLimit: [null, []],
tenantNotificationRequestsPerRuleRateLimit: [null, []], tenantNotificationRequestsPerRuleRateLimit: [null, []],
maxTransportMessages: [null, [Validators.required, Validators.min(0)]], maxTransportMessages: [null, [Validators.required, Validators.min(0)]],
maxRuleNodeDebugDurationMinutes: [null, [Validators.required, Validators.min(0)]],
maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]],
maxREExecutions: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]],
maxJSExecutions: [null, [Validators.required, Validators.min(0)]], maxJSExecutions: [null, [Validators.required, Validators.min(0)]],

37
ui-ngx/src/app/modules/home/pages/rulechain/debug-duration-left.pipe.ts

@ -0,0 +1,37 @@
///
/// 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 { Pipe, PipeTransform } from '@angular/core';
import { MINUTE } from '@shared/models/time/time.models';
@Pipe({
name: 'debugDurationLeft',
pure: false,
standalone: true,
})
export class DebugDurationLeftPipe implements PipeTransform {
transform(lastUpdateTs: number, maxRuleNodeDebugDurationMinutes: number): string {
const minutes = Math.max(0, Math.floor(this.getDebugTime(lastUpdateTs, maxRuleNodeDebugDurationMinutes) / MINUTE));
return `${minutes} min left`;
}
getDebugTime(lastUpdateTs: number, maxRuleNodeDebugDurationMinutes: number): number {
const maxDuration = maxRuleNodeDebugDurationMinutes * MINUTE;
const updateDuration = lastUpdateTs ? new Date().getTime() - lastUpdateTs : 0;
const leftTime = maxDuration - updateDuration;
return leftTime > 0 ? leftTime : 0;
}
}

32
ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-button.component.html

@ -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.
-->
<button mat-stroked-button
class="tb-rounded-btn flex-1 mr-2 w-[140px]"
color="primary"
#matButton
[class.active]="debugStrategyFormControl.value !== DebugStrategy.DISABLED && !disabled"
[disabled]="disabled"
(click)="openDebugStrategyPanel($event, matButton)">
<mat-icon [color]="disabled ? 'inherit' : 'primary'">bug_report</mat-icon>
<ng-container [ngSwitch]="debugStrategyFormControl.value">
<span *ngSwitchCase="DebugStrategy.DISABLED" translate>disabled</span>
<span *ngSwitchCase="DebugStrategy.ALL_WITH_FAILURES" translate>debug-strategy.all</span>
<span *ngSwitchCase="DebugStrategy.ALL_EVENTS">{{ lastUpdateTs | debugDurationLeft : maxRuleNodeDebugDurationMinutes }}</span>
<span *ngSwitchCase="DebugStrategy.ONLY_FAILURE_EVENTS" translate>debug-strategy.failures</span>
</ng-container>
</button>

120
ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-button.component.ts

@ -0,0 +1,120 @@
///
/// 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,
Input,
forwardRef,
Renderer2,
ViewContainerRef,
DestroyRef,
ChangeDetectionStrategy,
ChangeDetectorRef
} from '@angular/core';
import { ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, UntypedFormBuilder } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { DebugStrategy } from '@shared/models/rule-node.models';
import { DebugDurationLeftPipe } from '@home/pages/rulechain/debug-duration-left.pipe';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TbPopoverService } from '@shared/components/popover.service';
import { MatButton } from '@angular/material/button';
import { DebugStrategyPanelComponent } from '@home/pages/rulechain/debug-strategy-panel.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { interval } from 'rxjs';
import { MINUTE } from '@shared/models/time/time.models';
@Component({
selector: 'tb-debug-strategy-button',
templateUrl: './debug-strategy-button.component.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => DebugStrategyButtonComponent),
multi: true
}
],
standalone: true,
imports: [
CommonModule,
SharedModule,
DebugDurationLeftPipe,
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DebugStrategyButtonComponent implements ControlValueAccessor {
@Input() disabled = false;
@Input() lastUpdateTs: number;
debugStrategyFormControl: FormControl<DebugStrategy>;
private onChange: (debugStrategy: DebugStrategy) => void;
readonly maxRuleNodeDebugDurationMinutes = getCurrentAuthState(this.store).maxRuleNodeDebugDurationMinutes;
readonly DebugStrategy = DebugStrategy;
constructor(protected store: Store<AppState>,
private fb: UntypedFormBuilder,
private popoverService: TbPopoverService,
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef,
private destroyRef: DestroyRef,
private cdr: ChangeDetectorRef
) {
this.debugStrategyFormControl = this.fb.control(DebugStrategy.DISABLED);
this.debugStrategyFormControl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => this.onChange(value));
interval(0.5 * MINUTE)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.cdr.markForCheck());
}
openDebugStrategyPanel($event: Event, matButton: MatButton): void {
if ($event) {
$event.stopPropagation();
}
const trigger = matButton._elementRef.nativeElement;
const debugStrategy = this.debugStrategyFormControl.value;
if (this.popoverService.hasPopover(trigger)) {
this.popoverService.hidePopover(trigger);
} else {
const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer,
this.viewContainerRef, DebugStrategyPanelComponent, 'bottom', true, null,
{ debugStrategy },
{},
{}, {}, true);
debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover;
debugStrategyPopover.tbComponentRef.instance.onStrategyApplied.subscribe((strategy: DebugStrategy) => {
this.debugStrategyFormControl.patchValue(strategy);
this.cdr.markForCheck();
debugStrategyPopover.hide();
});
}
}
registerOnChange(onChange: (debugStrategy: DebugStrategy) => void): void {
this.onChange = onChange;
}
registerOnTouched(_: () => {}): void {}
writeValue(value: DebugStrategy): void {
this.debugStrategyFormControl.patchValue(value, { emitEvent: false });
this.cdr.markForCheck();
}
}

50
ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-panel.component.html

@ -0,0 +1,50 @@
<!--
Copyright © 2016-2024 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div [formGroup]="debugStrategyFormGroup">
<div class="tb-form-panel-title p-2" translate>debug-strategy.label</div>
<div class="p-1 hint-container">
<div class="tb-form-hint tb-primary-fill tb-flex center" tbTruncateWithTooltip>{{ 'debug-strategy.hint.main' | translate }}</div>
</div>
<div class="flex flex-col p-2">
<mat-slide-toggle class="mat-slide p-1" formControlName="onFailure">
<mat-label tb-hint-tooltip-icon="{{ 'debug-strategy.hint.on-failure' | translate }}">
{{ 'debug-strategy.on-failure' | translate }}
</mat-label>
</mat-slide-toggle>
<mat-slide-toggle class="mat-slide p-1" formControlName="allMessages">
<mat-label tb-hint-tooltip-icon="{{ 'debug-strategy.hint.all-messages' | translate }}">
{{ 'debug-strategy.all-messages' | translate }} {{ ' (' + maxRuleNodeDebugDurationMinutes + ' ' }} {{ ('debug-strategy.min' | translate) + ')'}}
</mat-label>
</mat-slide-toggle>
</div>
<div class="flex justify-end">
<button mat-button
color="primary"
type="button"
(click)="onCancel()">
{{ 'action.cancel' | translate }}
</button>
<button mat-raised-button
color="primary"
type="button"
(click)="onApply()">
{{ 'action.apply' | translate }}
</button>
</div>
</div>

94
ui-ngx/src/app/modules/home/pages/rulechain/debug-strategy-panel.component.ts

@ -0,0 +1,94 @@
///
/// 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, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { CommonModule } from '@angular/common';
import { SharedModule } from '@shared/shared.module';
import { getCurrentAuthState } from '@core/auth/auth.selectors';
import { DebugStrategy } from '@shared/models/rule-node.models';
@Component({
selector: 'tb-debug-strategy-panel',
templateUrl: './debug-strategy-panel.component.html',
standalone: true,
imports: [
SharedModule,
CommonModule
]
})
export class DebugStrategyPanelComponent extends PageComponent implements OnInit {
@Input() popover: TbPopoverComponent<DebugStrategyPanelComponent>;
@Input() debugStrategy: DebugStrategy;
debugStrategyFormGroup: UntypedFormGroup;
onStrategyApplied = new EventEmitter<DebugStrategy>()
readonly maxRuleNodeDebugDurationMinutes = getCurrentAuthState(this.store).maxRuleNodeDebugDurationMinutes;
constructor(private fb: UntypedFormBuilder,
protected store: Store<AppState>) {
super(store);
this.debugStrategyFormGroup = this.fb.group({
allMessages: [false],
onFailure: [false]
});
}
ngOnInit(): void {
this.updatePanelStrategy();
}
onCancel() {
this.popover?.hide();
}
onApply(): void {
const allMessages = this.debugStrategyFormGroup.get('allMessages').value;
const onFailure = this.debugStrategyFormGroup.get('onFailure').value;
if (allMessages && onFailure) {
this.onStrategyApplied.emit(DebugStrategy.ALL_WITH_FAILURES);
} else if (allMessages) {
this.onStrategyApplied.emit(DebugStrategy.ALL_EVENTS);
} else if (onFailure) {
this.onStrategyApplied.emit(DebugStrategy.ONLY_FAILURE_EVENTS);
} else {
this.onStrategyApplied.emit(DebugStrategy.DISABLED);
}
}
private updatePanelStrategy(): void {
switch (this.debugStrategy) {
case DebugStrategy.ALL_WITH_FAILURES:
this.debugStrategyFormGroup.get('allMessages').patchValue(true, { emitEvent: false });
this.debugStrategyFormGroup.get('onFailure').patchValue(true, { emitEvent: false });
break;
case DebugStrategy.ONLY_FAILURE_EVENTS:
this.debugStrategyFormGroup.get('onFailure').patchValue(true, { emitEvent: false });
break;
case DebugStrategy.ALL_EVENTS:
this.debugStrategyFormGroup.get('allMessages').patchValue(true, { emitEvent: false });
break;
}
}
}

22
ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html

@ -22,7 +22,7 @@
</button> </button>
</div> </div>
<form [formGroup]="ruleNodeFormGroup" class="mat-padding"> <form [formGroup]="ruleNodeFormGroup" class="mat-padding">
<section class="title-row"> <section class="title-row flex align-center">
<mat-form-field class="mat-block flex-1"> <mat-form-field class="mat-block flex-1">
<mat-label translate>rulenode.name</mat-label> <mat-label translate>rulenode.name</mat-label>
<input matInput formControlName="name" required> <input matInput formControlName="name" required>
@ -34,13 +34,19 @@
{{ 'rulenode.name-max-length' | translate }} {{ 'rulenode.name-max-length' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<section class="node-setting"> <section class="flex flex-row max-w-[280px] mb-[22px]">
<mat-slide-toggle formControlName="debugMode"> <tb-debug-strategy-button formControlName="debugStrategy" [disabled]="disabled" [lastUpdateTs]="ruleNode.lastUpdateTs"/>
{{ 'rulenode.debug-mode' | translate }} <button mat-stroked-button
</mat-slide-toggle> class="tb-rounded-btn flex-1"
<mat-slide-toggle *ngIf="isSingleton()" formControlName="singletonMode"> color="primary"
{{ 'rulenode.singleton-mode' | translate }} *ngIf="isSingleton()"
</mat-slide-toggle> [disabled]="disabled"
[class.active]="ruleNodeFormGroup.get('singletonMode').value"
(click)="onSingleModeChange($event)">
<mat-icon [class.opacity-0]="!ruleNodeFormGroup.get('singletonMode').value">checkmark</mat-icon>
<span translate>rulenode.singleton</span>
<input [class.hidden]="true" formControlName="singletonMode">
</button>
</section> </section>
</section> </section>
<tb-rule-node-config #ruleNodeConfigComponent <tb-rule-node-config #ruleNodeConfigComponent

7
ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.scss

@ -23,13 +23,6 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.node-setting {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 22px;
}
@media #{$mat-gt-sm} { @media #{$mat-gt-sm} {
flex-direction: row; flex-direction: row;
gap: 8px; gap: 8px;

11
ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.ts

@ -29,7 +29,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { FcRuleNode, RuleNodeType } from '@shared/models/rule-node.models'; import { DebugStrategy, FcRuleNode, RuleNodeType } from '@shared/models/rule-node.models';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { RuleNodeConfigComponent } from './rule-node-config.component'; import { RuleNodeConfigComponent } from './rule-node-config.component';
@ -92,7 +92,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
if (this.ruleNode) { if (this.ruleNode) {
this.ruleNodeFormGroup = this.fb.group({ this.ruleNodeFormGroup = this.fb.group({
name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*'), Validators.maxLength(255)]], name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*'), Validators.maxLength(255)]],
debugMode: [this.ruleNode.debugMode, []], debugStrategy: [this.ruleNode.debugStrategy ?? DebugStrategy.DISABLED],
singletonMode: [this.ruleNode.singletonMode, []], singletonMode: [this.ruleNode.singletonMode, []],
configuration: [this.ruleNode.configuration, [Validators.required]], configuration: [this.ruleNode.configuration, [Validators.required]],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
@ -167,6 +167,13 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O
this.ruleNodeConfigComponent.validate(); this.ruleNodeConfigComponent.validate();
} }
onSingleModeChange($event: Event): void {
if ($event) {
$event.stopPropagation();
}
this.ruleNodeFormGroup.get('singletonMode').patchValue(!this.ruleNodeFormGroup.get('singletonMode').value);
}
openRuleChain($event: Event) { openRuleChain($event: Event) {
if ($event) { if ($event) {
$event.stopPropagation(); $event.stopPropagation();

14
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

@ -62,6 +62,7 @@ import {
} from '@shared/models/rule-chain.models'; } from '@shared/models/rule-chain.models';
import { FcItemInfo, FlowchartConstants, NgxFlowchartComponent, UserCallbacks } from 'ngx-flowchart'; import { FcItemInfo, FlowchartConstants, NgxFlowchartComponent, UserCallbacks } from 'ngx-flowchart';
import { import {
DebugStrategy,
FcRuleEdge, FcRuleEdge,
FcRuleNode, FcRuleNode,
FcRuleNodeType, FcRuleNodeType,
@ -575,7 +576,8 @@ export class RuleChainPageComponent extends PageComponent
additionalInfo: ruleNode.additionalInfo, additionalInfo: ruleNode.additionalInfo,
configuration: ruleNode.configuration, configuration: ruleNode.configuration,
configurationVersion: isDefinedAndNotNull(ruleNode.configurationVersion) ? ruleNode.configurationVersion : 0, configurationVersion: isDefinedAndNotNull(ruleNode.configurationVersion) ? ruleNode.configurationVersion : 0,
debugMode: ruleNode.debugMode, debugStrategy: ruleNode.debugStrategy,
lastUpdateTs: ruleNode.lastUpdateTs,
singletonMode: ruleNode.singletonMode, singletonMode: ruleNode.singletonMode,
queueName: ruleNode.queueName, queueName: ruleNode.queueName,
x: Math.round(ruleNode.additionalInfo.layoutX), x: Math.round(ruleNode.additionalInfo.layoutX),
@ -936,7 +938,8 @@ export class RuleChainPageComponent extends PageComponent
name: node.name, name: node.name,
configuration: deepClone(node.configuration), configuration: deepClone(node.configuration),
additionalInfo: node.additionalInfo ? deepClone(node.additionalInfo) : {}, additionalInfo: node.additionalInfo ? deepClone(node.additionalInfo) : {},
debugMode: node.debugMode, debugStrategy: node.debugStrategy,
lastUpdateTs: node.lastUpdateTs,
singletonMode: node.singletonMode, singletonMode: node.singletonMode,
queueName: node.queueName queueName: node.queueName
}; };
@ -1009,7 +1012,7 @@ export class RuleChainPageComponent extends PageComponent
name: outputEdge.label, name: outputEdge.label,
configuration: {}, configuration: {},
additionalInfo: {}, additionalInfo: {},
debugMode: false, debugStrategy: DebugStrategy.DISABLED,
singletonMode: false singletonMode: false
}; };
outputNode.additionalInfo.layoutX = Math.round(destNode.x); outputNode.additionalInfo.layoutX = Math.round(destNode.x);
@ -1055,7 +1058,7 @@ export class RuleChainPageComponent extends PageComponent
configuration: { configuration: {
ruleChainId: ruleChain.id.id ruleChainId: ruleChain.id.id
}, },
debugMode: false, debugStrategy: DebugStrategy.DISABLED,
singletonMode: false, singletonMode: false,
x: Math.round(ruleChainNodeX), x: Math.round(ruleChainNodeX),
y: Math.round(ruleChainNodeY), y: Math.round(ruleChainNodeY),
@ -1475,7 +1478,8 @@ export class RuleChainPageComponent extends PageComponent
: node.component.configurationVersion, : node.component.configurationVersion,
configuration: node.configuration, configuration: node.configuration,
additionalInfo: node.additionalInfo ? node.additionalInfo : {}, additionalInfo: node.additionalInfo ? node.additionalInfo : {},
debugMode: node.debugMode, debugStrategy: node.debugStrategy,
lastUpdateTs: node.lastUpdateTs,
singletonMode: node.singletonMode, singletonMode: node.singletonMode,
queueName: node.queueName queueName: node.queueName
}; };

6
ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts

@ -32,6 +32,8 @@ import { RuleNodeDetailsComponent } from './rule-node-details.component';
import { RuleNodeLinkComponent } from './rule-node-link.component'; import { RuleNodeLinkComponent } from './rule-node-link.component';
import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component';
import { RuleNodeConfigComponent } from './rule-node-config.component'; import { RuleNodeConfigComponent } from './rule-node-config.component';
import { DebugDurationLeftPipe } from '@home/pages/rulechain/debug-duration-left.pipe';
import { DebugStrategyButtonComponent } from '@home/pages/rulechain/debug-strategy-button.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -59,7 +61,9 @@ import { RuleNodeConfigComponent } from './rule-node-config.component';
CommonModule, CommonModule,
SharedModule, SharedModule,
HomeComponentsModule, HomeComponentsModule,
RuleChainRoutingModule RuleChainRoutingModule,
DebugDurationLeftPipe,
DebugStrategyButtonComponent
] ]
}) })
export class RuleChainModule { } export class RuleChainModule { }

4
ui-ngx/src/app/shared/import-export/import-export.service.ts

@ -67,7 +67,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service';
import { DeviceService } from '@core/http/device.service'; import { DeviceService } from '@core/http/device.service';
import { AssetService } from '@core/http/asset.service'; import { AssetService } from '@core/http/asset.service';
import { EdgeService } from '@core/http/edge.service'; import { EdgeService } from '@core/http/edge.service';
import { RuleNode } from '@shared/models/rule-node.models'; import { DebugStrategy, RuleNode } from '@shared/models/rule-node.models';
import { AssetProfileService } from '@core/http/asset-profile.service'; import { AssetProfileService } from '@core/http/asset-profile.service';
import { AssetProfile } from '@shared/models/asset.models'; import { AssetProfile } from '@shared/models/asset.models';
import { import {
@ -637,7 +637,7 @@ export class ImportExportService {
if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) { if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) {
const ruleChainNode: RuleNode = { const ruleChainNode: RuleNode = {
name: '', name: '',
debugMode: false, debugStrategy: DebugStrategy.DISABLED,
singletonMode: false, singletonMode: false,
type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode', type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode',
configuration: { configuration: {

13
ui-ngx/src/app/shared/models/rule-node.models.ts

@ -37,7 +37,8 @@ export interface RuleNode extends BaseData<RuleNodeId> {
ruleChainId?: RuleChainId; ruleChainId?: RuleChainId;
type: string; type: string;
name: string; name: string;
debugMode: boolean; debugStrategy: DebugStrategy;
lastUpdateTs?: number;
singletonMode: boolean; singletonMode: boolean;
queueName?: string; queueName?: string;
configurationVersion?: number; configurationVersion?: number;
@ -345,13 +346,21 @@ export interface FcRuleNode extends FcRuleNodeType {
ruleNodeId?: RuleNodeId; ruleNodeId?: RuleNodeId;
additionalInfo?: any; additionalInfo?: any;
configuration?: RuleNodeConfiguration; configuration?: RuleNodeConfiguration;
debugMode?: boolean; debugStrategy?: DebugStrategy;
lastUpdateTs?: number;
error?: string; error?: string;
highlighted?: boolean; highlighted?: boolean;
componentClazz?: string; componentClazz?: string;
ruleChainType?: RuleChainType; ruleChainType?: RuleChainType;
} }
export enum DebugStrategy {
DISABLED = 'DISABLED',
ALL_EVENTS = 'ALL_EVENTS',
ONLY_FAILURE_EVENTS = 'ONLY_FAILURE_EVENTS',
ALL_WITH_FAILURES = 'ALL_WITH_FAILURES',
}
export interface FcRuleEdge extends FcEdge { export interface FcRuleEdge extends FcEdge {
labels?: string[]; labels?: string[];
} }

1
ui-ngx/src/assets/locale/locale.constant-ar_AE.json

@ -4556,7 +4556,6 @@
"deselect-all": "إلغاء تحديد الكل", "deselect-all": "إلغاء تحديد الكل",
"rulenode-details": "تفاصيل عقدة القاعدة", "rulenode-details": "تفاصيل عقدة القاعدة",
"debug-mode": "وضع التصحيح", "debug-mode": "وضع التصحيح",
"singleton-mode": "وضع المفرد",
"configuration": "التكوين", "configuration": "التكوين",
"link": "رابط", "link": "رابط",
"link-details": "تفاصيل رابط عقدة القاعدة", "link-details": "تفاصيل رابط عقدة القاعدة",

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

@ -995,6 +995,19 @@
"type-timeseries-deleted": "Telemetry deleted", "type-timeseries-deleted": "Telemetry deleted",
"type-sms-sent": "SMS sent" "type-sms-sent": "SMS sent"
}, },
"debug-strategy": {
"min": "min",
"label": "Debug strategy",
"on-failure": "Failures only (24/7)",
"all-messages": "All messages",
"failures": "Failures",
"all": "All",
"hint": {
"main": "All node debug messages rate limited with:",
"on-failure": "Save all failure debug events without time limit.",
"all-messages": "Save all debug events during time limit."
}
},
"confirm-on-exit": { "confirm-on-exit": {
"message": "You have unsaved changes. Are you sure you want to leave this page?", "message": "You have unsaved changes. Are you sure you want to leave this page?",
"html-message": "You have unsaved changes.<br/>Are you sure you want to leave this page?", "html-message": "You have unsaved changes.<br/>Are you sure you want to leave this page?",
@ -2071,6 +2084,7 @@
"column": "Column", "column": "Column",
"row": "Row" "row": "Row"
}, },
"disabled": "Disabled",
"edge": { "edge": {
"edge": "Edge", "edge": "Edge",
"edge-instances": "Edge instances", "edge-instances": "Edge instances",
@ -4014,7 +4028,7 @@
"deselect-all": "Deselect all", "deselect-all": "Deselect all",
"rulenode-details": "Rule node details", "rulenode-details": "Rule node details",
"debug-mode": "Debug mode", "debug-mode": "Debug mode",
"singleton-mode": "Singleton mode", "singleton": "Singleton",
"configuration": "Configuration", "configuration": "Configuration",
"link": "Link", "link": "Link",
"link-details": "Rule node link details", "link-details": "Rule node link details",
@ -4292,6 +4306,9 @@
"maximum-ota-packages-sum-data-size": "Maximum total size of OTA package files (bytes)", "maximum-ota-packages-sum-data-size": "Maximum total size of OTA package files (bytes)",
"maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.", "maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.",
"maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative", "maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative",
"maximum-rule-node-debug-duration-min": "Maximum rule node debug duration (min)",
"maximum-rule-node-debug-duration-min-required": "Maximum rule node debug duration is required.",
"maximum-rule-node-debug-duration-min-range": "Maximum rule node debug duration can't be negative",
"rest-requests-for-tenant": "REST requests for tenant", "rest-requests-for-tenant": "REST requests for tenant",
"transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages", "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages",
"transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points", "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points",

1
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -3485,7 +3485,6 @@
"deselect-all": "Deshacer selección de todos", "deselect-all": "Deshacer selección de todos",
"rulenode-details": "Detalles del nodo de reglas", "rulenode-details": "Detalles del nodo de reglas",
"debug-mode": "Modo Debug", "debug-mode": "Modo Debug",
"singleton-mode": "Módo único",
"configuration": "Configuración", "configuration": "Configuración",
"link": "Enlace", "link": "Enlace",
"link-details": "Detalles del enlace del nodo de reglas", "link-details": "Detalles del enlace del nodo de reglas",

1
ui-ngx/src/assets/locale/locale.constant-lt_LT.json

@ -4458,7 +4458,6 @@
"deselect-all": "Deselect all", "deselect-all": "Deselect all",
"rulenode-details": "Rule node details", "rulenode-details": "Rule node details",
"debug-mode": "Debug mode", "debug-mode": "Debug mode",
"singleton-mode": "Singleton mode",
"configuration": "Configuration", "configuration": "Configuration",
"link": "Link", "link": "Link",
"link-details": "Rule node link details", "link-details": "Rule node link details",

1
ui-ngx/src/assets/locale/locale.constant-nl_BE.json

@ -4260,7 +4260,6 @@
"deselect-all": "Alles deselecteren", "deselect-all": "Alles deselecteren",
"rulenode-details": "Details van rule nodes", "rulenode-details": "Details van rule nodes",
"debug-mode": "Foutopsporingsmodus", "debug-mode": "Foutopsporingsmodus",
"singleton-mode": "Singleton-modus",
"configuration": "Configuratie", "configuration": "Configuratie",
"link": "Verbinden", "link": "Verbinden",
"link-details": "Details van rule nodes links", "link-details": "Details van rule nodes links",

1
ui-ngx/src/assets/locale/locale.constant-pl_PL.json

@ -4474,7 +4474,6 @@
"deselect-all": "Odznacz wszystkie", "deselect-all": "Odznacz wszystkie",
"rulenode-details": "Szczegóły węzła reguły", "rulenode-details": "Szczegóły węzła reguły",
"debug-mode": "Tryb debugowania", "debug-mode": "Tryb debugowania",
"singleton-mode": "Tryb singletonowy",
"configuration": "Konfiguracja", "configuration": "Konfiguracja",
"link": "Połączenie", "link": "Połączenie",
"link-details": "Szczegóły połączenia węzła reguły", "link-details": "Szczegóły połączenia węzła reguły",

1
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -3968,7 +3968,6 @@
"deselect-all": "取消选择", "deselect-all": "取消选择",
"rulenode-details": "规则节点详情", "rulenode-details": "规则节点详情",
"debug-mode": "调试模式", "debug-mode": "调试模式",
"singleton-mode": "单例模式",
"configuration": "配置", "configuration": "配置",
"link": "链接", "link": "链接",
"link-details": "规则节点链接详情", "link-details": "规则节点链接详情",

17
ui-ngx/src/styles.scss

@ -1267,6 +1267,23 @@ pre.tb-highlight {
.no-wrap { .no-wrap {
white-space: nowrap; white-space: nowrap;
} }
.tb-rounded-btn {
border-radius: 20px;
padding: 0 16px;
&.active:not(:disabled) {
border-color: $primary !important;
}
&:disabled, &:not(.active) {
background: rgba(0, 0, 0, 0.06);
}
&:not(.active) {
color: rgba(0, 0, 0, 0.76);
}
}
} }
/*************** /***************

Loading…
Cancel
Save