Browse Source

TB-63: Add new 'Alarms' tab in entity details.

pull/167/head
Igor Kulikov 9 years ago
parent
commit
3e0ef11678
  1. 13
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  2. 12
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 1
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  4. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  5. 17
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  6. 134
      ui/src/app/alarm/alarm-details-dialog.controller.js
  7. 27
      ui/src/app/alarm/alarm-details-dialog.scss
  8. 107
      ui/src/app/alarm/alarm-details-dialog.tpl.html
  9. 39
      ui/src/app/alarm/alarm-header.directive.js
  10. 9
      ui/src/app/alarm/alarm-header.tpl.html
  11. 67
      ui/src/app/alarm/alarm-row.directive.js
  12. 17
      ui/src/app/alarm/alarm-row.tpl.html
  13. 211
      ui/src/app/alarm/alarm-table.directive.js
  14. 49
      ui/src/app/alarm/alarm-table.tpl.html
  15. 78
      ui/src/app/alarm/alarm.scss
  16. 27
      ui/src/app/alarm/index.js
  17. 16
      ui/src/app/api/alarm.service.js
  18. 7
      ui/src/app/asset/assets.tpl.html
  19. 2
      ui/src/app/asset/index.js
  20. 26
      ui/src/app/common/types.constant.js
  21. 7
      ui/src/app/customer/customers.tpl.html
  22. 7
      ui/src/app/device/devices.tpl.html
  23. 2
      ui/src/app/device/index.js
  24. 2
      ui/src/app/event/event-content-dialog.controller.js
  25. 2
      ui/src/app/event/event-content-dialog.tpl.html
  26. 4
      ui/src/app/event/event-header.directive.js
  27. 4
      ui/src/app/event/event-row.directive.js
  28. 2
      ui/src/app/event/event-table.tpl.html
  29. 4
      ui/src/app/event/event.scss
  30. 4
      ui/src/app/layout/index.js
  31. 1
      ui/src/app/locale/locale.constant-es.js
  32. 1
      ui/src/app/locale/locale.constant-ko.js
  33. 1
      ui/src/app/locale/locale.constant-ru.js
  34. 1
      ui/src/app/locale/locale.constant-zh.js
  35. 37
      ui/src/app/locale/locale.constant.js
  36. 2
      ui/src/app/plugin/index.js
  37. 8
      ui/src/app/plugin/plugins.tpl.html
  38. 2
      ui/src/app/rule/index.js
  39. 8
      ui/src/app/rule/rules.tpl.html
  40. 7
      ui/src/app/tenant/tenants.tpl.html
  41. 18
      ui/src/scss/main.scss

13
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -57,6 +57,19 @@ public class AlarmController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
@ResponseBody
public AlarmInfo getAlarmInfoById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
checkParameter("alarmId", strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmInfoId(alarmId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm", method = RequestMethod.POST) @RequestMapping(value = "/alarm", method = RequestMethod.POST)
@ResponseBody @ResponseBody

12
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId; import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
@ -351,6 +352,17 @@ public abstract class BaseController {
} }
} }
AlarmInfo checkAlarmInfoId(AlarmId alarmId) throws ThingsboardException {
try {
validateId(alarmId, "Incorrect alarmId " + alarmId);
AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(alarmId).get();
checkAlarm(alarmInfo);
return alarmInfo;
} catch (Exception e) {
throw handleException(e, false);
}
}
protected void checkAlarm(Alarm alarm) throws ThingsboardException { protected void checkAlarm(Alarm alarm) throws ThingsboardException {
checkNotNull(alarm); checkNotNull(alarm);
checkTenantId(alarm.getTenantId()); checkTenantId(alarm.getTenantId());

1
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -55,6 +55,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
public Alarm(Alarm alarm) { public Alarm(Alarm alarm) {
super(alarm.getId()); super(alarm.getId());
this.createdTime = alarm.getCreatedTime();
this.tenantId = alarm.getTenantId(); this.tenantId = alarm.getTenantId();
this.type = alarm.getType(); this.type = alarm.getType();
this.originator = alarm.getOriginator(); this.originator = alarm.getOriginator();

2
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -35,6 +35,8 @@ public interface AlarmService {
ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId); ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId);
ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query); ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
} }

17
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -198,6 +198,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
return alarmDao.findAlarmByIdAsync(alarmId.getId()); return alarmDao.findAlarmByIdAsync(alarmId.getId());
} }
@Override
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId);
return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()),
(AsyncFunction<Alarm, AlarmInfo>) alarm1 -> {
AlarmInfo alarmInfo = new AlarmInfo(alarm1);
return Futures.transform(
entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
originatorName -> {
alarmInfo.setOriginatorName(originatorName);
return alarmInfo;
}
);
});
}
@Override @Override
public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) { public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query); ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);

134
ui/src/app/alarm/alarm-details-dialog.controller.js

@ -0,0 +1,134 @@
/*
* Copyright © 2016-2017 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 'brace/ext/language_tools';
import 'brace/mode/json';
import 'brace/theme/github';
import beautify from 'js-beautify';
import './alarm-details-dialog.scss';
const js_beautify = beautify.js;
/*@ngInject*/
export default function AlarmDetailsDialogController($mdDialog, $filter, $translate, types, alarmService, alarmId, showingCallback) {
var vm = this;
vm.alarmId = alarmId;
vm.types = types;
vm.alarm = null;
vm.alarmUpdated = false;
showingCallback.onShowing = function(scope, element) {
updateEditorSize(element);
}
vm.alarmDetailsOptions = {
useWrapMode: false,
mode: 'json',
showGutter: false,
showPrintMargin: false,
theme: 'github',
advanced: {
enableSnippets: false,
enableBasicAutocompletion: false,
enableLiveAutocompletion: false
},
onLoad: function (_ace) {
vm.editor = _ace;
}
};
vm.close = close;
vm.acknowledge = acknowledge;
vm.clear = clear;
loadAlarm();
function updateEditorSize(element) {
var newWidth = 600;
var newHeight = 200;
angular.element('#tb-alarm-details', element).height(newHeight.toString() + "px")
.width(newWidth.toString() + "px");
vm.editor.resize();
}
function loadAlarm() {
alarmService.getAlarmInfo(vm.alarmId).then(
function success(alarm) {
vm.alarm = alarm;
loadAlarmFields();
},
function fail() {
vm.alarm = null;
}
);
}
function loadAlarmFields() {
vm.createdTime = $filter('date')(vm.alarm.createdTime, 'yyyy-MM-dd HH:mm:ss');
vm.startTime = null;
if (vm.alarm.startTs) {
vm.startTime = $filter('date')(vm.alarm.startTs, 'yyyy-MM-dd HH:mm:ss');
}
vm.endTime = null;
if (vm.alarm.endTs) {
vm.endTime = $filter('date')(vm.alarm.endTs, 'yyyy-MM-dd HH:mm:ss');
}
vm.ackTime = null;
if (vm.alarm.ackTs) {
vm.ackTime = $filter('date')(vm.alarm.ackTs, 'yyyy-MM-dd HH:mm:ss')
}
vm.clearTime = null;
if (vm.alarm.clearTs) {
vm.clearTime = $filter('date')(vm.alarm.clearTs, 'yyyy-MM-dd HH:mm:ss');
}
vm.alarmSeverity = $translate.instant(types.alarmSeverity[vm.alarm.severity].name);
vm.alarmStatus = $translate.instant('alarm.display-status.' + vm.alarm.status);
vm.alarmDetails = null;
if (vm.alarm.details) {
vm.alarmDetails = angular.toJson(vm.alarm.details);
vm.alarmDetails = js_beautify(vm.alarmDetails, {indent_size: 4});
}
}
function acknowledge () {
alarmService.ackAlarm(vm.alarmId).then(
function success() {
vm.alarmUpdated = true;
loadAlarm();
}
);
}
function clear () {
alarmService.clearAlarm(vm.alarmId).then(
function success() {
vm.alarmUpdated = true;
loadAlarm();
}
);
}
function close () {
$mdDialog.hide(vm.alarmUpdated ? vm.alarm : null);
}
}

27
ui/src/app/alarm/alarm-details-dialog.scss

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2017 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-alarm-details-panel {
margin-left: 15px;
border: 1px solid #C0C0C0;
height: 100%;
#tb-alarm-details {
min-width: 600px;
min-height: 200px;
width: 100%;
height: 100%;
}
}

107
ui/src/app/alarm/alarm-details-dialog.tpl.html

@ -0,0 +1,107 @@
<!--
Copyright © 2016-2017 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.
-->
<md-dialog aria-label="{{ 'alarm.alarm-details' | translate }}">
<md-toolbar>
<div class="md-toolbar-tools">
<h2 translate>alarm.alarm-details</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="vm.close()">
<ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<div class="md-dialog-content" layout="column">
<div layout="row">
<md-input-container class="md-block">
<label translate>alarm.created-time</label>
<input ng-model="vm.createdTime" readonly>
</md-input-container>
<md-input-container flex class="md-block">
<label translate>alarm.originator</label>
<input ng-model="vm.alarm.originatorName" readonly>
</md-input-container>
</div>
<div layout="row" ng-if="vm.startTime || vm.endTime">
<md-input-container ng-if="vm.startTime" flex class="md-block">
<label translate>alarm.start-time</label>
<input ng-model="vm.startTime" readonly>
</md-input-container>
<md-input-container ng-if="vm.endTime" flex class="md-block">
<label translate>alarm.end-time</label>
<input ng-model="vm.endTime" readonly>
</md-input-container>
<span flex ng-if="!vm.startTime || !vm.endTime"></span>
</div>
<div layout="row" ng-if="vm.ackTime || vm.clearTime">
<md-input-container ng-if="vm.ackTime" flex class="md-block">
<label translate>alarm.ack-time</label>
<input ng-model="vm.ackTime" readonly>
</md-input-container>
<md-input-container ng-if="vm.clearTime" flex class="md-block">
<label translate>alarm.clear-time</label>
<input ng-model="vm.clearTime" readonly>
</md-input-container>
<span flex ng-if="!vm.ackTime || !vm.clearTime"></span>
</div>
<div layout="row">
<md-input-container flex class="md-block">
<label translate>alarm.type</label>
<input ng-model="vm.alarm.type" readonly>
</md-input-container>
<md-input-container flex class="md-block">
<label translate>alarm.severity</label>
<input class="tb-severity" ng-class="vm.types.alarmSeverity[vm.alarm.severity].class"
ng-model="vm.alarmSeverity" readonly>
</md-input-container>
<md-input-container flex class="md-block">
<label translate>alarm.status</label>
<input ng-model="vm.alarmStatus" readonly>
</md-input-container>
</div>
<div class="md-caption" style="padding-left: 3px; padding-bottom: 10px; color: rgba(0,0,0,0.57);" translate>alarm.details</div>
<div flex class="tb-alarm-details-panel" layout="column">
<div flex id="tb-alarm-details" readonly
ui-ace="vm.alarmDetailsOptions"
ng-model="vm.alarmDetails">
</div>
</div>
</div>
</md-dialog-content>
<md-dialog-actions layout="row">
<md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeUnack ||
vm.alarm.status==vm.types.alarmStatus.clearedUnack"
class="md-raised md-primary"
ng-disabled="loading"
ng-click="vm.acknowledge()"
style="margin-right:20px;">{{ 'alarm.acknowledge' |
translate }}
</md-button>
<md-button ng-if="vm.alarm.status==vm.types.alarmStatus.activeAck ||
vm.alarm.status==vm.types.alarmStatus.activeUnack"
class="md-raised md-primary"
ng-disabled="loading"
ng-click="vm.clear()">{{ 'alarm.clear' |
translate }}
</md-button>
<span flex></span>
<md-button ng-disabled="loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' |
translate }}
</md-button>
</md-dialog-actions>
</md-dialog>

39
ui/src/app/alarm/alarm-header.directive.js

@ -0,0 +1,39 @@
/*
* Copyright © 2016-2017 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.
*/
/* eslint-disable import/no-unresolved, import/default */
import alarmHeaderTemplate from './alarm-header.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function AlarmHeaderDirective($compile, $templateCache) {
var linker = function (scope, element) {
var template = $templateCache.get(alarmHeaderTemplate);
element.html(template);
$compile(element.contents())(scope);
}
return {
restrict: "A",
replace: false,
link: linker,
scope: false
};
}

9
ui/src/app/event/event-header-alarm.tpl.html → ui/src/app/alarm/alarm-header.tpl.html

@ -15,6 +15,9 @@
limitations under the License. limitations under the License.
--> -->
<div translate class="tb-cell" flex="30">event.event-time</div> <div translate class="tb-cell" flex="30">alarm.created-time</div>
<div translate class="tb-cell" flex="20">event.server</div> <div translate class="tb-cell" flex="15">alarm.originator</div>
<div translate class="tb-cell" flex="20">event.alarm</div> <div translate class="tb-cell" flex="20">alarm.type</div>
<div translate class="tb-cell" flex="15">alarm.severity</div>
<div translate class="tb-cell" flex="20">alarm.status</div>
<div translate class="tb-cell" flex="15">alarm.details</div>

67
ui/src/app/alarm/alarm-row.directive.js

@ -0,0 +1,67 @@
/*
* Copyright © 2016-2017 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.
*/
/* eslint-disable import/no-unresolved, import/default */
import alarmDetailsDialogTemplate from './alarm-details-dialog.tpl.html';
import alarmRowTemplate from './alarm-row.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function AlarmRowDirective($compile, $templateCache, types, $mdDialog, $document) {
var linker = function (scope, element, attrs) {
var template = $templateCache.get(alarmRowTemplate);
element.html(template);
scope.alarm = attrs.alarm;
scope.types = types;
scope.showAlarmDetails = function($event) {
var onShowingCallback = {
onShowing: function(){}
}
$mdDialog.show({
controller: 'AlarmDetailsDialogController',
controllerAs: 'vm',
templateUrl: alarmDetailsDialogTemplate,
locals: {alarmId: scope.alarm.id.id, showingCallback: onShowingCallback},
parent: angular.element($document[0].body),
targetEvent: $event,
fullscreen: true,
skipHide: true,
onShowing: function(scope, element) {
onShowingCallback.onShowing(scope, element);
}
}).then(function (alarm) {
if (alarm) {
scope.alarm = alarm;
}
});
}
$compile(element.contents())(scope);
}
return {
restrict: "A",
replace: false,
link: linker,
scope: false
};
}

17
ui/src/app/event/event-row-alarm.tpl.html → ui/src/app/alarm/alarm-row.tpl.html

@ -15,14 +15,19 @@
limitations under the License. limitations under the License.
--> -->
<div class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div> <div class="tb-cell" flex="30">{{alarm.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
<div class="tb-cell" flex="20">{{event.body.server}}</div> <div class="tb-cell" flex="15">{{alarm.originatorName}}</div>
<div class="tb-cell" flex="20"> <div class="tb-cell" flex="20">{{alarm.type}}</div>
<md-button ng-if="event.body.body" class="md-icon-button md-primary" <div class="tb-cell tb-severity" flex="15" ng-class="types.alarmSeverity[alarm.severity].class">
ng-click="showContent($event, event.body.body, 'event.alarm')" {{ alarm ? (types.alarmSeverity[alarm.severity].name | translate) : '' }}
</div>
<div class="tb-cell" flex="20">{{ alarm ? (('alarm.display-status.' + alarm.status) | translate) : '' }}</div>
<div class="tb-cell" flex="15">
<md-button class="md-icon-button md-primary"
ng-click="showAlarmDetails($event)"
aria-label="{{ 'action.view' | translate }}"> aria-label="{{ 'action.view' | translate }}">
<md-tooltip md-direction="top"> <md-tooltip md-direction="top">
{{ 'action.view' | translate }} {{ 'alarm.details' | translate }}
</md-tooltip> </md-tooltip>
<md-icon aria-label="{{ 'action.view' | translate }}" <md-icon aria-label="{{ 'action.view' | translate }}"
class="material-icons"> class="material-icons">

211
ui/src/app/alarm/alarm-table.directive.js

@ -0,0 +1,211 @@
/*
* Copyright © 2016-2017 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 './alarm.scss';
/* eslint-disable import/no-unresolved, import/default */
import alarmTableTemplate from './alarm-table.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function AlarmTableDirective($compile, $templateCache, $rootScope, types, alarmService) {
var linker = function (scope, element) {
var template = $templateCache.get(alarmTableTemplate);
element.html(template);
scope.types = types;
scope.alarmSearchStatus = types.alarmSearchStatus.any;
var pageSize = 20;
var startTime = 0;
var endTime = 0;
scope.timewindow = {
history: {
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
}
}
scope.topIndex = 0;
scope.theAlarms = {
getItemAtIndex: function (index) {
if (index > scope.alarms.data.length) {
scope.theAlarms.fetchMoreItems_(index);
return null;
}
var item = scope.alarms.data[index];
if (item) {
item.indexNumber = index + 1;
}
return item;
},
getLength: function () {
if (scope.alarms.hasNext) {
return scope.alarms.data.length + scope.alarms.nextPageLink.limit;
} else {
return scope.alarms.data.length;
}
},
fetchMoreItems_: function () {
if (scope.alarms.hasNext && !scope.alarms.pending) {
if (scope.entityType && scope.entityId && scope.alarmSearchStatus) {
var promise = alarmService.getAlarms(scope.entityType, scope.entityId,
scope.alarms.nextPageLink, scope.alarmSearchStatus, null, true, false);
if (promise) {
scope.alarms.pending = true;
promise.then(
function success(alarms) {
scope.alarms.data = scope.alarms.data.concat(alarms.data);
scope.alarms.nextPageLink = alarms.nextPageLink;
scope.alarms.hasNext = alarms.hasNext;
if (scope.alarms.hasNext) {
scope.alarms.nextPageLink.limit = pageSize;
}
scope.alarms.pending = false;
},
function fail() {
scope.alarms.hasNext = false;
scope.alarms.pending = false;
});
} else {
scope.alarms.hasNext = false;
}
} else {
scope.alarms.hasNext = false;
}
}
}
};
scope.$watch("entityId", function(newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) {
resetFilter();
reload();
}
});
function destroyWatchers() {
if (scope.alarmSearchStatusWatchHandle) {
scope.alarmSearchStatusWatchHandle();
scope.alarmSearchStatusWatchHandle = null;
}
if (scope.timewindowWatchHandle) {
scope.timewindowWatchHandle();
scope.timewindowWatchHandle = null;
}
}
function initWatchers() {
scope.alarmSearchStatusWatchHandle = scope.$watch("alarmSearchStatus", function(newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) {
reload();
}
});
scope.timewindowWatchHandle = scope.$watch("timewindow", function(newVal, prevVal) {
if (newVal && !angular.equals(newVal, prevVal)) {
reload();
}
}, true);
}
function resetFilter() {
destroyWatchers();
scope.timewindow = {
history: {
timewindowMs: 24 * 60 * 60 * 1000 // 1 day
}
};
scope.alarmSearchStatus = types.alarmSearchStatus.any;
initWatchers();
}
function updateTimeWindowRange () {
if (scope.timewindow.history.timewindowMs) {
var currentTime = (new Date).getTime();
startTime = currentTime - scope.timewindow.history.timewindowMs;
endTime = currentTime;
} else {
startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
}
}
function reload () {
scope.topIndex = 0;
scope.selected = [];
updateTimeWindowRange();
scope.alarms = {
data: [],
nextPageLink: {
limit: pageSize,
startTime: startTime,
endTime: endTime
},
hasNext: true,
pending: false
};
scope.theAlarms.getItemAtIndex(pageSize);
}
scope.noData = function() {
return scope.alarms.data.length == 0 && !scope.alarms.hasNext;
}
scope.hasData = function() {
return scope.alarms.data.length > 0;
}
scope.loading = function() {
return $rootScope.loading;
}
scope.hasScroll = function() {
var repeatContainer = scope.repeatContainer[0];
if (repeatContainer) {
var scrollElement = repeatContainer.children[0];
if (scrollElement) {
return scrollElement.scrollHeight > scrollElement.clientHeight;
}
}
return false;
}
reload();
initWatchers();
$compile(element.contents())(scope);
}
return {
restrict: "E",
link: linker,
scope: {
entityType: '=',
entityId: '='
}
};
}

49
ui/src/app/alarm/alarm-table.tpl.html

@ -0,0 +1,49 @@
<!--
Copyright © 2016-2017 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.
-->
<md-content flex class="md-padding tb-absolute-fill" layout="column">
<section layout="row">
<md-input-container class="md-block" style="width: 200px;">
<label translate>alarm.alarm-status</label>
<md-select ng-model="alarmSearchStatus" ng-disabled="loading()">
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
{{ ('alarm.search-status.' + searchStatus) | translate }}
</md-option>
</md-select>
</md-input-container>
<tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
</section>
<div flex layout="column" class="tb-alarm-container md-whiteframe-z1">
<md-list flex layout="column" class="tb-alarm-table">
<md-list class="tb-row tb-header" layout="row" tb-alarm-header>
</md-list>
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"
ng-show="loading()"></md-progress-linear>
<md-divider></md-divider>
<span translate layout-align="center center"
style="margin-top: 25px;"
class="tb-prompt" ng-show="noData()">alarm.no-alarms-prompt</span>
<md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer">
<md-list-item md-virtual-repeat="alarm in theAlarms" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}">
<md-list class="tb-row" flex layout="row" tb-alarm-row alarm="{{alarm}}">
</md-list>
<md-divider flex></md-divider>
</md-list-item>
</md-virtual-repeat-container>
</md-list>
</div>
</md-content>

78
ui/src/app/alarm/alarm.scss

@ -0,0 +1,78 @@
/**
* Copyright © 2016-2017 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-alarm-container {
overflow-x: auto;
}
md-list.tb-alarm-table {
padding: 0px;
min-width: 700px;
md-list-item {
padding: 0px;
}
.tb-row {
height: 48px;
padding: 0px;
overflow: hidden;
}
.tb-row:hover {
background-color: #EEEEEE;
}
.tb-header:hover {
background: none;
}
.tb-header {
.tb-cell {
color: rgba(0,0,0,.54);
font-size: 12px;
font-weight: 700;
white-space: nowrap;
background: none;
}
}
.tb-cell {
padding: 0 24px;
margin: auto 0;
color: rgba(0,0,0,.87);
font-size: 13px;
vertical-align: middle;
text-align: left;
overflow: hidden;
.md-button {
padding: 0;
margin: 0;
}
}
.tb-cell.tb-number {
text-align: right;
}
}
#tb-alarm-content {
min-width: 400px;
min-height: 50px;
width: 100%;
height: 100%;
}

27
ui/src/app/alarm/index.js

@ -0,0 +1,27 @@
/*
* Copyright © 2016-2017 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 AlarmDetailsDialogController from './alarm-details-dialog.controller';
import AlarmHeaderDirective from './alarm-header.directive';
import AlarmRowDirective from './alarm-row.directive';
import AlarmTableDirective from './alarm-table.directive';
export default angular.module('thingsboard.alarm', [])
.controller('AlarmDetailsDialogController', AlarmDetailsDialogController)
.directive('tbAlarmHeader', AlarmHeaderDirective)
.directive('tbAlarmRow', AlarmRowDirective)
.directive('tbAlarmTable', AlarmTableDirective)
.name;

16
ui/src/app/api/alarm.service.js

@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', [])
function AlarmService($http, $q, $interval, $filter) { function AlarmService($http, $q, $interval, $filter) {
var service = { var service = {
getAlarm: getAlarm, getAlarm: getAlarm,
getAlarmInfo: getAlarmInfo,
saveAlarm: saveAlarm, saveAlarm: saveAlarm,
ackAlarm: ackAlarm, ackAlarm: ackAlarm,
clearAlarm: clearAlarm, clearAlarm: clearAlarm,
@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) {
return deferred.promise; return deferred.promise;
} }
function getAlarmInfo(alarmId, ignoreErrors, config) {
var deferred = $q.defer();
var url = '/api/alarm/info/' + alarmId;
if (!config) {
config = {};
}
config = Object.assign(config, { ignoreErrors: ignoreErrors });
$http.get(url, config).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function saveAlarm(alarm, ignoreErrors, config) { function saveAlarm(alarm, ignoreErrors, config) {
var deferred = $q.defer(); var deferred = $q.defer();
var url = '/api/alarm'; var url = '/api/alarm';

7
ui/src/app/asset/assets.tpl.html

@ -48,11 +48,16 @@
disable-attribute-scope-selection="true"> disable-attribute-scope-selection="true">
</tb-attribute-table> </tb-attribute-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.asset"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'asset.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.asset" <tb-event-table flex entity-type="vm.types.entityType.asset"
entity-id="vm.grid.operatingItem().id.id" entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id" tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

2
ui/src/app/asset/index.js

@ -15,7 +15,6 @@
*/ */
import uiRouter from 'angular-ui-router'; import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive'; import thingsboardGrid from '../components/grid.directive';
import thingsboardEvent from '../event';
import thingsboardApiUser from '../api/user.service'; import thingsboardApiUser from '../api/user.service';
import thingsboardApiAsset from '../api/asset.service'; import thingsboardApiAsset from '../api/asset.service';
import thingsboardApiCustomer from '../api/customer.service'; import thingsboardApiCustomer from '../api/customer.service';
@ -29,7 +28,6 @@ import AssetDirective from './asset.directive';
export default angular.module('thingsboard.asset', [ export default angular.module('thingsboard.asset', [
uiRouter, uiRouter,
thingsboardGrid, thingsboardGrid,
thingsboardEvent,
thingsboardApiUser, thingsboardApiUser,
thingsboardApiAsset, thingsboardApiAsset,
thingsboardApiCustomer thingsboardApiCustomer

26
ui/src/app/common/types.constant.js

@ -72,6 +72,28 @@ export default angular.module('thingsboard.types', [])
ack: "ACK", ack: "ACK",
unack: "UNACK" unack: "UNACK"
}, },
alarmSeverity: {
"CRITICAL": {
name: "alarm.severity-critical",
class: "tb-critical"
},
"MAJOR": {
name: "alarm.severity-major",
class: "tb-major"
},
"MINOR": {
name: "alarm.severity-minor",
class: "tb-minor"
},
"WARNING": {
name: "alarm.severity-warning",
class: "tb-warning"
},
"INDETERMINATE": {
name: "alarm.severity-indeterminate",
class: "tb-indeterminate"
}
},
aliasFilterType: { aliasFilterType: {
entityList: { entityList: {
value: 'entityList', value: 'entityList',
@ -215,10 +237,6 @@ export default angular.module('thingsboard.types', [])
manages: "Manages" manages: "Manages"
}, },
eventType: { eventType: {
alarm: {
value: "ALARM",
name: "event.type-alarm"
},
error: { error: {
value: "ERROR", value: "ERROR",
name: "event.type-error" name: "event.type-error"

7
ui/src/app/customer/customers.tpl.html

@ -48,11 +48,16 @@
disable-attribute-scope-selection="true"> disable-attribute-scope-selection="true">
</tb-attribute-table> </tb-attribute-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.customer"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'customer.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.customer" <tb-event-table flex entity-type="vm.types.entityType.customer"
entity-id="vm.grid.operatingItem().id.id" entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id" tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

7
ui/src/app/device/devices.tpl.html

@ -49,11 +49,16 @@
disable-attribute-scope-selection="true"> disable-attribute-scope-selection="true">
</tb-attribute-table> </tb-attribute-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.device"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'device.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.device" <tb-event-table flex entity-type="vm.types.entityType.device"
entity-id="vm.grid.operatingItem().id.id" entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id" tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

2
ui/src/app/device/index.js

@ -15,7 +15,6 @@
*/ */
import uiRouter from 'angular-ui-router'; import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive'; import thingsboardGrid from '../components/grid.directive';
import thingsboardEvent from '../event';
import thingsboardApiUser from '../api/user.service'; import thingsboardApiUser from '../api/user.service';
import thingsboardApiDevice from '../api/device.service'; import thingsboardApiDevice from '../api/device.service';
import thingsboardApiCustomer from '../api/customer.service'; import thingsboardApiCustomer from '../api/customer.service';
@ -30,7 +29,6 @@ import DeviceDirective from './device.directive';
export default angular.module('thingsboard.device', [ export default angular.module('thingsboard.device', [
uiRouter, uiRouter,
thingsboardGrid, thingsboardGrid,
thingsboardEvent,
thingsboardApiUser, thingsboardApiUser,
thingsboardApiDevice, thingsboardApiDevice,
thingsboardApiCustomer thingsboardApiCustomer

2
ui/src/app/event/event-content-dialog.controller.js

@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title,
} }
newWidth = 8 * maxLineLength + 16; newWidth = 8 * maxLineLength + 16;
} }
$('#tb-content', element).height(newHeight.toString() + "px") $('#tb-event-content', element).height(newHeight.toString() + "px")
.width(newWidth.toString() + "px"); .width(newWidth.toString() + "px");
vm.editor.resize(); vm.editor.resize();
} }

2
ui/src/app/event/event-content-dialog.tpl.html

@ -27,7 +27,7 @@
</md-toolbar> </md-toolbar>
<md-dialog-content> <md-dialog-content>
<div class="md-dialog-content"> <div class="md-dialog-content">
<div flex id="tb-content" readonly <div flex id="tb-event-content" readonly
ui-ace="vm.contentOptions" ui-ace="vm.contentOptions"
ng-model="vm.content"> ng-model="vm.content">
</div> </div>

4
ui/src/app/event/event-header.directive.js

@ -18,7 +18,6 @@
import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html'; import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
import eventHeaderStatsTemplate from './event-header-stats.tpl.html'; import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
import eventHeaderErrorTemplate from './event-header-error.tpl.html'; import eventHeaderErrorTemplate from './event-header-error.tpl.html';
import eventHeaderAlarmTemplate from './event-header-alarm.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
case types.eventType.error.value: case types.eventType.error.value:
template = eventHeaderErrorTemplate; template = eventHeaderErrorTemplate;
break; break;
case types.eventType.alarm.value:
template = eventHeaderAlarmTemplate;
break;
} }
return $templateCache.get(template); return $templateCache.get(template);
} }

4
ui/src/app/event/event-row.directive.js

@ -20,7 +20,6 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
import eventRowLcEventTemplate from './event-row-lc-event.tpl.html'; import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
import eventRowStatsTemplate from './event-row-stats.tpl.html'; import eventRowStatsTemplate from './event-row-stats.tpl.html';
import eventRowErrorTemplate from './event-row-error.tpl.html'; import eventRowErrorTemplate from './event-row-error.tpl.html';
import eventRowAlarmTemplate from './event-row-alarm.tpl.html';
/* eslint-enable import/no-unresolved, import/default */ /* eslint-enable import/no-unresolved, import/default */
@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
case types.eventType.error.value: case types.eventType.error.value:
template = eventRowErrorTemplate; template = eventRowErrorTemplate;
break; break;
case types.eventType.alarm.value:
template = eventRowAlarmTemplate;
break;
} }
return $templateCache.get(template); return $templateCache.get(template);
} }

2
ui/src/app/event/event-table.tpl.html

@ -27,7 +27,7 @@
</md-input-container> </md-input-container>
<tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow> <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
</section> </section>
<md-list flex layout="column" class="md-whiteframe-z1 tb-table"> <md-list flex layout="column" class="md-whiteframe-z1 tb-event-table">
<md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}"> <md-list class="tb-row tb-header" layout="row" tb-event-header event-type="{{eventType}}">
</md-list> </md-list>
<md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()" <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!loading()"

4
ui/src/app/event/event.scss

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
md-list.tb-table { md-list.tb-event-table {
padding: 0px; padding: 0px;
md-list-item { md-list-item {
@ -64,7 +64,7 @@ md-list.tb-table {
} }
#tb-content { #tb-event-content {
min-width: 400px; min-width: 400px;
min-height: 50px; min-height: 50px;
width: 100%; width: 100%;

4
ui/src/app/layout/index.js

@ -32,6 +32,8 @@ import thingsboardDashboardAutocomplete from '../components/dashboard-autocomple
import thingsboardUserMenu from './user-menu.directive'; import thingsboardUserMenu from './user-menu.directive';
import thingsboardEntity from '../entity'; import thingsboardEntity from '../entity';
import thingsboardEvent from '../event';
import thingsboardAlarm from '../alarm';
import thingsboardTenant from '../tenant'; import thingsboardTenant from '../tenant';
import thingsboardCustomer from '../customer'; import thingsboardCustomer from '../customer';
import thingsboardUser from '../user'; import thingsboardUser from '../user';
@ -61,6 +63,8 @@ export default angular.module('thingsboard.home', [
thingsboardHomeLinks, thingsboardHomeLinks,
thingsboardUserMenu, thingsboardUserMenu,
thingsboardEntity, thingsboardEntity,
thingsboardEvent,
thingsboardAlarm,
thingsboardTenant, thingsboardTenant,
thingsboardCustomer, thingsboardCustomer,
thingsboardUser, thingsboardUser,

1
ui/src/app/locale/locale.constant-es.js

@ -411,7 +411,6 @@
}, },
"event": { "event": {
"event-type": "Tipo de evento", "event-type": "Tipo de evento",
"type-alarm": "Alarma",
"type-error": "Error", "type-error": "Error",
"type-lc-event": "Ciclo de vida", "type-lc-event": "Ciclo de vida",
"type-stats": "Estadísticas", "type-stats": "Estadísticas",

1
ui/src/app/locale/locale.constant-ko.js

@ -378,7 +378,6 @@ export default function addLocaleKorean(locales) {
}, },
"event": { "event": {
"event-type": "이벤트 타입", "event-type": "이벤트 타입",
"type-alarm": "알람",
"type-error": "에러", "type-error": "에러",
"type-lc-event": "주기적 이벤트", "type-lc-event": "주기적 이벤트",
"type-stats": "통계", "type-stats": "통계",

1
ui/src/app/locale/locale.constant-ru.js

@ -411,7 +411,6 @@ export default function addLocaleRussian(locales) {
}, },
"event": { "event": {
"event-type": "Тип события", "event-type": "Тип события",
"type-alarm": "Аварийное оповещение",
"type-error": "Ошибка", "type-error": "Ошибка",
"type-lc-event": "Событие жизненного цикла", "type-lc-event": "Событие жизненного цикла",
"type-stats": "Статистика", "type-stats": "Статистика",

1
ui/src/app/locale/locale.constant-zh.js

@ -411,7 +411,6 @@ export default function addLocaleChinese(locales) {
}, },
"event" : { "event" : {
"event-type": "事件类型", "event-type": "事件类型",
"type-alarm": "报警",
"type-error": "错误", "type-error": "错误",
"type-lc-event": "生命周期事件", "type-lc-event": "生命周期事件",
"type-stats": "类型统计", "type-stats": "类型统计",

37
ui/src/app/locale/locale.constant.js

@ -108,9 +108,43 @@ export default angular.module('thingsboard.locale', [])
}, },
"alarm": { "alarm": {
"alarm": "Alarm", "alarm": "Alarm",
"alarms": "Alarms",
"select-alarm": "Select alarm", "select-alarm": "Select alarm",
"no-alarms-matching": "No alarms matching '{{entity}}' were found.", "no-alarms-matching": "No alarms matching '{{entity}}' were found.",
"alarm-required": "Alarm is required" "alarm-required": "Alarm is required",
"alarm-status": "Alarm status",
"search-status": {
"ANY": "Any",
"ACTIVE": "Active",
"CLEARED": "Cleared",
"ACK": "Acknowledged",
"UNACK": "Unacknowledged"
},
"display-status": {
"ACTIVE_UNACK": "Active Unacknowledged",
"ACTIVE_ACK": "Active Acknowledged",
"CLEARED_UNACK": "Cleared Unacknowledged",
"CLEARED_ACK": "Cleared Acknowledged"
},
"no-alarms-prompt": "No alarms found",
"created-time": "Created time",
"type": "Type",
"severity": "Severity",
"originator": "Originator",
"details": "Details",
"status": "Status",
"alarm-details": "Alarm details",
"start-time": "Start time",
"end-time": "End time",
"ack-time": "Acknowledged time",
"clear-time": "Cleared time",
"severity-critical": "Critical",
"severity-major": "Major",
"severity-minor": "Minor",
"severity-warning": "Warning",
"severity-indeterminate": "Indeterminate",
"acknowledge": "Acknowledge",
"clear": "Clear"
}, },
"alias": { "alias": {
"add": "Add alias", "add": "Add alias",
@ -647,7 +681,6 @@ export default angular.module('thingsboard.locale', [])
}, },
"event": { "event": {
"event-type": "Event type", "event-type": "Event type",
"type-alarm": "Alarm",
"type-error": "Error", "type-error": "Error",
"type-lc-event": "Lifecycle event", "type-lc-event": "Lifecycle event",
"type-stats": "Statistics", "type-stats": "Statistics",

2
ui/src/app/plugin/index.js

@ -16,7 +16,6 @@
import uiRouter from 'angular-ui-router'; import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive'; import thingsboardGrid from '../components/grid.directive';
import thingsboardJsonForm from '../components/json-form.directive'; import thingsboardJsonForm from '../components/json-form.directive';
import thingsboardEvent from '../event';
import thingsboardApiPlugin from '../api/plugin.service'; import thingsboardApiPlugin from '../api/plugin.service';
import thingsboardApiComponentDescriptor from '../api/component-descriptor.service'; import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@ -28,7 +27,6 @@ export default angular.module('thingsboard.plugin', [
uiRouter, uiRouter,
thingsboardGrid, thingsboardGrid,
thingsboardJsonForm, thingsboardJsonForm,
thingsboardEvent,
thingsboardApiPlugin, thingsboardApiPlugin,
thingsboardApiComponentDescriptor thingsboardApiComponentDescriptor
]) ])

8
ui/src/app/plugin/plugins.tpl.html

@ -48,12 +48,16 @@
disable-attribute-scope-selection="true"> disable-attribute-scope-selection="true">
</tb-attribute-table> </tb-attribute-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.plugin"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'plugin.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.plugin" <tb-event-table flex entity-type="vm.types.entityType.plugin"
entity-id="vm.grid.operatingItem().id.id" entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id" tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.lcEvent.value}}" default-event-type="{{vm.types.eventType.lcEvent.value}}">
disabled-event-types="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isPluginEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">

2
ui/src/app/rule/index.js

@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router';
import thingsboardGrid from '../components/grid.directive'; import thingsboardGrid from '../components/grid.directive';
import thingsboardPluginSelect from '../components/plugin-select.directive'; import thingsboardPluginSelect from '../components/plugin-select.directive';
import thingsboardComponent from '../component'; import thingsboardComponent from '../component';
import thingsboardEvent from '../event';
import thingsboardApiRule from '../api/rule.service'; import thingsboardApiRule from '../api/rule.service';
import thingsboardApiPlugin from '../api/plugin.service'; import thingsboardApiPlugin from '../api/plugin.service';
import thingsboardApiComponentDescriptor from '../api/component-descriptor.service'; import thingsboardApiComponentDescriptor from '../api/component-descriptor.service';
@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [
thingsboardGrid, thingsboardGrid,
thingsboardPluginSelect, thingsboardPluginSelect,
thingsboardComponent, thingsboardComponent,
thingsboardEvent,
thingsboardApiRule, thingsboardApiRule,
thingsboardApiPlugin, thingsboardApiPlugin,
thingsboardApiComponentDescriptor thingsboardApiComponentDescriptor

8
ui/src/app/rule/rules.tpl.html

@ -48,12 +48,16 @@
disable-attribute-scope-selection="true"> disable-attribute-scope-selection="true">
</tb-attribute-table> </tb-attribute-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.rule"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'rule.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.rule" <tb-event-table flex entity-type="vm.types.entityType.rule"
entity-id="vm.grid.operatingItem().id.id" entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.grid.operatingItem().tenantId.id" tenant-id="vm.grid.operatingItem().tenantId.id"
default-event-type="{{vm.types.eventType.lcEvent.value}}" default-event-type="{{vm.types.eventType.lcEvent.value}}">
disabled-event-types="{{vm.types.eventType.alarm.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleEditable(vm.grid.operatingItem())" label="{{ 'relation.relations' | translate }}">

7
ui/src/app/tenant/tenants.tpl.html

@ -46,11 +46,16 @@
disable-attribute-scope-selection="true"> disable-attribute-scope-selection="true">
</tb-attribute-table> </tb-attribute-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'alarm.alarms' | translate }}">
<tb-alarm-table flex entity-type="vm.types.entityType.tenant"
entity-id="vm.grid.operatingItem().id.id">
</tb-alarm-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'tenant.events' | translate }}">
<tb-event-table flex entity-type="vm.types.entityType.tenant" <tb-event-table flex entity-type="vm.types.entityType.tenant"
entity-id="vm.grid.operatingItem().id.id" entity-id="vm.grid.operatingItem().id.id"
tenant-id="vm.types.id.nullUid" tenant-id="vm.types.id.nullUid"
default-event-type="{{vm.types.eventType.alarm.value}}"> default-event-type="{{vm.types.eventType.error.value}}">
</tb-event-table> </tb-event-table>
</md-tab> </md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}"> <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">

18
ui/src/scss/main.scss

@ -309,6 +309,24 @@ pre.tb-highlight {
} }
} }
.tb-severity {
font-weight: bold;
&.tb-critical {
color: red !important;
}
&.tb-major {
color: orange !important;
}
&.tb-minor {
color: #ffca3d !important;
}
&.tb-warning {
color: #abab00 !important;
}
&.tb-indeterminate {
color: green !important;
}
}
/*********************** /***********************
* Flow * Flow

Loading…
Cancel
Save