35 changed files with 2253 additions and 375 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,30 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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 class="action-buttons-container" [style]="{'align-content': settings.alignment}"> |
|||
<button mat-button [color]="settings.buttonsClass" |
|||
*ngFor="let action of ctx.actionsApi.getActionDescriptors('actionButtonClick')" |
|||
[ngClass]="{ |
|||
'mat-mdc-button mat-mdc-raised-button': settings.buttonsType === 'raised', |
|||
'mdc-button--outlined mat-mdc-outlined-button': settings.buttonsType === 'stroked', |
|||
'mat-mdc-unelevated-button': settings.buttonsType === 'flat', |
|||
'mat-mdc-menu-item mdc-list-item': settings.buttonsType === 'menu' |
|||
}" |
|||
(click)="actionButtonClick($event, action)" |
|||
>{{action.displayName}} |
|||
</button> |
|||
</div> |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2023 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 { |
|||
width: 100%; |
|||
height: 100%; |
|||
|
|||
.action-buttons-container { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
flex-direction: row; |
|||
height: 100%; |
|||
width: 100%; |
|||
|
|||
button { |
|||
flex-grow: 1; |
|||
margin: 10px; |
|||
min-width: 150px; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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, OnInit } from '@angular/core'; |
|||
import { WidgetContext } from '@home/models/widget-component.models'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { WidgetActionDescriptor } from '@shared/models/widget.models'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { ThemePalette } from '@angular/material/core'; |
|||
|
|||
|
|||
interface ActionButtonsWidgetSettings { |
|||
buttonsType: string, |
|||
buttonsClass: ThemePalette, |
|||
alignment: string |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'tb-action-buttons-widget', |
|||
templateUrl: './action-buttons.component.html', |
|||
styleUrls: ['./action-buttons.component.scss'] |
|||
}) |
|||
export class ActionButtonsComponent implements OnInit { |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
settings: ActionButtonsWidgetSettings; |
|||
|
|||
constructor() { |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.settings = this.ctx.settings; |
|||
} |
|||
|
|||
actionButtonClick($event: MouseEvent, actionDescriptor: WidgetActionDescriptor) { |
|||
let entityId, entityName, entityLabel; |
|||
if (this.ctx.datasources) { |
|||
entityId = this.ctx.datasources[0].entity.id; |
|||
entityName = this.ctx.datasources[0].entityName; |
|||
entityLabel = this.ctx.datasources[0].entityLabel; |
|||
} |
|||
this.ctx.actionsApi.handleWidgetAction($event, actionDescriptor, entityId, entityName, null, entityLabel); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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 class="tb-widget-settings" [formGroup]="ActionButtonsSettingsForm" fxLayout="column"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.action-buttons.button-type</mat-label> |
|||
<mat-select required formControlName="buttonsType"> |
|||
<mat-option *ngFor="let type of buttonTypes" [value]="type"> |
|||
{{type}} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.action-buttons.button-class</mat-label> |
|||
<mat-select required formControlName="buttonsClass"> |
|||
<mat-option *ngFor="let class of buttonClasses" [value]="class"> |
|||
{{class}} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>widgets.action-buttons.alignment</mat-label> |
|||
<mat-select required formControlName="alignment"> |
|||
<mat-option *ngFor="let type of alignment" [value]="type"> |
|||
{{type}} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
</section> |
|||
@ -0,0 +1,62 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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 { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-action-buttons-widget-settings', |
|||
templateUrl: './action-buttons-widget-settings.component.html', |
|||
styleUrls: ['./../widget-settings.scss'] |
|||
}) |
|||
export class ActionButtonsWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
ActionButtonsSettingsForm: UntypedFormGroup; |
|||
|
|||
buttonTypes = ['basic', 'raised', 'stroked', 'flat']; |
|||
|
|||
buttonClasses = ['basic', 'primary', 'accent', 'warn']; |
|||
|
|||
alignment = ['center', 'start', 'end', 'normal', 'baseline', 'space-between', 'space-around', 'stretch']; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: UntypedFormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): UntypedFormGroup { |
|||
return this.ActionButtonsSettingsForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
buttonsType: 'basic', |
|||
buttonsClass: 'basic', |
|||
alignment: 'center' |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.ActionButtonsSettingsForm = this.fb.group({ |
|||
buttonsType: [settings.buttonsType, []], |
|||
buttonsClass: [settings.buttonsClass, []], |
|||
alignment: [settings.alignment, []] |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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 class="tb-widget-settings" [formGroup]="GatewayLogSettingForm" fxLayout="column"> |
|||
<mat-slide-toggle class="mat-slide" formControlName="isConnectorLog"> |
|||
Is Connector |
|||
</mat-slide-toggle> |
|||
<mat-form-field fxFlex class="mat-block" *ngIf="GatewayLogSettingForm.get('isConnectorLog').value"> |
|||
<mat-label translate>State parameter connector name</mat-label> |
|||
<input matInput formControlName="connectorLogState"> |
|||
</mat-form-field> |
|||
</section> |
|||
@ -0,0 +1,54 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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 { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-gateway-logs-settings', |
|||
templateUrl: './gateway-logs-settings.component.html', |
|||
styleUrls: ['../widget-settings.scss'] |
|||
}) |
|||
export class GatewayLogsSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
GatewayLogSettingForm: UntypedFormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: UntypedFormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): UntypedFormGroup { |
|||
return this.GatewayLogSettingForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
isConnectorLog: false, |
|||
connectorLogState: 'default' |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.GatewayLogSettingForm = this.fb.group({ |
|||
isConnectorLog: [false, []], |
|||
connectorLogState: ['default', []] |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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 class="tb-widget-settings" [formGroup]="GatewayLogSettingForm" fxLayout="column"> |
|||
<mat-slide-toggle class="mat-slide" formControlName="isConnector"> |
|||
Is Connector |
|||
</mat-slide-toggle> |
|||
</section> |
|||
@ -0,0 +1,52 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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 { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; |
|||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-gateway-service-rpc-settings', |
|||
templateUrl: './gateway-service-rpc-settings.component.html', |
|||
styleUrls: ['../widget-settings.scss'] |
|||
}) |
|||
export class GatewayServiceRPCSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
GatewayLogSettingForm: UntypedFormGroup; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private fb: UntypedFormBuilder) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): UntypedFormGroup { |
|||
return this.GatewayLogSettingForm; |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return { |
|||
isConnector: false, |
|||
}; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.GatewayLogSettingForm = this.fb.group({ |
|||
isConnector: [false, []] |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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. |
|||
|
|||
--> |
|||
<nav mat-tab-nav-bar [tabPanel]="tabPanel"> |
|||
<a mat-tab-link *ngFor="let link of logLinks" |
|||
(click)="onTabChanged(link)" |
|||
[active]="activeLink.name == link.name"> {{link.name}} </a> |
|||
</nav> |
|||
<mat-tab-nav-panel #tabPanel></mat-tab-nav-panel> |
|||
<table mat-table [dataSource]="dataSource" |
|||
matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" |
|||
matSortDisableClear> |
|||
<ng-container matColumnDef="ts"> |
|||
<mat-header-cell *matHeaderCellDef mat-sort-header> Created time</mat-header-cell> |
|||
<mat-cell *matCellDef="let attribute"> |
|||
{{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss'}} |
|||
</mat-cell> |
|||
</ng-container> |
|||
<ng-container matColumnDef="status"> |
|||
<mat-header-cell *matHeaderCellDef mat-sort-header> Status</mat-header-cell> |
|||
<mat-cell *matCellDef="let attribute"> |
|||
<span [ngClass]="statusClass(attribute.status )">{{ attribute.status }}</span> |
|||
</mat-cell> |
|||
</ng-container> |
|||
<ng-container matColumnDef="message"> |
|||
<mat-header-cell *matHeaderCellDef mat-sort-header style="width: 70%">Message</mat-header-cell> |
|||
<mat-cell *matCellDef="let attribute" [ngClass]="statusClassMsg(attribute.status )"> |
|||
{{ attribute.message }} |
|||
</mat-cell> |
|||
</ng-container> |
|||
<mat-header-row [ngClass]="{'mat-row-select': true}" |
|||
*matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> |
|||
<mat-row [ngClass]="{'mat-row-select': true}" |
|||
*matRowDef="let attribute; columns: displayedColumns;"></mat-row> |
|||
<!-- <ng-container matColumnDef="lastUpdateTs">--> |
|||
<!-- <mat-header-cell *matHeaderCellDef mat-sort-header style="min-width: 150px; max-width: 150px; width: 150px;"> {{ 'attribute.last-update-time' | translate }} </mat-header-cell>--> |
|||
<!-- <mat-cell *matCellDef="let attribute">--> |
|||
<!-- {{ attribute.lastUpdateTs | date:'yyyy-MM-dd HH:mm:ss' }}--> |
|||
<!-- </mat-cell>--> |
|||
<!-- </ng-container>--> |
|||
</table> |
|||
<mat-divider></mat-divider> |
|||
|
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* Copyright © 2016-2023 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 { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: block; |
|||
overflow-x: auto; |
|||
padding: 0; |
|||
|
|||
|
|||
.status { |
|||
border-radius: 20px; |
|||
font-weight: 500; |
|||
padding: 5px 15px; |
|||
} |
|||
|
|||
.status-debug { |
|||
color: green; |
|||
background: rgba(0, 128, 0, 0.1); |
|||
} |
|||
|
|||
.status-warning { |
|||
color: orange; |
|||
background: rgba(255, 165, 0, 0.1); |
|||
} |
|||
|
|||
.status-error { |
|||
color: red; |
|||
background: rgba(255, 0, 0, 0.1); |
|||
} |
|||
|
|||
.status-info { |
|||
color: black; |
|||
background: rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
.msg-status-exception { |
|||
color: red; |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,250 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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 { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, ViewChild } from '@angular/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Router } from '@angular/router'; |
|||
import { FormBuilder, FormGroup } from '@angular/forms'; |
|||
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; |
|||
import { AttributeService } from '@core/http/attribute.service'; |
|||
import { DeviceService } from '@core/http/device.service'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { AttributeData, DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { PageComponent } from "@shared/components/page.component"; |
|||
import { PageLink } from "@shared/models/page/page-link"; |
|||
import { AttributeDatasource } from "@home/models/datasource/attribute-datasource"; |
|||
import { Direction, SortOrder } from "@shared/models/page/sort-order"; |
|||
import { MatSort } from "@angular/material/sort"; |
|||
import { MatTableDataSource } from "@angular/material/table"; |
|||
import { GatewayLogLevel } from "@shared/components/device/gateway-configuration.component"; |
|||
import { DialogService } from '@app/core/services/dialog.service'; |
|||
import { WidgetContext } from "@home/models/widget-component.models"; |
|||
|
|||
|
|||
export interface gatewayConnector { |
|||
name: string; |
|||
type: string; |
|||
configuration?: string; |
|||
configurationJson: string; |
|||
log_level: string; |
|||
key?: string; |
|||
} |
|||
|
|||
export interface LogLink { |
|||
name: string; |
|||
key: string; |
|||
filterFn?: Function; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'tb-gateway-logs', |
|||
templateUrl: './gateway-logs.component.html', |
|||
styleUrls: ['./gateway-logs.component.scss'] |
|||
}) |
|||
export class GatewayLogsComponent extends PageComponent implements AfterViewInit { |
|||
|
|||
pageLink: PageLink; |
|||
|
|||
attributeDataSource: AttributeDatasource; |
|||
|
|||
dataSource: MatTableDataSource<any> |
|||
|
|||
displayedColumns = ['ts', 'status', 'message']; |
|||
|
|||
gatewayConnectorDefaultTypes: Array<string> = |
|||
['mqtt', |
|||
'modbus', |
|||
'grpc', |
|||
'opcua', |
|||
'opcua_asyncio', |
|||
'ble', |
|||
'request', |
|||
'can', |
|||
'bacnet', |
|||
'odbc', |
|||
'rest', |
|||
'snmp', |
|||
'ftp', |
|||
'socket', |
|||
'xmpp', |
|||
'ocpp', |
|||
'custom' |
|||
]; |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
@Input() |
|||
dialogRef: MatDialogRef<any>; |
|||
|
|||
@ViewChild('searchInput') searchInputField: ElementRef; |
|||
@ViewChild(MatSort) sort: MatSort; |
|||
|
|||
connectorForm: FormGroup; |
|||
|
|||
viewsInited = false; |
|||
|
|||
textSearchMode: boolean; |
|||
|
|||
activeConnectors: Array<string>; |
|||
|
|||
inactiveConnectors: Array<string>; |
|||
|
|||
InitialActiveConnectors: Array<string>; |
|||
|
|||
gatewayLogLevel = Object.values(GatewayLogLevel); |
|||
|
|||
logLinks: Array<LogLink>; |
|||
|
|||
initialConnector: gatewayConnector; |
|||
|
|||
activeLink: LogLink; |
|||
|
|||
gatewayLogLinks: Array<LogLink> = [ |
|||
{ |
|||
name: "General", |
|||
key: "LOGS" |
|||
}, { |
|||
name: "Service", |
|||
key: "SERVICE_LOGS" |
|||
}, |
|||
{ |
|||
name: "Connection", |
|||
key: "CONNECTION_LOGS" |
|||
}, { |
|||
name: "Storage", |
|||
key: "STORAGE_LOGS" |
|||
}] |
|||
|
|||
|
|||
constructor(protected router: Router, |
|||
protected store: Store<AppState>, |
|||
protected fb: FormBuilder, |
|||
protected translate: TranslateService, |
|||
protected attributeService: AttributeService, |
|||
protected deviceService: DeviceService, |
|||
protected dialogService: DialogService, |
|||
private cd: ChangeDetectorRef, |
|||
public dialog: MatDialog) { |
|||
super(store); |
|||
const sortOrder: SortOrder = {property: 'key', direction: Direction.ASC}; |
|||
this.pageLink = new PageLink(1000, 0, null, sortOrder); |
|||
this.dataSource = new MatTableDataSource<AttributeData>([]); |
|||
|
|||
} |
|||
|
|||
|
|||
ngAfterViewInit() { |
|||
this.dataSource.sort = this.sort; |
|||
this.ctx.defaultSubscription.onTimewindowChangeFunction = timewindow => { |
|||
this.ctx.defaultSubscription.options.timeWindowConfig = timewindow; |
|||
this.ctx.defaultSubscription.updateDataSubscriptions(); |
|||
return timewindow; |
|||
} |
|||
if (this.ctx.settings.isConnectorLog && this.ctx.settings.connectorLogState) { |
|||
console.log(this.ctx.settings.connectorLogState) |
|||
|
|||
const connector = this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState]; |
|||
console.log(connector) |
|||
this.logLinks = [{ |
|||
key: `${connector.key}_LOGS`, |
|||
name: "Connector", |
|||
filterFn: (attrData)=>{ |
|||
return !attrData.message.includes(`${connector.key}_converter`) |
|||
} |
|||
},{ |
|||
key: `${connector.key}_LOGS`, |
|||
name: "Convector", |
|||
filterFn: (attrData)=>{ |
|||
return attrData.message.includes(`${connector.key}_converter`) |
|||
} |
|||
}, |
|||
{ |
|||
key: `${connector.key}_EXTENSION_LOGS`, |
|||
name: "Extension" |
|||
}] |
|||
} else { |
|||
this.logLinks = this.gatewayLogLinks; |
|||
} |
|||
this.activeLink = this.logLinks[0]; |
|||
this.changeSubscription(); |
|||
} |
|||
|
|||
|
|||
updateData(sort?) { |
|||
if (this.ctx.defaultSubscription.data.length) { |
|||
let attrData = this.ctx.defaultSubscription.data[0].data.map(data => { |
|||
return { |
|||
ts: data[0], |
|||
key: this.activeLink.key, |
|||
status: data[1].match(/\|(\w+)\|/)[1], |
|||
message: /\[(.*)/.exec(data[1])[0] |
|||
}; |
|||
}); |
|||
if (this.activeLink.filterFn) { |
|||
attrData = attrData.filter(data => this.activeLink.filterFn(data)); |
|||
} |
|||
this.dataSource.data = attrData; |
|||
if (sort) { |
|||
this.dataSource.sortData(this.dataSource.data, this.sort); |
|||
} |
|||
} |
|||
} |
|||
|
|||
onTabChanged(link) { |
|||
this.activeLink = link; |
|||
this.changeSubscription(); |
|||
} |
|||
|
|||
statusClass(status) { |
|||
switch (status) { |
|||
case GatewayLogLevel.debug: |
|||
return "status status-debug"; |
|||
case GatewayLogLevel.warning: |
|||
return "status status-warning"; |
|||
case GatewayLogLevel.error: |
|||
case "EXCEPTION": |
|||
return "status status-error"; |
|||
case GatewayLogLevel.info: |
|||
default: |
|||
return "status status-info" |
|||
} |
|||
} |
|||
|
|||
statusClassMsg(status) { |
|||
if (status === "EXCEPTION") { |
|||
return 'msg-status-exception'; |
|||
} |
|||
} |
|||
|
|||
changeSubscription() { |
|||
if (this.ctx.datasources[0].entity) { |
|||
this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ |
|||
name: this.activeLink.key, |
|||
type: DataKeyType.timeseries, |
|||
settings: {} |
|||
}]; |
|||
this.ctx.defaultSubscription.unsubscribe(); |
|||
this.ctx.defaultSubscription.updateDataSubscriptions(); |
|||
this.ctx.defaultSubscription.callbacks.onDataUpdated = () => { |
|||
this.updateData(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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. |
|||
|
|||
--> |
|||
<mat-card fxLayout="row" class="command-form" fxLayoutGap="10px" [formGroup]="commandForm"> |
|||
<mat-form-field class="mat-block tb-value-type"> |
|||
<mat-label>Command</mat-label> |
|||
<mat-select formControlName="command" *ngIf="!isConnector"> |
|||
<mat-option *ngFor="let command of RPCCommands" [value]="command"> |
|||
{{command}} |
|||
</mat-option> |
|||
</mat-select> |
|||
<input matInput formControlName="command" *ngIf="isConnector"/> |
|||
</mat-form-field> |
|||
<mat-form-field class="mat-block tb-value-type" fxFlex *ngIf="!isConnector"> |
|||
<mat-label>Time</mat-label> |
|||
<input matInput formControlName="time" type="number"/> |
|||
</mat-form-field> |
|||
<mat-form-field class="mat-block tb-value-type" fxFlex *ngIf="isConnector"> |
|||
<mat-label>Parameters</mat-label> |
|||
<input matInput formControlName="params" type="JSON"/> |
|||
<mat-icon class="material-icons-outlined" aria-hidden="false" aria-label="help-icon" |
|||
matSuffix style="cursor:pointer;" |
|||
(click)="openEditJSONDialog($event)" |
|||
matTooltip="Edit params">edit |
|||
</mat-icon> |
|||
</mat-form-field> |
|||
<button mat-raised-button color="primary" (click)="sendCommand()" |
|||
[disabled]="commandForm.invalid">Send</button> |
|||
</mat-card> |
|||
<mat-card class="result-block" [formGroup]="commandForm" > |
|||
<span>Result</span> |
|||
<mat-divider></mat-divider> |
|||
<tb-json-content [contentType]="contentTypes.JSON" readonly="true" formControlName="result"></tb-json-content> |
|||
</mat-card> |
|||
|
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2023 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 { |
|||
width: 100%; |
|||
height: 100%; |
|||
overflow-x: auto; |
|||
display: flex; |
|||
flex-direction: column; |
|||
padding: 0; |
|||
|
|||
.command-form { |
|||
width: 100%; |
|||
flex-wrap: nowrap; |
|||
padding: 0 15px; |
|||
margin-bottom: 5px; |
|||
|
|||
&>button { |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
::ng-deep.mat-mdc-form-field-icon-suffix { |
|||
z-index: 100; |
|||
} |
|||
} |
|||
|
|||
.result-block { |
|||
padding: 0 15px; |
|||
|
|||
&>span { |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
@ -0,0 +1,125 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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 { AfterViewInit, ChangeDetectorRef, Component, Input } from '@angular/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Router } from '@angular/router'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { MatDialog, MatDialogRef } from '@angular/material/dialog'; |
|||
import { AttributeService } from '@core/http/attribute.service'; |
|||
import { DeviceService } from '@core/http/device.service'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { PageComponent } from "@shared/components/page.component"; |
|||
import { DialogService } from '@app/core/services/dialog.service'; |
|||
import { WidgetContext } from '@home/models/widget-component.models'; |
|||
import { ContentType } from '@shared/models/constants'; |
|||
import { |
|||
JsonObjectEditDialogComponent, |
|||
JsonObjectEditDialogData |
|||
} from "@shared/components/dialog/json-object-edit-dialog.component"; |
|||
|
|||
|
|||
@Component({ |
|||
selector: 'tb-gateway-service-rpc', |
|||
templateUrl: './gateway-service-rpc.component.html', |
|||
styleUrls: ['./gateway-service-rpc.component.scss'] |
|||
}) |
|||
export class GatewayServiceRPCComponent extends PageComponent implements AfterViewInit { |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
contentTypes = ContentType; |
|||
|
|||
@Input() |
|||
dialogRef: MatDialogRef<any>; |
|||
|
|||
commandForm: FormGroup; |
|||
|
|||
isConnector: boolean; |
|||
|
|||
RPCCommands: Array<string> = [ |
|||
"Ping", |
|||
"Stats", |
|||
"Devices", |
|||
"Update", |
|||
"Version", |
|||
"Restart", |
|||
"Reboot" |
|||
] |
|||
|
|||
|
|||
constructor(protected router: Router, |
|||
protected store: Store<AppState>, |
|||
protected fb: FormBuilder, |
|||
protected translate: TranslateService, |
|||
protected attributeService: AttributeService, |
|||
protected deviceService: DeviceService, |
|||
protected dialogService: DialogService, |
|||
private cd: ChangeDetectorRef, |
|||
public dialog: MatDialog) { |
|||
super(store); |
|||
this.commandForm = this.fb.group({ |
|||
command: [null,[Validators.required]], |
|||
time: [60, [Validators.required]], |
|||
params: ["{}", [Validators.required]], |
|||
result: [null] |
|||
}) |
|||
|
|||
|
|||
} |
|||
|
|||
|
|||
ngAfterViewInit() { |
|||
this.isConnector = this.ctx.settings.isConnector; |
|||
if (!this.isConnector) { |
|||
this.commandForm.get('command').setValue(this.RPCCommands[0]); |
|||
} |
|||
} |
|||
|
|||
|
|||
sendCommand() { |
|||
const formValues = this.commandForm.value; |
|||
this.ctx.controlApi.sendTwoWayCommand(formValues.command.toLowerCase(), {},formValues.time).subscribe(resp=>{ |
|||
console.log(resp); |
|||
this.commandForm.get('result').setValue(resp); |
|||
},error => { |
|||
console.log(error); |
|||
this.commandForm.get('result').setValue(error.error); |
|||
}) |
|||
} |
|||
|
|||
openEditJSONDialog($event: Event) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
this.dialog.open<JsonObjectEditDialogComponent, JsonObjectEditDialogData, object>(JsonObjectEditDialogComponent, { |
|||
disableClose: true, |
|||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
|||
data: { |
|||
jsonValue: JSON.parse(this.commandForm.get('params').value) |
|||
} |
|||
}).afterClosed().subscribe( |
|||
(res) => { |
|||
if (res) { |
|||
this.commandForm.get('params').setValue(JSON.stringify(res)); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 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 class="statistics-container" fxLayout="row"> |
|||
<mat-card [formGroup]="statisticForm"> |
|||
<mat-form-field class="mat-block" > |
|||
<mat-label>Statistic</mat-label> |
|||
<mat-select formControlName="statisticKey"> |
|||
<mat-option *ngFor="let key of statisticsKeys" [value]="key"> |
|||
{{key}} |
|||
</mat-option> |
|||
<mat-option *ngFor="let command of commands" [value]="command.attributeName"> |
|||
{{command.attributeName}} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field class="mat-block" *ngIf="commandObj"> |
|||
<mat-label>Timeout</mat-label> |
|||
<input matInput [value]="commandObj.timeout" disabled> |
|||
</mat-form-field> |
|||
<mat-form-field class="mat-block" *ngIf="commandObj"> |
|||
<mat-label>Command</mat-label> |
|||
<input matInput [value]="commandObj.command" disabled> |
|||
</mat-form-field> |
|||
</mat-card> |
|||
<div #statisticChart></div> |
|||
</div> |
|||
|
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2023 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 { |
|||
width: 100%; |
|||
height: 100%; |
|||
padding: 0; |
|||
|
|||
|
|||
.statistics-container { |
|||
height: 100%; |
|||
|
|||
mat-card { |
|||
width: 40%; |
|||
margin-right: 35px; |
|||
padding: 15px; |
|||
} |
|||
|
|||
div { |
|||
height: 100%; |
|||
flex-grow: 1; |
|||
} |
|||
|
|||
& > * { |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,150 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 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 { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, ViewChild } from '@angular/core'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { Router } from '@angular/router'; |
|||
import { FormBuilder, FormGroup } from '@angular/forms'; |
|||
import { MatDialog } from '@angular/material/dialog'; |
|||
import { AttributeService } from '@core/http/attribute.service'; |
|||
import { DeviceService } from '@core/http/device.service'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { AttributeScope, DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { DialogService } from '@app/core/services/dialog.service'; |
|||
import { WidgetContext } from '@home/models/widget-component.models'; |
|||
import { TbFlot } from '@home/components/widget/lib/flot-widget'; |
|||
import { ResizeObserver } from "@juggle/resize-observer"; |
|||
|
|||
|
|||
@Component({ |
|||
selector: 'tb-gateway-statistics', |
|||
templateUrl: './gateway-statistics.component.html', |
|||
styleUrls: ['./gateway-statistics.component.scss'] |
|||
}) |
|||
export class GatewayStatisticsComponent extends PageComponent implements AfterViewInit { |
|||
|
|||
@ViewChild('statisticChart') statisticChart: ElementRef; |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
private flot: TbFlot; |
|||
|
|||
public statisticForm: FormGroup; |
|||
|
|||
public statisticsKeys = []; |
|||
public commands = []; |
|||
public commandObj: any; |
|||
private resize$: ResizeObserver; |
|||
|
|||
|
|||
constructor(protected router: Router, |
|||
protected store: Store<AppState>, |
|||
protected fb: FormBuilder, |
|||
protected translate: TranslateService, |
|||
protected attributeService: AttributeService, |
|||
protected deviceService: DeviceService, |
|||
protected dialogService: DialogService, |
|||
private cd: ChangeDetectorRef, |
|||
public dialog: MatDialog) { |
|||
super(store); |
|||
this.statisticForm = this.fb.group({ |
|||
statisticKey: [this.statisticsKeys[0], []] |
|||
}) |
|||
|
|||
this.statisticForm.get('statisticKey').valueChanges.subscribe(value=>{ |
|||
this.commandObj = null; |
|||
if (this.commands.length) { |
|||
this.commandObj = this.commands.find(command=>command.attributeName === value); |
|||
} |
|||
this.changeSubscription(true); |
|||
}) |
|||
} |
|||
|
|||
|
|||
ngAfterViewInit() { |
|||
if (this.ctx.defaultSubscription.datasources.length) { |
|||
const gatewayId = this.ctx.defaultSubscription.datasources[0].entity.id; |
|||
this.attributeService.getEntityAttributes(gatewayId, AttributeScope.SHARED_SCOPE, ["configuration"]).subscribe(resp=>{ |
|||
if (resp && resp.length) { |
|||
this.commands = resp[0].value.thingsboard.thingsboard.statistics.commands; |
|||
if (!this.statisticForm.get('statisticKey').value ) { |
|||
this.statisticForm.get('statisticKey').setValue(this.commands[0].attributeName); |
|||
this.changeSubscription(true); |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
this.ctx.defaultSubscription.onTimewindowChangeFunction = timeWindow => { |
|||
this.ctx.defaultSubscription.options.timeWindowConfig = timeWindow; |
|||
this.ctx.defaultSubscription.updateTimewindowConfig(timeWindow); |
|||
this.ctx.defaultSubscription.update(); |
|||
this.updateChart(); |
|||
return timeWindow; |
|||
} |
|||
this.changeSubscription(true); |
|||
this.resize$ = new ResizeObserver(() => { |
|||
this.resize(); |
|||
}); |
|||
this.resize$.observe(this.statisticChart.nativeElement) |
|||
} |
|||
|
|||
initChart = () => { |
|||
if (this.ctx.defaultSubscription.data.length && !this.flot) { |
|||
this.ctx.$container = $(this.statisticChart.nativeElement); |
|||
this.flot = new TbFlot(this.ctx, 'line'); |
|||
} else setTimeout(this.initChart, 500); |
|||
} |
|||
|
|||
updateChart = () => { |
|||
if (this.flot && this.ctx.defaultSubscription.data.length) { |
|||
this.flot.update(); |
|||
} |
|||
} |
|||
|
|||
resize = () => { |
|||
if (this.flot) { |
|||
this.flot.resize(); |
|||
} |
|||
} |
|||
|
|||
|
|||
changeSubscription(init?: boolean) { |
|||
if (this.ctx.datasources[0].entity) { |
|||
if (this.flot) { |
|||
this.flot.destroy(); |
|||
delete this.flot; |
|||
} |
|||
this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ |
|||
name: this.statisticForm.get('statisticKey').value, |
|||
type: DataKeyType.timeseries, |
|||
settings: {}, |
|||
color: "#2196f3" |
|||
}]; |
|||
this.ctx.defaultSubscription.unsubscribe(); |
|||
this.ctx.defaultSubscription.updateDataSubscriptions(); |
|||
if (init) this.initChart(); |
|||
this.ctx.defaultSubscription.options.callbacks.dataLoading = ()=>{}; |
|||
this.ctx.defaultSubscription.callbacks.onDataUpdated = () => { |
|||
this.updateChart(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue