Commit 0726fbb546720f53ba6a265eb7b8ed9ded5ecffe

Authored by Igor Kulikov
Committed by GitHub
2 parents f17f502c 3e0ef116

Merge pull request #167 from thingsboard/feature/TB-63

TB-63: Add new 'Alarms' tab in entity details.
Showing 41 changed files with 937 additions and 48 deletions
@@ -58,6 +58,19 @@ public class AlarmController extends BaseController { @@ -58,6 +58,19 @@ public class AlarmController extends BaseController {
58 } 58 }
59 59
60 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 60 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  61 + @RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
  62 + @ResponseBody
  63 + public AlarmInfo getAlarmInfoById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
  64 + checkParameter("alarmId", strAlarmId);
  65 + try {
  66 + AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
  67 + return checkAlarmInfoId(alarmId);
  68 + } catch (Exception e) {
  69 + throw handleException(e);
  70 + }
  71 + }
  72 +
  73 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
61 @RequestMapping(value = "/alarm", method = RequestMethod.POST) 74 @RequestMapping(value = "/alarm", method = RequestMethod.POST)
62 @ResponseBody 75 @ResponseBody
63 public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException { 76 public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
@@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.ActorService; @@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.ActorService;
27 import org.thingsboard.server.common.data.*; 27 import org.thingsboard.server.common.data.*;
28 import org.thingsboard.server.common.data.alarm.Alarm; 28 import org.thingsboard.server.common.data.alarm.Alarm;
29 import org.thingsboard.server.common.data.alarm.AlarmId; 29 import org.thingsboard.server.common.data.alarm.AlarmId;
  30 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
30 import org.thingsboard.server.common.data.asset.Asset; 31 import org.thingsboard.server.common.data.asset.Asset;
31 import org.thingsboard.server.common.data.id.*; 32 import org.thingsboard.server.common.data.id.*;
32 import org.thingsboard.server.common.data.page.TextPageLink; 33 import org.thingsboard.server.common.data.page.TextPageLink;
@@ -351,6 +352,17 @@ public abstract class BaseController { @@ -351,6 +352,17 @@ public abstract class BaseController {
351 } 352 }
352 } 353 }
353 354
  355 + AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException {
  356 + try {
  357 + validateId(alarmId, "Incorrect alarmId " + alarmId);
  358 + AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get();
  359 + checkAlarm(alarmInfo);
  360 + return alarmInfo;
  361 + } catch (Exception e) {
  362 + throw handleException(e, false);
  363 + }
  364 + }
  365 +
354 protected void checkAlarm(Alarm alarm) throws ThingsboardException { 366 protected void checkAlarm(Alarm alarm) throws ThingsboardException {
355 checkNotNull(alarm); 367 checkNotNull(alarm);
356 checkTenantId(alarm.getTenantId()); 368 checkTenantId(alarm.getTenantId());
@@ -55,6 +55,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName { @@ -55,6 +55,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
55 55
56 public Alarm(Alarm alarm) { 56 public Alarm(Alarm alarm) {
57 super(alarm.getId()); 57 super(alarm.getId());
  58 + this.createdTime = alarm.getCreatedTime();
58 this.tenantId = alarm.getTenantId(); 59 this.tenantId = alarm.getTenantId();
59 this.type = alarm.getType(); 60 this.type = alarm.getType();
60 this.originator = alarm.getOriginator(); 61 this.originator = alarm.getOriginator();
@@ -35,6 +35,8 @@ public interface AlarmService { @@ -35,6 +35,8 @@ public interface AlarmService {
35 35
36 ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId); 36 ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
37 37
  38 + ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId);
  39 +
38 ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query); 40 ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
39 41
40 } 42 }
@@ -199,6 +199,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -199,6 +199,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
199 } 199 }
200 200
201 @Override 201 @Override
  202 + public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
  203 + log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
  204 + validateId(alarmId, "Incorrect alarmId " + alarmId);
  205 + return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()),
  206 + (AsyncFunction<Alarm, AlarmInfo>) alarm1 -> {
  207 + AlarmInfo alarmInfo = new AlarmInfo(alarm1);
  208 + return Futures.transform(
  209 + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
  210 + originatorName -> {
  211 + alarmInfo.setOriginatorName(originatorName);
  212 + return alarmInfo;
  213 + }
  214 + );
  215 + });
  216 + }
  217 +
  218 + @Override
202 public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) { 219 public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
203 ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query); 220 ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
204 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { 221 if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import 'brace/ext/language_tools';
  17 +import 'brace/mode/json';
  18 +import 'brace/theme/github';
  19 +import beautify from 'js-beautify';
  20 +
  21 +import './alarm-details-dialog.scss';
  22 +
  23 +const js_beautify = beautify.js;
  24 +
  25 +/*@ngInject*/
  26 +export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) {
  27 +
  28 + var vm = this;
  29 +
  30 + vm.alarmId = alarmId;
  31 + vm.types = types;
  32 + vm.alarm = null;
  33 +
  34 + vm.alarmUpdated = false;
  35 +
  36 + showingCallback.onShowing = function(scope, element) {
  37 + updateEditorSize(element);
  38 + }
  39 +
  40 + vm.alarmDetailsOptions = {
  41 + useWrapMode: false,
  42 + mode: 'json',
  43 + showGutter: false,
  44 + showPrintMargin: false,
  45 + theme: 'github',
  46 + advanced: {
  47 + enableSnippets: false,
  48 + enableBasicAutocompletion: false,
  49 + enableLiveAutocompletion: false
  50 + },
  51 + onLoad: function (_ace) {
  52 + vm.editor = _ace;
  53 + }
  54 + };
  55 +
  56 + vm.close = close;
  57 + vm.acknowledge = acknowledge;
  58 + vm.clear = clear;
  59 +
  60 + loadAlarm();
  61 +
  62 + function updateEditorSize(element) {
  63 + var newWidth = 600;
  64 + var newHeight = 200;
  65 + angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px")
  66 + .width(newWidth.toString() + "px");
  67 + vm.editor.resize();
  68 + }
  69 +
  70 + function loadAlarm() {
  71 + alarmService.getAlarmInfo(vm.alarmId).then(
  72 + function success(alarm) {
  73 + vm.alarm = alarm;
  74 + loadAlarmFields();
  75 + },
  76 + function fail() {
  77 + vm.alarm = null;
  78 + }
  79 + );
  80 + }
  81 +
  82 + function loadAlarmFields() {
  83 + vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss');
  84 + vm.startTime = null;
  85 + if (vm.alarm.startTs) {
  86 + vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss');
  87 + }
  88 + vm.endTime = null;
  89 + if (vm.alarm.endTs) {
  90 + vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss');
  91 + }
  92 + vm.ackTime = null;
  93 + if (vm.alarm.ackTs) {
  94 + vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss')
  95 + }
  96 + vm.clearTime = null;
  97 + if (vm.alarm.clearTs) {
  98 + vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss');
  99 + }
  100 +
  101 + vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name);
  102 +
  103 + vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status);
  104 +
  105 + vm.alarmDetails = null;
  106 + if (vm.alarm.details) {
  107 + vm.alarmDetails = angular.toJson(vm.alarm.details);
  108 + vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4});
  109 + }
  110 + }
  111 +
  112 + function acknowledge () {
  113 + alarmService.ackAlarm(vm.alarmId).then(
  114 + function success() {
  115 + vm.alarmUpdated = true;
  116 + loadAlarm();
  117 + }
  118 + );
  119 + }
  120 +
  121 + function clear () {
  122 + alarmService.clearAlarm(vm.alarmId).then(
  123 + function success() {
  124 + vm.alarmUpdated = true;
  125 + loadAlarm();
  126 + }
  127 + );
  128 + }
  129 +
  130 + function close () {
  131 + $mdDialog.hide(vm.alarmUpdated ? vm.alarm : null);
  132 + }
  133 +
  134 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +.tb-alarm-details-panel {
  18 + margin-left: 15px;
  19 + border: 1px solid #C0C0C0;
  20 + height: 100%;
  21 + #tb-alarm-details {
  22 + min-width: 600px;
  23 + min-height: 200px;
  24 + width: 100%;
  25 + height: 100%;
  26 + }
  27 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-dialog aria-label="{{ 'alarm.alarm-details' | translate }}">
  19 + <md-toolbar>
  20 + <div class="md-toolbar-tools">
  21 + <h2 translate>alarm.alarm-details</h2>
  22 + <span flex></span>
  23 + <md-button class="md-icon-button" ng-click="vm.close()">
  24 + <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
  25 + </md-button>
  26 + </div>
  27 + </md-toolbar>
  28 + <md-dialog-content>
  29 + <div class="md-dialog-content" layout="column">
  30 + <div layout="row">
  31 + <md-input-container class="md-block">
  32 + <label translate>alarm.created-time</label>
  33 + <input ng-model="vm.createdTime" readonly>
  34 + </md-input-container>
  35 + <md-input-container flex class="md-block">
  36 + <label translate>alarm.originator</label>
  37 + <input ng-model="vm.alarm.originatorName" readonly>
  38 + </md-input-container>
  39 + </div>
  40 + <div layout="row" ng-if="vm.startTime || vm.endTime">
  41 + <md-input-container ng-if="vm.startTime" flex class="md-block">
  42 + <label translate>alarm.start-time</label>
  43 + <input ng-model="vm.startTime" readonly>
  44 + </md-input-container>
  45 + <md-input-container ng-if="vm.endTime" flex class="md-block">
  46 + <label translate>alarm.end-time</label>
  47 + <input ng-model="vm.endTime" readonly>
  48 + </md-input-container>
  49 + <span flex ng-if="!vm.startTime || !vm.endTime"></span>
  50 + </div>
  51 + <div layout="row" ng-if="vm.ackTime || vm.clearTime">
  52 + <md-input-container ng-if="vm.ackTime" flex class="md-block">
  53 + <label translate>alarm.ack-time</label>
  54 + <input ng-model="vm.ackTime" readonly>
  55 + </md-input-container>
  56 + <md-input-container ng-if="vm.clearTime" flex class="md-block">
  57 + <label translate>alarm.clear-time</label>
  58 + <input ng-model="vm.clearTime" readonly>
  59 + </md-input-container>
  60 + <span flex ng-if="!vm.ackTime || !vm.clearTime"></span>
  61 + </div>
  62 + <div layout="row">
  63 + <md-input-container flex class="md-block">
  64 + <label translate>alarm.type</label>
  65 + <input ng-model="vm.alarm.type" readonly>
  66 + </md-input-container>
  67 + <md-input-container flex class="md-block">
  68 + <label translate>alarm.severity</label>
  69 + <input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class"
  70 + ng-model="vm.alarmSeverity" readonly>
  71 + </md-input-container>
  72 + <md-input-container flex class="md-block">
  73 + <label translate>alarm.status</label>
  74 + <input ng-model="vm.alarmStatus" readonly>
  75 + </md-input-container>
  76 + </div>
  77 + <div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div>
  78 + <div flex class="tb-alarm-details-panel" layout="column">
  79 + <div flex id="tb-alarm-details" readonly
  80 + ui-ace="vm.alarmDetailsOptions"
  81 + ng-model="vm.alarmDetails">
  82 + </div>
  83 + </div>
  84 + </div>
  85 + </md-dialog-content>
  86 + <md-dialog-actions layout="row">
  87 + <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack ||
  88 + vm.alarm.status==vm.types.alarmStatus.clearedUnack"
  89 + class="md-raised md-primary"
  90 + ng-disabled="loading"
  91 + ng-click="vm.acknowledge()"
  92 + style="margin-right:20px;">{{ 'alarm.acknowledge' |
  93 + translate }}
  94 + </md-button>
  95 + <md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck ||
  96 + vm.alarm.status==vm.types.alarmStatus.activeUnack"
  97 + class="md-raised md-primary"
  98 + ng-disabled="loading"
  99 + ng-click="vm.clear()">{{ 'alarm.clear' |
  100 + translate }}
  101 + </md-button>
  102 + <span flex></span>
  103 + <md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
  104 + translate }}
  105 + </md-button>
  106 + </md-dialog-actions>
  107 +</md-dialog>
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +/* eslint-disable import/no-unresolved, import/default */
  17 +
  18 +import alarmHeaderTemplate from './alarm-header.tpl.html';
  19 +
  20 +/* eslint-enable import/no-unresolved, import/default */
  21 +
  22 +/*@ngInject*/
  23 +export default function AlarmHeaderDirective($compile, $templateCache) {
  24 +
  25 + var linker = function (scope, element) {
  26 +
  27 + var template = $templateCache.get(alarmHeaderTemplate);
  28 + element.html(template);
  29 + $compile(element.contents())(scope);
  30 +
  31 + }
  32 +
  33 + return {
  34 + restrict: "A",
  35 + replace: false,
  36 + link: linker,
  37 + scope: false
  38 + };
  39 +}
ui/src/app/alarm/alarm-header.tpl.html renamed from ui/src/app/event/event-header-alarm.tpl.html
@@ -15,6 +15,9 @@ @@ -15,6 +15,9 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div translate class="tb-cell" flex="30">event.event-time</div>  
19 -<div translate class="tb-cell" flex="20">event.server</div>  
20 -<div translate class="tb-cell" flex="20">event.alarm</div> 18 +<div translate class="tb-cell" flex="30">alarm.created-time</div>
  19 +<div translate class="tb-cell" flex="15">alarm.originator</div>
  20 +<div translate class="tb-cell" flex="20">alarm.type</div>
  21 +<div translate class="tb-cell" flex="15">alarm.severity</div>
  22 +<div translate class="tb-cell" flex="20">alarm.status</div>
  23 +<div translate class="tb-cell" flex="15">alarm.details</div>
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +/* eslint-disable import/no-unresolved, import/default */
  17 +
  18 +import alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html';
  19 +
  20 +import alarmRowTemplate from './alarm-row.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
  24 +/*@ngInject*/
  25 +export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) {
  26 +
  27 + var linker = function (scope, element, attrs) {
  28 +
  29 + var template = $templateCache.get(alarmRowTemplate);
  30 + element.html(template);
  31 +
  32 + scope.alarm = attrs.alarm;
  33 + scope.types = types;
  34 +
  35 + scope.showAlarmDetails = function($event) {
  36 + var onShowingCallback = {
  37 + onShowing: function(){}
  38 + }
  39 + $mdDialog.show({
  40 + controller: 'AlarmDetailsDialogController',
  41 + controllerAs: 'vm',
  42 + templateUrl: alarmDetailsDialogTemplate,
  43 + locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback},
  44 + parent: angular.element($document[0].body),
  45 + targetEvent: $event,
  46 + fullscreen: true,
  47 + skipHide: true,
  48 + onShowing: function(scope, element) {
  49 + onShowingCallback.onShowing(scope, element);
  50 + }
  51 + }).then(function (alarm) {
  52 + if (alarm) {
  53 + scope.alarm = alarm;
  54 + }
  55 + });
  56 + }
  57 +
  58 + $compile(element.contents())(scope);
  59 + }
  60 +
  61 + return {
  62 + restrict: "A",
  63 + replace: false,
  64 + link: linker,
  65 + scope: false
  66 + };
  67 +}
ui/src/app/alarm/alarm-row.tpl.html renamed from ui/src/app/event/event-row-alarm.tpl.html
@@ -15,14 +15,19 @@ @@ -15,14 +15,19 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>  
19 -<div class="tb-cell" flex="20">{{event.body.server}}</div>  
20 -<div class="tb-cell" flex="20">  
21 - <md-button ng-if="event.body.body" class="md-icon-button md-primary"  
22 - ng-click="showContent($event, event.body.body, 'event.alarm')" 18 +<div class="tb-cell" flex="30">{{alarm.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
  19 +<div class="tb-cell" flex="15">{{alarm.originatorName}}</div>
  20 +<div class="tb-cell" flex="20">{{alarm.type}}</div>
  21 +<div class="tb-cell tb-severity" flex="15" ng-class="types.alarmSeverity[alarm.severity].class">
  22 + {{ alarm ? (types.alarmSeverity[alarm.severity].name | translate) : '' }}
  23 +</div>
  24 +<div class="tb-cell" flex="20">{{ alarm ? (('alarm.display-status.' + alarm.status) | translate) : '' }}</div>
  25 +<div class="tb-cell" flex="15">
  26 + <md-button class="md-icon-button md-primary"
  27 + ng-click="showAlarmDetails($event)"
23 aria-label="{{ 'action.view' | translate }}"> 28 aria-label="{{ 'action.view' | translate }}">
24 <md-tooltip md-direction="top"> 29 <md-tooltip md-direction="top">
25 - {{ 'action.view' | translate }} 30 + {{ 'alarm.details' | translate }}
26 </md-tooltip> 31 </md-tooltip>
27 <md-icon aria-label="{{ 'action.view' | translate }}" 32 <md-icon aria-label="{{ 'action.view' | translate }}"
28 class="material-icons"> 33 class="material-icons">
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +import './alarm.scss';
  17 +
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import alarmTableTemplate from './alarm-table.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
  24 +/*@ngInject*/
  25 +export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) {
  26 +
  27 + var linker = function (scope, element) {
  28 +
  29 + var template = $templateCache.get(alarmTableTemplate);
  30 +
  31 + element.html(template);
  32 +
  33 + scope.types = types;
  34 +
  35 + scope.alarmSearchStatus = types.alarmSearchStatus.any;
  36 +
  37 + var pageSize = 20;
  38 + var startTime = 0;
  39 + var endTime = 0;
  40 +
  41 + scope.timewindow = {
  42 + history: {
  43 + timewindowMs: 24 * 60 * 60 * 1000 // 1 day
  44 + }
  45 + }
  46 +
  47 + scope.topIndex = 0;
  48 +
  49 + scope.theAlarms = {
  50 + getItemAtIndex: function (index) {
  51 + if (index > scope.alarms.data.length) {
  52 + scope.theAlarms.fetchMoreItems_(index);
  53 + return null;
  54 + }
  55 + var item = scope.alarms.data[index];
  56 + if (item) {
  57 + item.indexNumber = index + 1;
  58 + }
  59 + return item;
  60 + },
  61 +
  62 + getLength: function () {
  63 + if (scope.alarms.hasNext) {
  64 + return scope.alarms.data.length + scope.alarms.nextPageLink.limit;
  65 + } else {
  66 + return scope.alarms.data.length;
  67 + }
  68 + },
  69 +
  70 + fetchMoreItems_: function () {
  71 + if (scope.alarms.hasNext && !scope.alarms.pending) {
  72 + if (scope.entityType && scope.entityId && scope.alarmSearchStatus) {
  73 + var promise = alarmService.getAlarms(scope.entityType, scope.entityId,
  74 + scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false);
  75 + if (promise) {
  76 + scope.alarms.pending = true;
  77 + promise.then(
  78 + function success(alarms) {
  79 + scope.alarms.data = scope.alarms.data.concat(alarms.data);
  80 + scope.alarms.nextPageLink = alarms.nextPageLink;
  81 + scope.alarms.hasNext = alarms.hasNext;
  82 + if (scope.alarms.hasNext) {
  83 + scope.alarms.nextPageLink.limit = pageSize;
  84 + }
  85 + scope.alarms.pending = false;
  86 + },
  87 + function fail() {
  88 + scope.alarms.hasNext = false;
  89 + scope.alarms.pending = false;
  90 + });
  91 + } else {
  92 + scope.alarms.hasNext = false;
  93 + }
  94 + } else {
  95 + scope.alarms.hasNext = false;
  96 + }
  97 + }
  98 + }
  99 + };
  100 +
  101 + scope.$watch("entityId", function(newVal, prevVal) {
  102 + if (newVal && !angular.equals(newVal, prevVal)) {
  103 + resetFilter();
  104 + reload();
  105 + }
  106 + });
  107 +
  108 +
  109 +
  110 + function destroyWatchers() {
  111 + if (scope.alarmSearchStatusWatchHandle) {
  112 + scope.alarmSearchStatusWatchHandle();
  113 + scope.alarmSearchStatusWatchHandle = null;
  114 + }
  115 + if (scope.timewindowWatchHandle) {
  116 + scope.timewindowWatchHandle();
  117 + scope.timewindowWatchHandle = null;
  118 + }
  119 + }
  120 +
  121 + function initWatchers() {
  122 + scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) {
  123 + if (newVal && !angular.equals(newVal, prevVal)) {
  124 + reload();
  125 + }
  126 + });
  127 + scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) {
  128 + if (newVal && !angular.equals(newVal, prevVal)) {
  129 + reload();
  130 + }
  131 + }, true);
  132 + }
  133 +
  134 + function resetFilter() {
  135 + destroyWatchers();
  136 + scope.timewindow = {
  137 + history: {
  138 + timewindowMs: 24 * 60 * 60 * 1000 // 1 day
  139 + }
  140 + };
  141 + scope.alarmSearchStatus = types.alarmSearchStatus.any;
  142 + initWatchers();
  143 + }
  144 +
  145 + function updateTimeWindowRange () {
  146 + if (scope.timewindow.history.timewindowMs) {
  147 + var currentTime = (new Date).getTime();
  148 + startTime = currentTime - scope.timewindow.history.timewindowMs;
  149 + endTime = currentTime;
  150 + } else {
  151 + startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
  152 + endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
  153 + }
  154 + }
  155 +
  156 + function reload () {
  157 + scope.topIndex = 0;
  158 + scope.selected = [];
  159 + updateTimeWindowRange();
  160 + scope.alarms = {
  161 + data: [],
  162 + nextPageLink: {
  163 + limit: pageSize,
  164 + startTime: startTime,
  165 + endTime: endTime
  166 + },
  167 + hasNext: true,
  168 + pending: false
  169 + };
  170 + scope.theAlarms.getItemAtIndex(pageSize);
  171 + }
  172 +
  173 + scope.noData = function() {
  174 + return scope.alarms.data.length == 0 && !scope.alarms.hasNext;
  175 + }
  176 +
  177 + scope.hasData = function() {
  178 + return scope.alarms.data.length > 0;
  179 + }
  180 +
  181 + scope.loading = function() {
  182 + return $rootScope.loading;
  183 + }
  184 +
  185 + scope.hasScroll = function() {
  186 + var repeatContainer = scope.repeatContainer[0];
  187 + if (repeatContainer) {
  188 + var scrollElement = repeatContainer.children[0];
  189 + if (scrollElement) {
  190 + return scrollElement.scrollHeight > scrollElement.clientHeight;
  191 + }
  192 + }
  193 + return false;
  194 + }
  195 +
  196 + reload();
  197 +
  198 + initWatchers();
  199 +
  200 + $compile(element.contents())(scope);
  201 + }
  202 +
  203 + return {
  204 + restrict: "E",
  205 + link: linker,
  206 + scope: {
  207 + entityType: '=',
  208 + entityId: '='
  209 + }
  210 + };
  211 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2017 The Thingsboard Authors
  4 +
  5 + Licensed under the Apache License, Version 2.0 (the "License");
  6 + you may not use this file except in compliance with the License.
  7 + You may obtain a copy of the License at
  8 +
  9 + http://www.apache.org/licenses/LICENSE-2.0
  10 +
  11 + Unless required by applicable law or agreed to in writing, software
  12 + distributed under the License is distributed on an "AS IS" BASIS,
  13 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 + See the License for the specific language governing permissions and
  15 + limitations under the License.
  16 +
  17 +-->
  18 +<md-content flex class="md-padding tb-absolute-fill" layout="column">
  19 + <section layout="row">
  20 + <md-input-container class="md-block" style="width: 200px;">
  21 + <label translate>alarm.alarm-status</label>
  22 + <md-select ng-model="alarmSearchStatus" ng-disabled="loading()">
  23 + <md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
  24 + {{ ('alarm.search-status.' + searchStatus) | translate }}
  25 + </md-option>
  26 + </md-select>
  27 + </md-input-container>
  28 + <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
  29 + </section>
  30 + <div flex layout="column" class="tb-alarm-container md-whiteframe-z1">
  31 + <md-list flex layout="column" class="tb-alarm-table">
  32 + <md-list class="tb-row tb-header" layout="row" tb-alarm-header>
  33 + </md-list>
  34 + <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
  35 + ng-show="loading()"></md-progress-linear>
  36 + <md-divider></md-divider>
  37 + <span translate layout-align="center center"
  38 + style="margin-top: 25px;"
  39 + class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span>
  40 + <md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer">
  41 + <md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}">
  42 + <md-list class="tb-row" flex layout="row" tb-alarm-row alarm="{{alarm}}">
  43 + </md-list>
  44 + <md-divider flex></md-divider>
  45 + </md-list-item>
  46 + </md-virtual-repeat-container>
  47 + </md-list>
  48 + </div>
  49 +</md-content>
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +.tb-alarm-container {
  18 + overflow-x: auto;
  19 +}
  20 +
  21 +md-list.tb-alarm-table {
  22 + padding: 0px;
  23 + min-width: 700px;
  24 +
  25 + md-list-item {
  26 + padding: 0px;
  27 + }
  28 +
  29 + .tb-row {
  30 + height: 48px;
  31 + padding: 0px;
  32 + overflow: hidden;
  33 + }
  34 +
  35 + .tb-row:hover {
  36 + background-color: #EEEEEE;
  37 + }
  38 +
  39 + .tb-header:hover {
  40 + background: none;
  41 + }
  42 +
  43 + .tb-header {
  44 + .tb-cell {
  45 + color: rgba(0,0,0,.54);
  46 + font-size: 12px;
  47 + font-weight: 700;
  48 + white-space: nowrap;
  49 + background: none;
  50 + }
  51 + }
  52 +
  53 + .tb-cell {
  54 + padding: 0 24px;
  55 + margin: auto 0;
  56 + color: rgba(0,0,0,.87);
  57 + font-size: 13px;
  58 + vertical-align: middle;
  59 + text-align: left;
  60 + overflow: hidden;
  61 + .md-button {
  62 + padding: 0;
  63 + margin: 0;
  64 + }
  65 + }
  66 +
  67 + .tb-cell.tb-number {
  68 + text-align: right;
  69 + }
  70 +
  71 +}
  72 +
  73 +#tb-alarm-content {
  74 + min-width: 400px;
  75 + min-height: 50px;
  76 + width: 100%;
  77 + height: 100%;
  78 +}
  1 +/*
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +import AlarmDetailsDialogController from './alarm-details-dialog.controller';
  18 +import AlarmHeaderDirective from './alarm-header.directive';
  19 +import AlarmRowDirective from './alarm-row.directive';
  20 +import AlarmTableDirective from './alarm-table.directive';
  21 +
  22 +export default angular.module('thingsboard.alarm', [])
  23 + .controller('AlarmDetailsDialogController', AlarmDetailsDialogController)
  24 + .directive('tbAlarmHeader', AlarmHeaderDirective)
  25 + .directive('tbAlarmRow', AlarmRowDirective)
  26 + .directive('tbAlarmTable', AlarmTableDirective)
  27 + .name;
@@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', []) @@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', [])
21 function AlarmService($http, $q, $interval, $filter) { 21 function AlarmService($http, $q, $interval, $filter) {
22 var service = { 22 var service = {
23 getAlarm: getAlarm, 23 getAlarm: getAlarm,
  24 + getAlarmInfo: getAlarmInfo,
24 saveAlarm: saveAlarm, 25 saveAlarm: saveAlarm,
25 ackAlarm: ackAlarm, 26 ackAlarm: ackAlarm,
26 clearAlarm: clearAlarm, 27 clearAlarm: clearAlarm,
@@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) { @@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) {
46 return deferred.promise; 47 return deferred.promise;
47 } 48 }
48 49
  50 + function getAlarmInfo(alarmId, ignoreErrors, config) {
  51 + var deferred = $q.defer();
  52 + var url = '/api/alarm/info/' + alarmId;
  53 + if (!config) {
  54 + config = {};
  55 + }
  56 + config = Object.assign(config, { ignoreErrors: ignoreErrors });
  57 + $http.get(url, config).then(function success(response) {
  58 + deferred.resolve(response.data);
  59 + }, function fail() {
  60 + deferred.reject();
  61 + });
  62 + return deferred.promise;
  63 + }
  64 +
49 function saveAlarm(alarm, ignoreErrors, config) { 65 function saveAlarm(alarm, ignoreErrors, config) {
50 var deferred = $q.defer(); 66 var deferred = $q.defer();
51 var url = '/api/alarm'; 67 var url = '/api/alarm';
@@ -48,11 +48,16 @@ @@ -48,11 +48,16 @@
48 disable-attribute-scope-selection="true"> 48 disable-attribute-scope-selection="true">
49 </tb-attribute-table> 49 </tb-attribute-table>
50 </md-tab> 50 </md-tab>
  51 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
  52 + <tb-alarm-table flex entity-type="vm.types.entityType.asset"
  53 + entity-id="vm.grid.operatingItem().id.id">
  54 + </tb-alarm-table>
  55 + </md-tab>
51 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}"> 56 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}">
52 <tb-event-table flex entity-type="vm.types.entityType.asset" 57 <tb-event-table flex entity-type="vm.types.entityType.asset"
53 entity-id="vm.grid.operatingItem().id.id" 58 entity-id="vm.grid.operatingItem().id.id"
54 tenant-id="vm.grid.operatingItem().tenantId.id" 59 tenant-id="vm.grid.operatingItem().tenantId.id"
55 - default-event-type="{{vm.types.eventType.alarm.value}}"> 60 + default-event-type="{{vm.types.eventType.error.value}}">
56 </tb-event-table> 61 </tb-event-table>
57 </md-tab> 62 </md-tab>
58 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> 63 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 */ 15 */
16 import uiRouter from 'angular-ui-router'; 16 import uiRouter from 'angular-ui-router';
17 import thingsboardGrid from '../components/grid.directive'; 17 import thingsboardGrid from '../components/grid.directive';
18 -import thingsboardEvent from '../event';  
19 import thingsboardApiUser from '../api/user.service'; 18 import thingsboardApiUser from '../api/user.service';
20 import thingsboardApiAsset from '../api/asset.service'; 19 import thingsboardApiAsset from '../api/asset.service';
21 import thingsboardApiCustomer from '../api/customer.service'; 20 import thingsboardApiCustomer from '../api/customer.service';
@@ -29,7 +28,6 @@ import AssetDirective from './asset.directive'; @@ -29,7 +28,6 @@ import AssetDirective from './asset.directive';
29 export default angular.module('thingsboard.asset', [ 28 export default angular.module('thingsboard.asset', [
30 uiRouter, 29 uiRouter,
31 thingsboardGrid, 30 thingsboardGrid,
32 - thingsboardEvent,  
33 thingsboardApiUser, 31 thingsboardApiUser,
34 thingsboardApiAsset, 32 thingsboardApiAsset,
35 thingsboardApiCustomer 33 thingsboardApiCustomer
@@ -72,6 +72,28 @@ export default angular.module('thingsboard.types', []) @@ -72,6 +72,28 @@ export default angular.module('thingsboard.types', [])
72 ack: "ACK", 72 ack: "ACK",
73 unack: "UNACK" 73 unack: "UNACK"
74 }, 74 },
  75 + alarmSeverity: {
  76 + "CRITICAL": {
  77 + name: "alarm.severity-critical",
  78 + class: "tb-critical"
  79 + },
  80 + "MAJOR": {
  81 + name: "alarm.severity-major",
  82 + class: "tb-major"
  83 + },
  84 + "MINOR": {
  85 + name: "alarm.severity-minor",
  86 + class: "tb-minor"
  87 + },
  88 + "WARNING": {
  89 + name: "alarm.severity-warning",
  90 + class: "tb-warning"
  91 + },
  92 + "INDETERMINATE": {
  93 + name: "alarm.severity-indeterminate",
  94 + class: "tb-indeterminate"
  95 + }
  96 + },
75 aliasFilterType: { 97 aliasFilterType: {
76 entityList: { 98 entityList: {
77 value: 'entityList', 99 value: 'entityList',
@@ -215,10 +237,6 @@ export default angular.module('thingsboard.types', []) @@ -215,10 +237,6 @@ export default angular.module('thingsboard.types', [])
215 manages: "Manages" 237 manages: "Manages"
216 }, 238 },
217 eventType: { 239 eventType: {
218 - alarm: {  
219 - value: "ALARM",  
220 - name: "event.type-alarm"  
221 - },  
222 error: { 240 error: {
223 value: "ERROR", 241 value: "ERROR",
224 name: "event.type-error" 242 name: "event.type-error"
@@ -48,11 +48,16 @@ @@ -48,11 +48,16 @@
48 disable-attribute-scope-selection="true"> 48 disable-attribute-scope-selection="true">
49 </tb-attribute-table> 49 </tb-attribute-table>
50 </md-tab> 50 </md-tab>
  51 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
  52 + <tb-alarm-table flex entity-type="vm.types.entityType.customer"
  53 + entity-id="vm.grid.operatingItem().id.id">
  54 + </tb-alarm-table>
  55 + </md-tab>
51 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}"> 56 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}">
52 <tb-event-table flex entity-type="vm.types.entityType.customer" 57 <tb-event-table flex entity-type="vm.types.entityType.customer"
53 entity-id="vm.grid.operatingItem().id.id" 58 entity-id="vm.grid.operatingItem().id.id"
54 tenant-id="vm.grid.operatingItem().tenantId.id" 59 tenant-id="vm.grid.operatingItem().tenantId.id"
55 - default-event-type="{{vm.types.eventType.alarm.value}}"> 60 + default-event-type="{{vm.types.eventType.error.value}}">
56 </tb-event-table> 61 </tb-event-table>
57 </md-tab> 62 </md-tab>
58 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> 63 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
@@ -49,11 +49,16 @@ @@ -49,11 +49,16 @@
49 disable-attribute-scope-selection="true"> 49 disable-attribute-scope-selection="true">
50 </tb-attribute-table> 50 </tb-attribute-table>
51 </md-tab> 51 </md-tab>
  52 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
  53 + <tb-alarm-table flex entity-type="vm.types.entityType.device"
  54 + entity-id="vm.grid.operatingItem().id.id">
  55 + </tb-alarm-table>
  56 + </md-tab>
52 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}"> 57 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}">
53 <tb-event-table flex entity-type="vm.types.entityType.device" 58 <tb-event-table flex entity-type="vm.types.entityType.device"
54 entity-id="vm.grid.operatingItem().id.id" 59 entity-id="vm.grid.operatingItem().id.id"
55 tenant-id="vm.grid.operatingItem().tenantId.id" 60 tenant-id="vm.grid.operatingItem().tenantId.id"
56 - default-event-type="{{vm.types.eventType.alarm.value}}"> 61 + default-event-type="{{vm.types.eventType.error.value}}">
57 </tb-event-table> 62 </tb-event-table>
58 </md-tab> 63 </md-tab>
59 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> 64 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 */ 15 */
16 import uiRouter from 'angular-ui-router'; 16 import uiRouter from 'angular-ui-router';
17 import thingsboardGrid from '../components/grid.directive'; 17 import thingsboardGrid from '../components/grid.directive';
18 -import thingsboardEvent from '../event';  
19 import thingsboardApiUser from '../api/user.service'; 18 import thingsboardApiUser from '../api/user.service';
20 import thingsboardApiDevice from '../api/device.service'; 19 import thingsboardApiDevice from '../api/device.service';
21 import thingsboardApiCustomer from '../api/customer.service'; 20 import thingsboardApiCustomer from '../api/customer.service';
@@ -30,7 +29,6 @@ import DeviceDirective from './device.directive'; @@ -30,7 +29,6 @@ import DeviceDirective from './device.directive';
30 export default angular.module('thingsboard.device', [ 29 export default angular.module('thingsboard.device', [
31 uiRouter, 30 uiRouter,
32 thingsboardGrid, 31 thingsboardGrid,
33 - thingsboardEvent,  
34 thingsboardApiUser, 32 thingsboardApiUser,
35 thingsboardApiDevice, 33 thingsboardApiDevice,
36 thingsboardApiCustomer 34 thingsboardApiCustomer
@@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title, @@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title,
62 } 62 }
63 newWidth = 8 * maxLineLength + 16; 63 newWidth = 8 * maxLineLength + 16;
64 } 64 }
65 - $('#tb-content', element).height(newHeight.toString() + "px") 65 + $('#tb-event-content', element).height(newHeight.toString() + "px")
66 .width(newWidth.toString() + "px"); 66 .width(newWidth.toString() + "px");
67 vm.editor.resize(); 67 vm.editor.resize();
68 } 68 }
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 </md-toolbar> 27 </md-toolbar>
28 <md-dialog-content> 28 <md-dialog-content>
29 <div class="md-dialog-content"> 29 <div class="md-dialog-content">
30 - <div flex id="tb-content" readonly 30 + <div flex id="tb-event-content" readonly
31 ui-ace="vm.contentOptions" 31 ui-ace="vm.contentOptions"
32 ng-model="vm.content"> 32 ng-model="vm.content">
33 </div> 33 </div>
@@ -18,7 +18,6 @@ @@ -18,7 +18,6 @@
18 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html'; 18 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
19 import eventHeaderStatsTemplate from './event-header-stats.tpl.html'; 19 import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
20 import eventHeaderErrorTemplate from './event-header-error.tpl.html'; 20 import eventHeaderErrorTemplate from './event-header-error.tpl.html';
21 -import eventHeaderAlarmTemplate from './event-header-alarm.tpl.html';  
22 21
23 /* eslint-enable import/no-unresolved, import/default */ 22 /* eslint-enable import/no-unresolved, import/default */
24 23
@@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) { @@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
39 case types.eventType.error.value: 38 case types.eventType.error.value:
40 template = eventHeaderErrorTemplate; 39 template = eventHeaderErrorTemplate;
41 break; 40 break;
42 - case types.eventType.alarm.value:  
43 - template = eventHeaderAlarmTemplate;  
44 - break;  
45 } 41 }
46 return $templateCache.get(template); 42 return $templateCache.get(template);
47 } 43 }
@@ -20,7 +20,6 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html'; @@ -20,7 +20,6 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
20 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html'; 20 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
21 import eventRowStatsTemplate from './event-row-stats.tpl.html'; 21 import eventRowStatsTemplate from './event-row-stats.tpl.html';
22 import eventRowErrorTemplate from './event-row-error.tpl.html'; 22 import eventRowErrorTemplate from './event-row-error.tpl.html';
23 -import eventRowAlarmTemplate from './event-row-alarm.tpl.html';  
24 23
25 /* eslint-enable import/no-unresolved, import/default */ 24 /* eslint-enable import/no-unresolved, import/default */
26 25
@@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ @@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
41 case types.eventType.error.value: 40 case types.eventType.error.value:
42 template = eventRowErrorTemplate; 41 template = eventRowErrorTemplate;
43 break; 42 break;
44 - case types.eventType.alarm.value:  
45 - template = eventRowAlarmTemplate;  
46 - break;  
47 } 43 }
48 return $templateCache.get(template); 44 return $templateCache.get(template);
49 } 45 }
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 </md-input-container> 27 </md-input-container>
28 <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow> 28 <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
29 </section> 29 </section>
30 - <md-list flex layout="column" class="md-whiteframe-z1 tb-table"> 30 + <md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
31 <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}"> 31 <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
32 </md-list> 32 </md-list>
33 <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()" 33 <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -md-list.tb-table { 16 +md-list.tb-event-table {
17 padding: 0px; 17 padding: 0px;
18 18
19 md-list-item { 19 md-list-item {
@@ -64,7 +64,7 @@ md-list.tb-table { @@ -64,7 +64,7 @@ md-list.tb-table {
64 64
65 } 65 }
66 66
67 -#tb-content { 67 +#tb-event-content {
68 min-width: 400px; 68 min-width: 400px;
69 min-height: 50px; 69 min-height: 50px;
70 width: 100%; 70 width: 100%;
@@ -32,6 +32,8 @@ import thingsboardDashboardAutocomplete from '../components/dashboard-autocomple @@ -32,6 +32,8 @@ import thingsboardDashboardAutocomplete from '../components/dashboard-autocomple
32 import thingsboardUserMenu from './user-menu.directive'; 32 import thingsboardUserMenu from './user-menu.directive';
33 33
34 import thingsboardEntity from '../entity'; 34 import thingsboardEntity from '../entity';
  35 +import thingsboardEvent from '../event';
  36 +import thingsboardAlarm from '../alarm';
35 import thingsboardTenant from '../tenant'; 37 import thingsboardTenant from '../tenant';
36 import thingsboardCustomer from '../customer'; 38 import thingsboardCustomer from '../customer';
37 import thingsboardUser from '../user'; 39 import thingsboardUser from '../user';
@@ -61,6 +63,8 @@ export default angular.module('thingsboard.home', [ @@ -61,6 +63,8 @@ export default angular.module('thingsboard.home', [
61 thingsboardHomeLinks, 63 thingsboardHomeLinks,
62 thingsboardUserMenu, 64 thingsboardUserMenu,
63 thingsboardEntity, 65 thingsboardEntity,
  66 + thingsboardEvent,
  67 + thingsboardAlarm,
64 thingsboardTenant, 68 thingsboardTenant,
65 thingsboardCustomer, 69 thingsboardCustomer,
66 thingsboardUser, 70 thingsboardUser,
@@ -411,7 +411,6 @@ @@ -411,7 +411,6 @@
411 }, 411 },
412 "event": { 412 "event": {
413 "event-type": "Tipo de evento", 413 "event-type": "Tipo de evento",
414 - "type-alarm": "Alarma",  
415 "type-error": "Error", 414 "type-error": "Error",
416 "type-lc-event": "Ciclo de vida", 415 "type-lc-event": "Ciclo de vida",
417 "type-stats": "Estadísticas", 416 "type-stats": "Estadísticas",
@@ -378,7 +378,6 @@ export default function addLocaleKorean(locales) { @@ -378,7 +378,6 @@ export default function addLocaleKorean(locales) {
378 }, 378 },
379 "event": { 379 "event": {
380 "event-type": "이벤트 타입", 380 "event-type": "이벤트 타입",
381 - "type-alarm": "알람",  
382 "type-error": "에러", 381 "type-error": "에러",
383 "type-lc-event": "주기적 이벤트", 382 "type-lc-event": "주기적 이벤트",
384 "type-stats": "통계", 383 "type-stats": "통계",
@@ -411,7 +411,6 @@ export default function addLocaleRussian(locales) { @@ -411,7 +411,6 @@ export default function addLocaleRussian(locales) {
411 }, 411 },
412 "event": { 412 "event": {
413 "event-type": "Тип события", 413 "event-type": "Тип события",
414 - "type-alarm": "Аварийное оповещение",  
415 "type-error": "Ошибка", 414 "type-error": "Ошибка",
416 "type-lc-event": "Событие жизненного цикла", 415 "type-lc-event": "Событие жизненного цикла",
417 "type-stats": "Статистика", 416 "type-stats": "Статистика",
@@ -411,7 +411,6 @@ export default function addLocaleChinese(locales) { @@ -411,7 +411,6 @@ export default function addLocaleChinese(locales) {
411 }, 411 },
412 "event" : { 412 "event" : {
413 "event-type": "事件类型", 413 "event-type": "事件类型",
414 - "type-alarm": "报警",  
415 "type-error": "错误", 414 "type-error": "错误",
416 "type-lc-event": "生命周期事件", 415 "type-lc-event": "生命周期事件",
417 "type-stats": "类型统计", 416 "type-stats": "类型统计",
@@ -108,9 +108,43 @@ export default angular.module('thingsboard.locale', []) @@ -108,9 +108,43 @@ export default angular.module('thingsboard.locale', [])
108 }, 108 },
109 "alarm": { 109 "alarm": {
110 "alarm": "Alarm", 110 "alarm": "Alarm",
  111 + "alarms": "Alarms",
111 "select-alarm": "Select alarm", 112 "select-alarm": "Select alarm",
112 "no-alarms-matching": "No alarms matching '{{entity}}' were found.", 113 "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
113 - "alarm-required": "Alarm is required" 114 + "alarm-required": "Alarm is required",
  115 + "alarm-status": "Alarm status",
  116 + "search-status": {
  117 + "ANY": "Any",
  118 + "ACTIVE": "Active",
  119 + "CLEARED": "Cleared",
  120 + "ACK": "Acknowledged",
  121 + "UNACK": "Unacknowledged"
  122 + },
  123 + "display-status": {
  124 + "ACTIVE_UNACK": "Active Unacknowledged",
  125 + "ACTIVE_ACK": "Active Acknowledged",
  126 + "CLEARED_UNACK": "Cleared Unacknowledged",
  127 + "CLEARED_ACK": "Cleared Acknowledged"
  128 + },
  129 + "no-alarms-prompt": "No alarms found",
  130 + "created-time": "Created time",
  131 + "type": "Type",
  132 + "severity": "Severity",
  133 + "originator": "Originator",
  134 + "details": "Details",
  135 + "status": "Status",
  136 + "alarm-details": "Alarm details",
  137 + "start-time": "Start time",
  138 + "end-time": "End time",
  139 + "ack-time": "Acknowledged time",
  140 + "clear-time": "Cleared time",
  141 + "severity-critical": "Critical",
  142 + "severity-major": "Major",
  143 + "severity-minor": "Minor",
  144 + "severity-warning": "Warning",
  145 + "severity-indeterminate": "Indeterminate",
  146 + "acknowledge": "Acknowledge",
  147 + "clear": "Clear"
114 }, 148 },
115 "alias": { 149 "alias": {
116 "add": "Add alias", 150 "add": "Add alias",
@@ -647,7 +681,6 @@ export default angular.module('thingsboard.locale', []) @@ -647,7 +681,6 @@ export default angular.module('thingsboard.locale', [])
647 }, 681 },
648 "event": { 682 "event": {
649 "event-type": "Event type", 683 "event-type": "Event type",
650 - "type-alarm": "Alarm",  
651 "type-error": "Error", 684 "type-error": "Error",
652 "type-lc-event": "Lifecycle event", 685 "type-lc-event": "Lifecycle event",
653 "type-stats": "Statistics", 686 "type-stats": "Statistics",
@@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
16 import uiRouter from 'angular-ui-router'; 16 import uiRouter from 'angular-ui-router';
17 import thingsboardGrid from '../components/grid.directive'; 17 import thingsboardGrid from '../components/grid.directive';
18 import thingsboardJsonForm from '../components/json-form.directive'; 18 import thingsboardJsonForm from '../components/json-form.directive';
19 -import thingsboardEvent from '../event';  
20 import thingsboardApiPlugin from '../api/plugin.service'; 19 import thingsboardApiPlugin from '../api/plugin.service';
21 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service'; 20 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
22 21
@@ -28,7 +27,6 @@ export default angular.module('thingsboard.plugin', [ @@ -28,7 +27,6 @@ export default angular.module('thingsboard.plugin', [
28 uiRouter, 27 uiRouter,
29 thingsboardGrid, 28 thingsboardGrid,
30 thingsboardJsonForm, 29 thingsboardJsonForm,
31 - thingsboardEvent,  
32 thingsboardApiPlugin, 30 thingsboardApiPlugin,
33 thingsboardApiComponentDescriptor 31 thingsboardApiComponentDescriptor
34 ]) 32 ])
@@ -48,12 +48,16 @@ @@ -48,12 +48,16 @@
48 disable-attribute-scope-selection="true"> 48 disable-attribute-scope-selection="true">
49 </tb-attribute-table> 49 </tb-attribute-table>
50 </md-tab> 50 </md-tab>
  51 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
  52 + <tb-alarm-table flex entity-type="vm.types.entityType.plugin"
  53 + entity-id="vm.grid.operatingItem().id.id">
  54 + </tb-alarm-table>
  55 + </md-tab>
51 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}"> 56 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}">
52 <tb-event-table flex entity-type="vm.types.entityType.plugin" 57 <tb-event-table flex entity-type="vm.types.entityType.plugin"
53 entity-id="vm.grid.operatingItem().id.id" 58 entity-id="vm.grid.operatingItem().id.id"
54 tenant-id="vm.grid.operatingItem().tenantId.id" 59 tenant-id="vm.grid.operatingItem().tenantId.id"
55 - default-event-type="{{vm.types.eventType.lcEvent.value}}"  
56 - disabled-event-types="{{vm.types.eventType.alarm.value}}"> 60 + default-event-type="{{vm.types.eventType.lcEvent.value}}">
57 </tb-event-table> 61 </tb-event-table>
58 </md-tab> 62 </md-tab>
59 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}"> 63 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
@@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router'; @@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router';
17 import thingsboardGrid from '../components/grid.directive'; 17 import thingsboardGrid from '../components/grid.directive';
18 import thingsboardPluginSelect from '../components/plugin-select.directive'; 18 import thingsboardPluginSelect from '../components/plugin-select.directive';
19 import thingsboardComponent from '../component'; 19 import thingsboardComponent from '../component';
20 -import thingsboardEvent from '../event';  
21 import thingsboardApiRule from '../api/rule.service'; 20 import thingsboardApiRule from '../api/rule.service';
22 import thingsboardApiPlugin from '../api/plugin.service'; 21 import thingsboardApiPlugin from '../api/plugin.service';
23 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service'; 22 import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [ @@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [
31 thingsboardGrid, 30 thingsboardGrid,
32 thingsboardPluginSelect, 31 thingsboardPluginSelect,
33 thingsboardComponent, 32 thingsboardComponent,
34 - thingsboardEvent,  
35 thingsboardApiRule, 33 thingsboardApiRule,
36 thingsboardApiPlugin, 34 thingsboardApiPlugin,
37 thingsboardApiComponentDescriptor 35 thingsboardApiComponentDescriptor
@@ -48,12 +48,16 @@ @@ -48,12 +48,16 @@
48 disable-attribute-scope-selection="true"> 48 disable-attribute-scope-selection="true">
49 </tb-attribute-table> 49 </tb-attribute-table>
50 </md-tab> 50 </md-tab>
  51 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
  52 + <tb-alarm-table flex entity-type="vm.types.entityType.rule"
  53 + entity-id="vm.grid.operatingItem().id.id">
  54 + </tb-alarm-table>
  55 + </md-tab>
51 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}"> 56 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}">
52 <tb-event-table flex entity-type="vm.types.entityType.rule" 57 <tb-event-table flex entity-type="vm.types.entityType.rule"
53 entity-id="vm.grid.operatingItem().id.id" 58 entity-id="vm.grid.operatingItem().id.id"
54 tenant-id="vm.grid.operatingItem().tenantId.id" 59 tenant-id="vm.grid.operatingItem().tenantId.id"
55 - default-event-type="{{vm.types.eventType.lcEvent.value}}"  
56 - disabled-event-types="{{vm.types.eventType.alarm.value}}"> 60 + default-event-type="{{vm.types.eventType.lcEvent.value}}">
57 </tb-event-table> 61 </tb-event-table>
58 </md-tab> 62 </md-tab>
59 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}"> 63 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">
@@ -46,11 +46,16 @@ @@ -46,11 +46,16 @@
46 disable-attribute-scope-selection="true"> 46 disable-attribute-scope-selection="true">
47 </tb-attribute-table> 47 </tb-attribute-table>
48 </md-tab> 48 </md-tab>
  49 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
  50 + <tb-alarm-table flex entity-type="vm.types.entityType.tenant"
  51 + entity-id="vm.grid.operatingItem().id.id">
  52 + </tb-alarm-table>
  53 + </md-tab>
49 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}"> 54 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}">
50 <tb-event-table flex entity-type="vm.types.entityType.tenant" 55 <tb-event-table flex entity-type="vm.types.entityType.tenant"
51 entity-id="vm.grid.operatingItem().id.id" 56 entity-id="vm.grid.operatingItem().id.id"
52 tenant-id="vm.types.id.nullUid" 57 tenant-id="vm.types.id.nullUid"
53 - default-event-type="{{vm.types.eventType.alarm.value}}"> 58 + default-event-type="{{vm.types.eventType.error.value}}">
54 </tb-event-table> 59 </tb-event-table>
55 </md-tab> 60 </md-tab>
56 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> 61 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
@@ -309,6 +309,24 @@ pre.tb-highlight { @@ -309,6 +309,24 @@ pre.tb-highlight {
309 } 309 }
310 } 310 }
311 311
  312 +.tb-severity {
  313 + font-weight: bold;
  314 + &.tb-critical {
  315 + color: red !important;
  316 + }
  317 + &.tb-major {
  318 + color: orange !important;
  319 + }
  320 + &.tb-minor {
  321 + color: #ffca3d !important;
  322 + }
  323 + &.tb-warning {
  324 + color: #abab00 !important;
  325 + }
  326 + &.tb-indeterminate {
  327 + color: green !important;
  328 + }
  329 +}
312 330
313 /*********************** 331 /***********************
314 * Flow 332 * Flow