31 changed files with 1141 additions and 151 deletions
@ -0,0 +1,157 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import com.datastax.driver.core.utils.UUIDs; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.Type; |
|||
import org.hibernate.annotations.TypeDef; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.UUIDConverter; |
|||
import org.thingsboard.server.common.data.alarm.Alarm; |
|||
import org.thingsboard.server.common.data.alarm.AlarmId; |
|||
import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
|||
import org.thingsboard.server.common.data.alarm.AlarmStatus; |
|||
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.dao.model.BaseEntity; |
|||
import org.thingsboard.server.dao.model.BaseSqlEntity; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
|||
|
|||
import javax.persistence.*; |
|||
|
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY; |
|||
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@TypeDef(name = "json", typeClass = JsonStringType.class) |
|||
@MappedSuperclass |
|||
public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity<T> implements BaseEntity<T> { |
|||
|
|||
@Column(name = ALARM_TENANT_ID_PROPERTY) |
|||
private String tenantId; |
|||
|
|||
@Column(name = ALARM_ORIGINATOR_ID_PROPERTY) |
|||
private String originatorId; |
|||
|
|||
@Column(name = ALARM_ORIGINATOR_TYPE_PROPERTY) |
|||
private EntityType originatorType; |
|||
|
|||
@Column(name = ALARM_TYPE_PROPERTY) |
|||
private String type; |
|||
|
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ALARM_SEVERITY_PROPERTY) |
|||
private AlarmSeverity severity; |
|||
|
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ALARM_STATUS_PROPERTY) |
|||
private AlarmStatus status; |
|||
|
|||
@Column(name = ALARM_START_TS_PROPERTY) |
|||
private Long startTs; |
|||
|
|||
@Column(name = ALARM_END_TS_PROPERTY) |
|||
private Long endTs; |
|||
|
|||
@Column(name = ALARM_ACK_TS_PROPERTY) |
|||
private Long ackTs; |
|||
|
|||
@Column(name = ALARM_CLEAR_TS_PROPERTY) |
|||
private Long clearTs; |
|||
|
|||
@Type(type = "json") |
|||
@Column(name = ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY) |
|||
private JsonNode details; |
|||
|
|||
@Column(name = ALARM_PROPAGATE_PROPERTY) |
|||
private Boolean propagate; |
|||
|
|||
public AbstractAlarmEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public AbstractAlarmEntity(Alarm alarm) { |
|||
if (alarm.getId() != null) { |
|||
this.setId(alarm.getId().getId()); |
|||
} |
|||
if (alarm.getTenantId() != null) { |
|||
this.tenantId = UUIDConverter.fromTimeUUID(alarm.getTenantId().getId()); |
|||
} |
|||
this.type = alarm.getType(); |
|||
this.originatorId = UUIDConverter.fromTimeUUID(alarm.getOriginator().getId()); |
|||
this.originatorType = alarm.getOriginator().getEntityType(); |
|||
this.type = alarm.getType(); |
|||
this.severity = alarm.getSeverity(); |
|||
this.status = alarm.getStatus(); |
|||
this.propagate = alarm.isPropagate(); |
|||
this.startTs = alarm.getStartTs(); |
|||
this.endTs = alarm.getEndTs(); |
|||
this.ackTs = alarm.getAckTs(); |
|||
this.clearTs = alarm.getClearTs(); |
|||
this.details = alarm.getDetails(); |
|||
} |
|||
|
|||
public AbstractAlarmEntity(AlarmEntity alarmEntity) { |
|||
this.setId(alarmEntity.getId()); |
|||
this.tenantId = alarmEntity.getTenantId(); |
|||
this.type = alarmEntity.getType(); |
|||
this.originatorId = alarmEntity.getOriginatorId(); |
|||
this.originatorType = alarmEntity.getOriginatorType(); |
|||
this.type = alarmEntity.getType(); |
|||
this.severity = alarmEntity.getSeverity(); |
|||
this.status = alarmEntity.getStatus(); |
|||
this.propagate = alarmEntity.getPropagate(); |
|||
this.startTs = alarmEntity.getStartTs(); |
|||
this.endTs = alarmEntity.getEndTs(); |
|||
this.ackTs = alarmEntity.getAckTs(); |
|||
this.clearTs = alarmEntity.getClearTs(); |
|||
this.details = alarmEntity.getDetails(); |
|||
} |
|||
|
|||
protected Alarm toAlarm() { |
|||
Alarm alarm = new Alarm(new AlarmId(UUIDConverter.fromString(id))); |
|||
alarm.setCreatedTime(UUIDs.unixTimestamp(UUIDConverter.fromString(id))); |
|||
if (tenantId != null) { |
|||
alarm.setTenantId(new TenantId(UUIDConverter.fromString(tenantId))); |
|||
} |
|||
alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, UUIDConverter.fromString(originatorId))); |
|||
alarm.setType(type); |
|||
alarm.setSeverity(severity); |
|||
alarm.setStatus(status); |
|||
alarm.setPropagate(propagate); |
|||
alarm.setStartTs(startTs); |
|||
alarm.setEndTs(endTs); |
|||
alarm.setAckTs(ackTs); |
|||
alarm.setClearTs(clearTs); |
|||
alarm.setDetails(details); |
|||
return alarm; |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.alarm.AlarmInfo; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class AlarmInfoEntity extends AbstractAlarmEntity<AlarmInfo> { |
|||
|
|||
private String originatorName; |
|||
|
|||
public AlarmInfoEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public AlarmInfoEntity(AlarmEntity alarmEntity) { |
|||
super(alarmEntity); |
|||
} |
|||
|
|||
@Override |
|||
public AlarmInfo toData() { |
|||
return new AlarmInfo(super.toAlarm(), this.originatorName); |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
///
|
|||
/// Copyright © 2016-2019 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 { Injectable } from '@angular/core'; |
|||
import { defaultHttpOptions } from './http-utils'; |
|||
import { Observable } from 'rxjs/index'; |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { PageData } from '@shared/models/page/page-data'; |
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
import { |
|||
Alarm, |
|||
AlarmInfo, |
|||
AlarmQuery, |
|||
AlarmSearchStatus, |
|||
AlarmSeverity, |
|||
AlarmStatus |
|||
} from '@shared/models/alarm.models'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root' |
|||
}) |
|||
export class AlarmService { |
|||
|
|||
constructor( |
|||
private http: HttpClient |
|||
) { } |
|||
|
|||
public getAlarm(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Alarm> { |
|||
return this.http.get<Alarm>(`/api/alarm/${alarmId}`, defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
|
|||
public getAlarmInfo(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<AlarmInfo> { |
|||
return this.http.get<AlarmInfo>(`/api/alarm/info/${alarmId}`, defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
|
|||
public saveAlarm(alarm: Alarm, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<Alarm> { |
|||
return this.http.post<Alarm>('/api/alarm', alarm, defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
|
|||
public ackAlarm(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<void> { |
|||
return this.http.post<void>(`/api/alarm/${alarmId}/ack`, null, defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
|
|||
public clearAlarm(alarmId: string, ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<void> { |
|||
return this.http.post<void>(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
|
|||
public getAlarms(query: AlarmQuery, |
|||
ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<PageData<AlarmInfo>> { |
|||
return this.http.get<PageData<AlarmInfo>>(`/api/alarm${query.toQuery()}`, |
|||
defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
|
|||
public getHighestAlarmSeverity(entityId: EntityId, alarmSearchStatus: AlarmSearchStatus, alarmStatus: AlarmStatus, |
|||
ignoreErrors: boolean = false, ignoreLoading: boolean = false): Observable<AlarmSeverity> { |
|||
let url = `/api/alarm/highestSeverity/${entityId.entityType}/${entityId.entityType}`; |
|||
if (alarmSearchStatus) { |
|||
url += `?searchStatus=${alarmSearchStatus}`; |
|||
} else if (alarmStatus) { |
|||
url += `?status=${alarmStatus}`; |
|||
} |
|||
return this.http.get<AlarmSeverity>(url, |
|||
defaultHttpOptions(ignoreLoading, ignoreErrors)); |
|||
} |
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2019 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. |
|||
|
|||
--> |
|||
<form [formGroup]="alarmFormGroup" style="width: 600px;"> |
|||
<mat-toolbar fxLayout="row" color="primary"> |
|||
<h2>{{ 'alarm.alarm-details' | translate }}</h2> |
|||
<span fxFlex></span> |
|||
<button mat-button mat-icon-button |
|||
(click)="close()" |
|||
type="button"> |
|||
<mat-icon class="material-icons">close</mat-icon> |
|||
</button> |
|||
</mat-toolbar> |
|||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
|||
</mat-progress-bar> |
|||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> |
|||
<div mat-dialog-content> |
|||
<fieldset [disabled]="isLoading$ | async"> |
|||
<div fxLayout="row" fxLayoutGap="6px"> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>alarm.created-time</mat-label> |
|||
<input matInput formControlName="createdTime" readonly> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.originator</mat-label> |
|||
<input matInput formControlName="originatorName" readonly> |
|||
</mat-form-field> |
|||
</div> |
|||
<div fxLayout="row" fxLayoutGap="6px" *ngIf="alarmFormGroup.get('startTime').value || |
|||
alarmFormGroup.get('endTime').value"> |
|||
<mat-form-field *ngIf="alarmFormGroup.get('startTime').value" fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.start-time</mat-label> |
|||
<input matInput formControlName="startTime" readonly> |
|||
</mat-form-field> |
|||
<mat-form-field *ngIf="alarmFormGroup.get('endTime').value" fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.end-time</mat-label> |
|||
<input matInput formControlName="endTime" readonly> |
|||
</mat-form-field> |
|||
<span fxFlex *ngIf="!alarmFormGroup.get('startTime').value || !alarmFormGroup.get('endTime').value"></span> |
|||
</div> |
|||
<div fxLayout="row" fxLayoutGap="6px" *ngIf="alarmFormGroup.get('ackTime').value || |
|||
alarmFormGroup.get('clearTime').value"> |
|||
<mat-form-field *ngIf="alarmFormGroup.get('ackTime').value" fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.ack-time</mat-label> |
|||
<input matInput formControlName="ackTime" readonly> |
|||
</mat-form-field> |
|||
<mat-form-field *ngIf="alarmFormGroup.get('clearTime').value" fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.clear-time</mat-label> |
|||
<input matInput formControlName="clearTime" readonly> |
|||
</mat-form-field> |
|||
<span fxFlex *ngIf="!alarmFormGroup.get('ackTime').value || !alarmFormGroup.get('clearTime').value"></span> |
|||
</div> |
|||
<div fxLayout="row" fxLayoutGap="6px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.type</mat-label> |
|||
<input matInput formControlName="type" readonly> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.severity</mat-label> |
|||
<input matInput formControlName="alarmSeverity" readonly |
|||
[ngStyle]="{fontWeight: 'bold', color: alarmSeverityColorsMap.get((alarm$ | async)?.severity)}"> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>alarm.status</mat-label> |
|||
<input matInput formControlName="alarmStatus" readonly> |
|||
</mat-form-field> |
|||
</div> |
|||
<tb-json-object-edit |
|||
*ngIf="displayDetails" |
|||
formControlName="alarmDetails" |
|||
readonly |
|||
label="{{ 'alarm.details' | translate }}"> |
|||
</tb-json-object-edit> |
|||
</fieldset> |
|||
</div> |
|||
<div mat-dialog-actions fxLayout="row"> |
|||
<div fxLayout="row" *ngIf="alarm$ | async; let alarm;"> |
|||
<button *ngIf="allowAcknowledgment && (alarm.status === alarmStatuses.ACTIVE_UNACK || |
|||
alarm.status === alarmStatuses.CLEARED_UNACK)" |
|||
mat-button |
|||
mat-raised-button |
|||
color="primary" |
|||
type="button" |
|||
(click)="acknowledge()" |
|||
style="margin-right: 20px;" |
|||
[disabled]="(isLoading$ | async)"> |
|||
{{ 'alarm.acknowledge' | translate }} |
|||
</button> |
|||
<button *ngIf="allowClear && (alarm.status === alarmStatuses.ACTIVE_ACK || |
|||
alarm.status === alarmStatuses.ACTIVE_UNACK)" |
|||
mat-button |
|||
mat-raised-button |
|||
color="primary" |
|||
type="button" |
|||
(click)="clear()" |
|||
[disabled]="(isLoading$ | async)"> |
|||
{{ 'alarm.clear' | translate }} |
|||
</button> |
|||
</div> |
|||
<span fxFlex></span> |
|||
<button mat-button color="primary" |
|||
style="margin-right: 20px;" |
|||
type="button" |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="close()" cdkFocusInitial> |
|||
{{ 'action.close' | translate }} |
|||
</button> |
|||
</div> |
|||
</form> |
|||
@ -0,0 +1,155 @@ |
|||
///
|
|||
/// Copyright © 2016-2019 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, Inject, OnInit } from '@angular/core'; |
|||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { FormBuilder, FormGroup } from '@angular/forms'; |
|||
import { Observable, Subject } from 'rxjs'; |
|||
import { Router } from '@angular/router'; |
|||
import { DialogComponent } from '@app/shared/components/dialog.component'; |
|||
import { |
|||
AlarmInfo, |
|||
alarmSeverityColors, |
|||
alarmSeverityTranslations, |
|||
AlarmStatus, |
|||
alarmStatusTranslations |
|||
} from '@app/shared/models/alarm.models'; |
|||
import { AlarmService } from '@core/http/alarm.service'; |
|||
import { share, tap } from 'rxjs/operators'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
|
|||
export interface AlarmDetailsDialogData { |
|||
alarmId: string; |
|||
allowAcknowledgment: boolean; |
|||
allowClear: boolean; |
|||
displayDetails: boolean; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'tb-alarm-details-dialog', |
|||
templateUrl: './alarm-details-dialog.component.html', |
|||
styleUrls: [] |
|||
}) |
|||
export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDialogComponent, boolean> implements OnInit { |
|||
|
|||
alarmFormGroup: FormGroup; |
|||
|
|||
allowAcknowledgment: boolean; |
|||
allowClear: boolean; |
|||
displayDetails: boolean; |
|||
|
|||
loadAlarmSubject = new Subject<AlarmInfo>(); |
|||
alarm$: Observable<AlarmInfo> = this.loadAlarmSubject.asObservable().pipe( |
|||
tap(alarm => this.loadAlarmFields(alarm)), |
|||
share() |
|||
); |
|||
|
|||
alarmSeverityColorsMap = alarmSeverityColors; |
|||
alarmStatuses = AlarmStatus; |
|||
|
|||
alarmUpdated = false; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected router: Router, |
|||
private datePipe: DatePipe, |
|||
private translate: TranslateService, |
|||
@Inject(MAT_DIALOG_DATA) public data: AlarmDetailsDialogData, |
|||
private alarmService: AlarmService, |
|||
public dialogRef: MatDialogRef<AlarmDetailsDialogComponent, boolean>, |
|||
public fb: FormBuilder) { |
|||
super(store, router, dialogRef); |
|||
|
|||
this.allowAcknowledgment = data.allowAcknowledgment; |
|||
this.allowClear = data.allowClear; |
|||
this.displayDetails = data.displayDetails; |
|||
|
|||
this.alarmFormGroup = this.fb.group( |
|||
{ |
|||
createdTime: [''], |
|||
originatorName: [''], |
|||
startTime: [''], |
|||
endTime: [''], |
|||
ackTime: [''], |
|||
clearTime: [''], |
|||
type: [''], |
|||
alarmSeverity: [''], |
|||
alarmStatus: [''], |
|||
alarmDetails: [null] |
|||
} |
|||
); |
|||
|
|||
this.loadAlarm(); |
|||
|
|||
} |
|||
|
|||
loadAlarm() { |
|||
this.alarmService.getAlarmInfo(this.data.alarmId).subscribe( |
|||
alarm => this.loadAlarmSubject.next(alarm) |
|||
); |
|||
} |
|||
|
|||
loadAlarmFields(alarm: AlarmInfo) { |
|||
this.alarmFormGroup.get('createdTime') |
|||
.patchValue(this.datePipe.transform(alarm.createdTime, 'yyyy-MM-dd HH:mm:ss')); |
|||
this.alarmFormGroup.get('originatorName') |
|||
.patchValue(alarm.originatorName); |
|||
if (alarm.startTs) { |
|||
this.alarmFormGroup.get('startTime') |
|||
.patchValue(this.datePipe.transform(alarm.startTs, 'yyyy-MM-dd HH:mm:ss')); |
|||
} |
|||
if (alarm.endTs) { |
|||
this.alarmFormGroup.get('endTime') |
|||
.patchValue(this.datePipe.transform(alarm.endTs, 'yyyy-MM-dd HH:mm:ss')); |
|||
} |
|||
if (alarm.ackTs) { |
|||
this.alarmFormGroup.get('ackTime') |
|||
.patchValue(this.datePipe.transform(alarm.ackTs, 'yyyy-MM-dd HH:mm:ss')); |
|||
} |
|||
if (alarm.clearTs) { |
|||
this.alarmFormGroup.get('clearTime') |
|||
.patchValue(this.datePipe.transform(alarm.clearTs, 'yyyy-MM-dd HH:mm:ss')); |
|||
} |
|||
this.alarmFormGroup.get('type').patchValue(alarm.type); |
|||
this.alarmFormGroup.get('alarmSeverity') |
|||
.patchValue(this.translate.instant(alarmSeverityTranslations.get(alarm.severity))); |
|||
this.alarmFormGroup.get('alarmStatus') |
|||
.patchValue(this.translate.instant(alarmStatusTranslations.get(alarm.status))); |
|||
this.alarmFormGroup.get('alarmDetails').patchValue(alarm.details); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
} |
|||
|
|||
close(): void { |
|||
this.dialogRef.close(this.alarmUpdated); |
|||
} |
|||
|
|||
acknowledge(): void { |
|||
this.alarmService.ackAlarm(this.data.alarmId).subscribe( |
|||
() => { this.alarmUpdated = true; this.loadAlarm(); } |
|||
); |
|||
} |
|||
|
|||
clear(): void { |
|||
this.alarmService.clearAlarm(this.data.alarmId).subscribe( |
|||
() => { this.alarmUpdated = true; this.loadAlarm(); } |
|||
); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,137 @@ |
|||
///
|
|||
/// Copyright © 2016-2019 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 { |
|||
DateEntityTableColumn, |
|||
EntityTableColumn, |
|||
EntityTableConfig |
|||
} from '@home/models/entity/entities-table-config.models'; |
|||
import { EntityType, EntityTypeResource, entityTypeTranslations } from '@shared/models/entity-type.models'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { Direction } from '@shared/models/page/sort-order'; |
|||
import { MatDialog } from '@angular/material'; |
|||
import { TimePageLink } from '@shared/models/page/page-link'; |
|||
import { Observable } from 'rxjs'; |
|||
import { PageData } from '@shared/models/page/page-data'; |
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
import { |
|||
AlarmInfo, |
|||
AlarmQuery, |
|||
AlarmSearchStatus, |
|||
alarmSeverityColors, |
|||
alarmSeverityTranslations, |
|||
alarmStatusTranslations |
|||
} from '@app/shared/models/alarm.models'; |
|||
import { AlarmService } from '@app/core/http/alarm.service'; |
|||
import { DialogService } from '@core/services/dialog.service'; |
|||
import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component'; |
|||
import { |
|||
AuditLogDetailsDialogComponent, |
|||
AuditLogDetailsDialogData |
|||
} from '@home/components/audit-log/audit-log-details-dialog.component'; |
|||
import { |
|||
AlarmDetailsDialogComponent, |
|||
AlarmDetailsDialogData |
|||
} from '@home/components/alarm/alarm-details-dialog.component'; |
|||
|
|||
export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink> { |
|||
|
|||
searchStatus: AlarmSearchStatus; |
|||
|
|||
constructor(private alarmService: AlarmService, |
|||
private dialogService: DialogService, |
|||
private translate: TranslateService, |
|||
private datePipe: DatePipe, |
|||
private dialog: MatDialog, |
|||
public entityId: EntityId = null, |
|||
private defaultSearchStatus: AlarmSearchStatus = AlarmSearchStatus.ANY) { |
|||
super(); |
|||
this.loadDataOnInit = false; |
|||
this.tableTitle = ''; |
|||
this.useTimePageLink = true; |
|||
this.detailsPanelEnabled = false; |
|||
this.selectionEnabled = false; |
|||
this.searchEnabled = true; |
|||
this.addEnabled = false; |
|||
this.entitiesDeleteEnabled = false; |
|||
this.actionsColumnTitle = 'alarm.details'; |
|||
this.entityType = EntityType.ALARM; |
|||
this.entityTranslations = entityTypeTranslations.get(EntityType.ALARM); |
|||
this.entityResources = { |
|||
} as EntityTypeResource; |
|||
this.searchStatus = defaultSearchStatus; |
|||
|
|||
this.headerComponent = AlarmTableHeaderComponent; |
|||
|
|||
this.entitiesFetchFunction = pageLink => this.fetchAlarms(pageLink); |
|||
|
|||
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; |
|||
|
|||
this.columns.push( |
|||
new DateEntityTableColumn<AlarmInfo>('createdTime', 'alarm.created-time', this.datePipe, '150px')); |
|||
this.columns.push( |
|||
new EntityTableColumn<AlarmInfo>('originatorName', 'alarm.originator', '100%', |
|||
(entity) => entity.originatorName, entity => ({}), false)); |
|||
this.columns.push( |
|||
new EntityTableColumn<AlarmInfo>('type', 'alarm.type', '100%')); |
|||
this.columns.push( |
|||
new EntityTableColumn<AlarmInfo>('severity', 'alarm.severity', '100%', |
|||
(entity) => this.translate.instant(alarmSeverityTranslations.get(entity.severity)), |
|||
entity => ({ |
|||
fontWeight: 'bold', |
|||
color: alarmSeverityColors.get(entity.severity) |
|||
}))); |
|||
this.columns.push( |
|||
new EntityTableColumn<AlarmInfo>('status', 'alarm.status', '100%', |
|||
(entity) => this.translate.instant(alarmStatusTranslations.get(entity.status)))); |
|||
|
|||
this.cellActionDescriptors.push( |
|||
{ |
|||
name: this.translate.instant('alarm.details'), |
|||
icon: 'more_horiz', |
|||
isEnabled: () => true, |
|||
onAction: ($event, entity) => this.showAlarmDetails(entity) |
|||
} |
|||
); |
|||
} |
|||
|
|||
fetchAlarms(pageLink: TimePageLink): Observable<PageData<AlarmInfo>> { |
|||
const query = new AlarmQuery(this.entityId, pageLink, this.searchStatus, null, true); |
|||
return this.alarmService.getAlarms(query); |
|||
} |
|||
|
|||
showAlarmDetails(entity: AlarmInfo) { |
|||
this.dialog.open<AlarmDetailsDialogComponent, AlarmDetailsDialogData, boolean> |
|||
(AlarmDetailsDialogComponent, |
|||
{ |
|||
disableClose: true, |
|||
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
|||
data: { |
|||
alarmId: entity.id.id, |
|||
allowAcknowledgment: true, |
|||
allowClear: true, |
|||
displayDetails: true |
|||
} |
|||
}).afterClosed().subscribe( |
|||
(res) => { |
|||
if (res) { |
|||
this.table.updateData(); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2019 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-form-field class="mat-block" style="width: 200px;"> |
|||
<mat-label translate>alarm.alarm-status</mat-label> |
|||
<mat-select matInput [ngModel]="alarmTableConfig.searchStatus" |
|||
(ngModelChange)="searchStatusChanged($event)"> |
|||
<mat-option *ngFor="let status of alarmSearchStatusTypes" [value]="status"> |
|||
{{ alarmSearchStatusTranslationsMap.get(status) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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 { |
|||
/* flex: 1; |
|||
display: flex; |
|||
justify-content: flex-start; */ |
|||
padding-right: 8px; |
|||
} |
|||
|
|||
:host ::ng-deep { |
|||
mat-form-field { |
|||
font-size: 16px; |
|||
|
|||
.mat-form-field-wrapper { |
|||
padding-bottom: 0; |
|||
} |
|||
|
|||
.mat-form-field-underline { |
|||
bottom: 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
///
|
|||
/// Copyright © 2016-2019 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 { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { EntityTableHeaderComponent } from '../../components/entity/entity-table-header.component'; |
|||
import { AlarmInfo, AlarmSearchStatus, alarmSearchStatusTranslations } from '@shared/models/alarm.models'; |
|||
import { AlarmTableConfig } from './alarm-table-config'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-alarm-table-header', |
|||
templateUrl: './alarm-table-header.component.html', |
|||
styleUrls: ['./alarm-table-header.component.scss'] |
|||
}) |
|||
export class AlarmTableHeaderComponent extends EntityTableHeaderComponent<AlarmInfo> { |
|||
|
|||
alarmSearchStatusTranslationsMap = alarmSearchStatusTranslations; |
|||
|
|||
alarmSearchStatusTypes = Object.keys(AlarmSearchStatus); |
|||
|
|||
get alarmTableConfig(): AlarmTableConfig { |
|||
return this.entitiesTableConfig as AlarmTableConfig; |
|||
} |
|||
|
|||
constructor(protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
searchStatusChanged(searchStatus: AlarmSearchStatus) { |
|||
this.alarmTableConfig.searchStatus = searchStatus; |
|||
this.alarmTableConfig.table.updateData(); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2019 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. |
|||
|
|||
--> |
|||
<tb-entities-table [entitiesTableConfig]="alarmTableConfig"></tb-entities-table> |
|||
@ -0,0 +1,22 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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 { |
|||
tb-entities-table { |
|||
.mat-drawer-container { |
|||
background-color: white; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,90 @@ |
|||
///
|
|||
/// Copyright © 2016-2019 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, ViewChild } from '@angular/core'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { MatDialog } from '@angular/material'; |
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { DialogService } from '@core/services/dialog.service'; |
|||
import { AlarmTableConfig } from './alarm-table-config'; |
|||
import { AlarmSearchStatus } from '@shared/models/alarm.models'; |
|||
import { AlarmService } from '@app/core/http/alarm.service'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-alarm-table', |
|||
templateUrl: './alarm-table.component.html', |
|||
styleUrls: ['./alarm-table.component.scss'] |
|||
}) |
|||
export class AlarmTableComponent implements OnInit { |
|||
|
|||
activeValue = false; |
|||
dirtyValue = false; |
|||
entityIdValue: EntityId; |
|||
|
|||
@Input() |
|||
set active(active: boolean) { |
|||
if (this.activeValue !== active) { |
|||
this.activeValue = active; |
|||
if (this.activeValue && this.dirtyValue) { |
|||
this.dirtyValue = false; |
|||
this.entitiesTable.updateData(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@Input() |
|||
set entityId(entityId: EntityId) { |
|||
this.entityIdValue = entityId; |
|||
if (this.alarmTableConfig && this.alarmTableConfig.entityId !== entityId) { |
|||
this.alarmTableConfig.searchStatus = AlarmSearchStatus.ANY; |
|||
this.alarmTableConfig.entityId = entityId; |
|||
this.entitiesTable.resetSortAndFilter(this.activeValue); |
|||
if (!this.activeValue) { |
|||
this.dirtyValue = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ViewChild(EntitiesTableComponent, {static: true}) entitiesTable: EntitiesTableComponent; |
|||
|
|||
alarmTableConfig: AlarmTableConfig; |
|||
|
|||
constructor(private alarmService: AlarmService, |
|||
private dialogService: DialogService, |
|||
private translate: TranslateService, |
|||
private datePipe: DatePipe, |
|||
private dialog: MatDialog, |
|||
private store: Store<AppState>) { |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.dirtyValue = !this.activeValue; |
|||
this.alarmTableConfig = new AlarmTableConfig( |
|||
this.alarmService, |
|||
this.dialogService, |
|||
this.translate, |
|||
this.datePipe, |
|||
this.dialog, |
|||
this.entityIdValue, |
|||
AlarmSearchStatus.ANY |
|||
); |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue