diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 19774bbf42..19c75e7b9b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/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')") @RequestMapping(value = "/alarm", method = RequestMethod.POST) @ResponseBody diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index aba4b6e162..c63dda7b3a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/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.alarm.Alarm; 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.id.*; 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 { checkNotNull(alarm); checkTenantId(alarm.getTenantId()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index 9c6f998a3f..e48cf5b7a8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -55,6 +55,7 @@ public class Alarm extends BaseData implements HasName { public Alarm(Alarm alarm) { super(alarm.getId()); + this.createdTime = alarm.getCreatedTime(); this.tenantId = alarm.getTenantId(); this.type = alarm.getType(); this.originator = alarm.getOriginator(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index fb8a80d611..3556d51f09 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -35,6 +35,8 @@ public interface AlarmService { ListenableFuture findAlarmByIdAsync(AlarmId alarmId); + ListenableFuture findAlarmInfoByIdAsync(AlarmId alarmId); + ListenableFuture> findAlarms(AlarmQuery query); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index d152d96767..58f7316a75 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/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()); } + @Override + public ListenableFuture findAlarmInfoByIdAsync(AlarmId alarmId) { + log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); + validateId(alarmId, "Incorrect alarmId " + alarmId); + return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()), + (AsyncFunction) alarm1 -> { + AlarmInfo alarmInfo = new AlarmInfo(alarm1); + return Futures.transform( + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function) + originatorName -> { + alarmInfo.setOriginatorName(originatorName); + return alarmInfo; + } + ); + }); + } + @Override public ListenableFuture> findAlarms(AlarmQuery query) { ListenableFuture> alarms = alarmDao.findAlarms(query); diff --git a/ui/src/app/alarm/alarm-details-dialog.controller.js b/ui/src/app/alarm/alarm-details-dialog.controller.js new file mode 100644 index 0000000000..0cc05ef295 --- /dev/null +++ b/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); + } + +} diff --git a/ui/src/app/alarm/alarm-details-dialog.scss b/ui/src/app/alarm/alarm-details-dialog.scss new file mode 100644 index 0000000000..9b923d076c --- /dev/null +++ b/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%; + } +} diff --git a/ui/src/app/alarm/alarm-details-dialog.tpl.html b/ui/src/app/alarm/alarm-details-dialog.tpl.html new file mode 100644 index 0000000000..c958201ea9 --- /dev/null +++ b/ui/src/app/alarm/alarm-details-dialog.tpl.html @@ -0,0 +1,107 @@ + + + +
+

alarm.alarm-details

+ + + + +
+
+ +
+
+ + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + +
+
+ + + + + + + + + + + + +
+
alarm.details
+
+
+
+
+
+
+ + {{ 'alarm.acknowledge' | + translate }} + + {{ 'alarm.clear' | + translate }} + + + {{ 'action.close' | + translate }} + + +
diff --git a/ui/src/app/alarm/alarm-header.directive.js b/ui/src/app/alarm/alarm-header.directive.js new file mode 100644 index 0000000000..b66a9729a1 --- /dev/null +++ b/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 + }; +} diff --git a/ui/src/app/event/event-header-alarm.tpl.html b/ui/src/app/alarm/alarm-header.tpl.html similarity index 62% rename from ui/src/app/event/event-header-alarm.tpl.html rename to ui/src/app/alarm/alarm-header.tpl.html index be1b0752b3..b67ac8f8e1 100644 --- a/ui/src/app/event/event-header-alarm.tpl.html +++ b/ui/src/app/alarm/alarm-header.tpl.html @@ -15,6 +15,9 @@ limitations under the License. --> -
event.event-time
-
event.server
-
event.alarm
+
alarm.created-time
+
alarm.originator
+
alarm.type
+
alarm.severity
+
alarm.status
+
alarm.details
diff --git a/ui/src/app/alarm/alarm-row.directive.js b/ui/src/app/alarm/alarm-row.directive.js new file mode 100644 index 0000000000..9cb9bedd68 --- /dev/null +++ b/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 + }; +} diff --git a/ui/src/app/event/event-row-alarm.tpl.html b/ui/src/app/alarm/alarm-row.tpl.html similarity index 59% rename from ui/src/app/event/event-row-alarm.tpl.html rename to ui/src/app/alarm/alarm-row.tpl.html index fa6a2148ab..00c83241f7 100644 --- a/ui/src/app/event/event-row-alarm.tpl.html +++ b/ui/src/app/alarm/alarm-row.tpl.html @@ -15,14 +15,19 @@ limitations under the License. --> -
{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}
-
{{event.body.server}}
-
- {{alarm.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}
+
{{alarm.originatorName}}
+
{{alarm.type}}
+
+ {{ alarm ? (types.alarmSeverity[alarm.severity].name | translate) : '' }} +
+
{{ alarm ? (('alarm.display-status.' + alarm.status) | translate) : '' }}
+
+ - {{ 'action.view' | translate }} + {{ 'alarm.details' | translate }} diff --git a/ui/src/app/alarm/alarm-table.directive.js b/ui/src/app/alarm/alarm-table.directive.js new file mode 100644 index 0000000000..c93ea03981 --- /dev/null +++ b/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: '=' + } + }; +} diff --git a/ui/src/app/alarm/alarm-table.tpl.html b/ui/src/app/alarm/alarm-table.tpl.html new file mode 100644 index 0000000000..c32e39a2d2 --- /dev/null +++ b/ui/src/app/alarm/alarm-table.tpl.html @@ -0,0 +1,49 @@ + + +
+ + + + + {{ ('alarm.search-status.' + searchStatus) | translate }} + + + + +
+
+ + + + + + alarm.no-alarms-prompt + + + + + + + + +
+
diff --git a/ui/src/app/alarm/alarm.scss b/ui/src/app/alarm/alarm.scss new file mode 100644 index 0000000000..aea5225445 --- /dev/null +++ b/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%; +} diff --git a/ui/src/app/alarm/index.js b/ui/src/app/alarm/index.js new file mode 100644 index 0000000000..0ea4610c3d --- /dev/null +++ b/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; diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js index ca892f292b..34e6b59e20 100644 --- a/ui/src/app/api/alarm.service.js +++ b/ui/src/app/api/alarm.service.js @@ -21,6 +21,7 @@ export default angular.module('thingsboard.api.alarm', []) function AlarmService($http, $q, $interval, $filter) { var service = { getAlarm: getAlarm, + getAlarmInfo: getAlarmInfo, saveAlarm: saveAlarm, ackAlarm: ackAlarm, clearAlarm: clearAlarm, @@ -46,6 +47,21 @@ function AlarmService($http, $q, $interval, $filter) { 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) { var deferred = $q.defer(); var url = '/api/alarm'; diff --git a/ui/src/app/asset/assets.tpl.html b/ui/src/app/asset/assets.tpl.html index 11a118f5e4..8370d3dbac 100644 --- a/ui/src/app/asset/assets.tpl.html +++ b/ui/src/app/asset/assets.tpl.html @@ -48,11 +48,16 @@ disable-attribute-scope-selection="true"> + + + + + default-event-type="{{vm.types.eventType.error.value}}"> diff --git a/ui/src/app/asset/index.js b/ui/src/app/asset/index.js index 62ab201b69..2426e85b3c 100644 --- a/ui/src/app/asset/index.js +++ b/ui/src/app/asset/index.js @@ -15,7 +15,6 @@ */ import uiRouter from 'angular-ui-router'; import thingsboardGrid from '../components/grid.directive'; -import thingsboardEvent from '../event'; import thingsboardApiUser from '../api/user.service'; import thingsboardApiAsset from '../api/asset.service'; import thingsboardApiCustomer from '../api/customer.service'; @@ -29,7 +28,6 @@ import AssetDirective from './asset.directive'; export default angular.module('thingsboard.asset', [ uiRouter, thingsboardGrid, - thingsboardEvent, thingsboardApiUser, thingsboardApiAsset, thingsboardApiCustomer diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index 20760937a3..b281ad1043 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -72,6 +72,28 @@ export default angular.module('thingsboard.types', []) ack: "ACK", 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: { entityList: { value: 'entityList', @@ -215,10 +237,6 @@ export default angular.module('thingsboard.types', []) manages: "Manages" }, eventType: { - alarm: { - value: "ALARM", - name: "event.type-alarm" - }, error: { value: "ERROR", name: "event.type-error" diff --git a/ui/src/app/customer/customers.tpl.html b/ui/src/app/customer/customers.tpl.html index 6c70a70dad..a6521f066d 100644 --- a/ui/src/app/customer/customers.tpl.html +++ b/ui/src/app/customer/customers.tpl.html @@ -48,11 +48,16 @@ disable-attribute-scope-selection="true"> + + + + + default-event-type="{{vm.types.eventType.error.value}}"> diff --git a/ui/src/app/device/devices.tpl.html b/ui/src/app/device/devices.tpl.html index 3ff6c1ce39..1e467be124 100644 --- a/ui/src/app/device/devices.tpl.html +++ b/ui/src/app/device/devices.tpl.html @@ -49,11 +49,16 @@ disable-attribute-scope-selection="true"> + + + + + default-event-type="{{vm.types.eventType.error.value}}"> diff --git a/ui/src/app/device/index.js b/ui/src/app/device/index.js index ea42ee8e95..5a8adaf9bb 100644 --- a/ui/src/app/device/index.js +++ b/ui/src/app/device/index.js @@ -15,7 +15,6 @@ */ import uiRouter from 'angular-ui-router'; import thingsboardGrid from '../components/grid.directive'; -import thingsboardEvent from '../event'; import thingsboardApiUser from '../api/user.service'; import thingsboardApiDevice from '../api/device.service'; import thingsboardApiCustomer from '../api/customer.service'; @@ -30,7 +29,6 @@ import DeviceDirective from './device.directive'; export default angular.module('thingsboard.device', [ uiRouter, thingsboardGrid, - thingsboardEvent, thingsboardApiUser, thingsboardApiDevice, thingsboardApiCustomer diff --git a/ui/src/app/event/event-content-dialog.controller.js b/ui/src/app/event/event-content-dialog.controller.js index 19ddb8c4d5..235cfcff32 100644 --- a/ui/src/app/event/event-content-dialog.controller.js +++ b/ui/src/app/event/event-content-dialog.controller.js @@ -62,7 +62,7 @@ export default function EventContentDialogController($mdDialog, content, title, } newWidth = 8 * maxLineLength + 16; } - $('#tb-content', element).height(newHeight.toString() + "px") + $('#tb-event-content', element).height(newHeight.toString() + "px") .width(newWidth.toString() + "px"); vm.editor.resize(); } diff --git a/ui/src/app/event/event-content-dialog.tpl.html b/ui/src/app/event/event-content-dialog.tpl.html index 4cf046b080..7b4184c5f4 100644 --- a/ui/src/app/event/event-content-dialog.tpl.html +++ b/ui/src/app/event/event-content-dialog.tpl.html @@ -27,7 +27,7 @@
-
diff --git a/ui/src/app/event/event-header.directive.js b/ui/src/app/event/event-header.directive.js index 5e8cc7c6e7..c43894ef85 100644 --- a/ui/src/app/event/event-header.directive.js +++ b/ui/src/app/event/event-header.directive.js @@ -18,7 +18,6 @@ import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html'; import eventHeaderStatsTemplate from './event-header-stats.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 */ @@ -39,9 +38,6 @@ export default function EventHeaderDirective($compile, $templateCache, types) { case types.eventType.error.value: template = eventHeaderErrorTemplate; break; - case types.eventType.alarm.value: - template = eventHeaderAlarmTemplate; - break; } return $templateCache.get(template); } diff --git a/ui/src/app/event/event-row.directive.js b/ui/src/app/event/event-row.directive.js index 9bff1921a6..d1feb60760 100644 --- a/ui/src/app/event/event-row.directive.js +++ b/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 eventRowStatsTemplate from './event-row-stats.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 */ @@ -41,9 +40,6 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ case types.eventType.error.value: template = eventRowErrorTemplate; break; - case types.eventType.alarm.value: - template = eventRowAlarmTemplate; - break; } return $templateCache.get(template); } diff --git a/ui/src/app/event/event-table.tpl.html b/ui/src/app/event/event-table.tpl.html index f44aeb32eb..82a921fedd 100644 --- a/ui/src/app/event/event-table.tpl.html +++ b/ui/src/app/event/event-table.tpl.html @@ -27,7 +27,7 @@ - + + + + + + default-event-type="{{vm.types.eventType.lcEvent.value}}"> diff --git a/ui/src/app/rule/index.js b/ui/src/app/rule/index.js index 64819201cf..2700ec71cf 100644 --- a/ui/src/app/rule/index.js +++ b/ui/src/app/rule/index.js @@ -17,7 +17,6 @@ import uiRouter from 'angular-ui-router'; import thingsboardGrid from '../components/grid.directive'; import thingsboardPluginSelect from '../components/plugin-select.directive'; import thingsboardComponent from '../component'; -import thingsboardEvent from '../event'; import thingsboardApiRule from '../api/rule.service'; import thingsboardApiPlugin from '../api/plugin.service'; import thingsboardApiComponentDescriptor from '../api/component-descriptor.service'; @@ -31,7 +30,6 @@ export default angular.module('thingsboard.rule', [ thingsboardGrid, thingsboardPluginSelect, thingsboardComponent, - thingsboardEvent, thingsboardApiRule, thingsboardApiPlugin, thingsboardApiComponentDescriptor diff --git a/ui/src/app/rule/rules.tpl.html b/ui/src/app/rule/rules.tpl.html index 098bbee7fb..336fe63ce9 100644 --- a/ui/src/app/rule/rules.tpl.html +++ b/ui/src/app/rule/rules.tpl.html @@ -48,12 +48,16 @@ disable-attribute-scope-selection="true"> + + + + + default-event-type="{{vm.types.eventType.lcEvent.value}}"> diff --git a/ui/src/app/tenant/tenants.tpl.html b/ui/src/app/tenant/tenants.tpl.html index 00350a4e02..e407291757 100644 --- a/ui/src/app/tenant/tenants.tpl.html +++ b/ui/src/app/tenant/tenants.tpl.html @@ -46,11 +46,16 @@ disable-attribute-scope-selection="true"> + + + + + default-event-type="{{vm.types.eventType.error.value}}"> diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss index bf444937a4..ab720c5e5a 100644 --- a/ui/src/scss/main.scss +++ b/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