Commit 0a5796c9fded2939c54290fd48e964b176f67605

Authored by Artem Babak
1 parent b85b51f5

Edge Downlinks as tab

@@ -35,7 +35,7 @@ function EdgeService($http, $q, customerService) { @@ -35,7 +35,7 @@ function EdgeService($http, $q, customerService) {
35 unassignEdgeFromCustomer: unassignEdgeFromCustomer, 35 unassignEdgeFromCustomer: unassignEdgeFromCustomer,
36 makeEdgePublic: makeEdgePublic, 36 makeEdgePublic: makeEdgePublic,
37 setRootRuleChain: setRootRuleChain, 37 setRootRuleChain: setRootRuleChain,
38 - getEdgeEvents: getEdgeEvents, 38 + getEdgeDownlinks: getEdgeDownlinks,
39 syncEdge: syncEdge, 39 syncEdge: syncEdge,
40 findMissingToRelatedRuleChains: findMissingToRelatedRuleChains 40 findMissingToRelatedRuleChains: findMissingToRelatedRuleChains
41 }; 41 };
@@ -276,7 +276,7 @@ function EdgeService($http, $q, customerService) { @@ -276,7 +276,7 @@ function EdgeService($http, $q, customerService) {
276 return deferred.promise; 276 return deferred.promise;
277 } 277 }
278 278
279 - function getEdgeEvents(edgeId, pageLink) { 279 + function getEdgeDownlinks(edgeId, pageLink) {
280 var deferred = $q.defer(); 280 var deferred = $q.defer();
281 var url = '/api/edge/' + edgeId + '/events' + '?limit=' + pageLink.limit; 281 var url = '/api/edge/' + edgeId + '/events' + '?limit=' + pageLink.limit;
282 if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { 282 if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) {
@@ -661,10 +661,6 @@ export default angular.module('thingsboard.types', []) @@ -661,10 +661,6 @@ export default angular.module('thingsboard.types', [])
661 stats: { 661 stats: {
662 value: "STATS", 662 value: "STATS",
663 name: "event.type-stats" 663 name: "event.type-stats"
664 - },  
665 - edgeEvent: {  
666 - value: "EDGE_EVENT",  
667 - name: "event.type-edge-event"  
668 } 664 }
669 }, 665 },
670 debugEventType: { 666 debugEventType: {
@@ -1197,6 +1193,14 @@ export default angular.module('thingsboard.types', []) @@ -1197,6 +1193,14 @@ export default angular.module('thingsboard.types', [])
1197 "ADMIN_SETTINGS": { 1193 "ADMIN_SETTINGS": {
1198 name: "permission.resource.display-type.ADMIN_SETTINGS" 1194 name: "permission.resource.display-type.ADMIN_SETTINGS"
1199 } 1195 }
  1196 + },
  1197 + edgeEvent: {
  1198 + value: "EDGE_EVENT",
  1199 + name: "edge.downlink"
  1200 + },
  1201 + edgeDownlinks: {
  1202 + value: "EDGE_DOWNLINKS",
  1203 + name: "edge.downlinks"
1200 } 1204 }
1201 } 1205 }
1202 ).name; 1206 ).name;
@@ -65,6 +65,13 @@ @@ -65,6 +65,13 @@
65 default-event-type="{{vm.types.eventType.error.value}}"> 65 default-event-type="{{vm.types.eventType.error.value}}">
66 </tb-event-table> 66 </tb-event-table>
67 </md-tab> 67 </md-tab>
  68 + <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'edge.downlinks' | translate }}">
  69 + <tb-edge-downlinks-table flex entity-type="vm.types.entityType.edge"
  70 + entity-id="vm.grid.operatingItem().id.id"
  71 + tenant-id="vm.grid.operatingItem().tenantId.id"
  72 + default-event-type="{{vm.types.edgeDownlinks.value}}">
  73 + </tb-edge-downlinks-table>
  74 + </md-tab>
68 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}"> 75 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}">
69 <tb-relation-table flex 76 <tb-relation-table flex
70 readonly="!('edge' | hasGenericPermission:'write')" 77 readonly="!('edge' | hasGenericPermission:'write')"
  1 +/*
  2 + * Copyright © 2016-2020 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 './event.scss';
  17 +
  18 +/* eslint-disable import/no-unresolved, import/default */
  19 +
  20 +import edgeDownlinksTableTemplate from './edge-downlinks-table.tpl.html';
  21 +
  22 +/* eslint-enable import/no-unresolved, import/default */
  23 +
  24 +/*@ngInject*/
  25 +export default function EdgeDownlinksDirective($compile, $templateCache, $rootScope, $translate, types,
  26 + eventService, edgeService, attributeService) {
  27 +
  28 + var linker = function (scope, element, attrs) {
  29 +
  30 + var template = $templateCache.get(edgeDownlinksTableTemplate);
  31 +
  32 + element.html(template);
  33 +
  34 + if (attrs.disabledEventTypes) {
  35 + var disabledEventTypes = attrs.disabledEventTypes.split(',');
  36 + scope.eventTypes = {};
  37 + for (var type in types.eventType) {
  38 + var eventType = types.eventType[type];
  39 + var enabled = true;
  40 + for (var i=0;i<disabledEventTypes.length;i++) {
  41 + if (eventType.value === disabledEventTypes[i]) {
  42 + enabled = false;
  43 + break;
  44 + }
  45 + }
  46 + if (enabled) {
  47 + scope.eventTypes[type] = eventType;
  48 + }
  49 + }
  50 + } else {
  51 + scope.eventTypes = angular.copy(types.eventType);
  52 + }
  53 +
  54 + if (attrs.debugEventTypes) {
  55 + var debugEventTypes = attrs.debugEventTypes.split(',');
  56 + for (i=0;i<debugEventTypes.length;i++) {
  57 + for (type in types.debugEventType) {
  58 + eventType = types.debugEventType[type];
  59 + if (eventType.value === debugEventTypes[i]) {
  60 + scope.eventTypes[type] = eventType;
  61 + }
  62 + }
  63 + }
  64 + }
  65 +
  66 + scope.eventType = attrs.defaultEventType;
  67 +
  68 + var pageSize = 20;
  69 + var startTime = 0;
  70 + var endTime = 0;
  71 +
  72 + scope.timewindow = {
  73 + history: {
  74 + timewindowMs: 24 * 60 * 60 * 1000 // 1 day
  75 + }
  76 + }
  77 +
  78 + scope.topIndex = 0;
  79 +
  80 + scope.theEvents = {
  81 + getItemAtIndex: function (index) {
  82 + if (index > scope.events.data.length) {
  83 + scope.theEvents.fetchMoreItems_(index);
  84 + return null;
  85 + }
  86 + var item = scope.events.data[index];
  87 + if (item) {
  88 + item.indexNumber = index + 1;
  89 + }
  90 + return item;
  91 + },
  92 +
  93 + getLength: function () {
  94 + if (scope.events.hasNext) {
  95 + return scope.events.data.length + scope.events.nextPageLink.limit;
  96 + } else {
  97 + return scope.events.data.length;
  98 + }
  99 + },
  100 +
  101 + fetchMoreItems_: function () {
  102 + if (scope.events.hasNext && !scope.events.pending) {
  103 + if (scope.entityType && scope.entityId && scope.eventType && scope.tenantId) {
  104 + scope.loadEdgeInfo();
  105 + scope.events.pending = true;
  106 + edgeService.getEdgeDownlinks(scope.entityId, scope.events.nextPageLink).then(
  107 + function success(events) {
  108 + scope.events.data = scope.events.data.concat(prepareEdgeEventData(events.data));
  109 + scope.events.nextPageLink = events.nextPageLink;
  110 + scope.events.hasNext = events.hasNext;
  111 + if (scope.events.hasNext) {
  112 + scope.events.nextPageLink.limit = pageSize;
  113 + }
  114 + scope.events.pending = false;
  115 + },
  116 + function fail() {
  117 + scope.events.hasNext = false;
  118 + scope.events.pending = false;
  119 + });
  120 + } else {
  121 + scope.events.hasNext = false;
  122 + }
  123 + }
  124 + }
  125 + };
  126 +
  127 + scope.$watch("entityId", function(newVal, prevVal) {
  128 + if (newVal && !angular.equals(newVal, prevVal)) {
  129 + scope.resetFilter();
  130 + scope.reload();
  131 + }
  132 + });
  133 +
  134 + scope.$watch("eventType", function(newVal, prevVal) {
  135 + if (newVal && !angular.equals(newVal, prevVal)) {
  136 + scope.reload();
  137 + }
  138 + });
  139 +
  140 + scope.$watch("timewindow", function(newVal, prevVal) {
  141 + if (newVal && !angular.equals(newVal, prevVal)) {
  142 + scope.reload();
  143 + }
  144 + }, true);
  145 +
  146 + scope.resetFilter = function() {
  147 + scope.timewindow = {
  148 + history: {
  149 + timewindowMs: 24 * 60 * 60 * 1000 // 1 day
  150 + }
  151 + };
  152 + }
  153 +
  154 + scope.updateTimeWindowRange = function() {
  155 + if (scope.timewindow.history.timewindowMs) {
  156 + var currentTime = (new Date).getTime();
  157 + startTime = currentTime - scope.timewindow.history.timewindowMs;
  158 + endTime = currentTime;
  159 + } else {
  160 + startTime = scope.timewindow.history.fixedTimewindow.startTimeMs;
  161 + endTime = scope.timewindow.history.fixedTimewindow.endTimeMs;
  162 + }
  163 + }
  164 +
  165 + scope.reload = function() {
  166 + scope.topIndex = 0;
  167 + scope.selected = [];
  168 + scope.updateTimeWindowRange();
  169 + scope.events = {
  170 + data: [],
  171 + nextPageLink: {
  172 + limit: pageSize,
  173 + startTime: startTime,
  174 + endTime: endTime
  175 + },
  176 + hasNext: true,
  177 + pending: false
  178 + };
  179 + scope.theEvents.getItemAtIndex(pageSize);
  180 + }
  181 +
  182 + scope.noData = function() {
  183 + return scope.events.data.length == 0 && !scope.events.hasNext;
  184 + }
  185 +
  186 + scope.hasData = function() {
  187 + return scope.events.data.length > 0;
  188 + }
  189 +
  190 + scope.loading = function() {
  191 + return $rootScope.loading;
  192 + }
  193 +
  194 + scope.hasScroll = function() {
  195 + var repeatContainer = scope.repeatContainer[0];
  196 + if (repeatContainer) {
  197 + var scrollElement = repeatContainer.children[0];
  198 + if (scrollElement) {
  199 + return scrollElement.scrollHeight > scrollElement.clientHeight;
  200 + }
  201 + }
  202 + return false;
  203 + }
  204 +
  205 + scope.subscriptionId = null;
  206 +
  207 + scope.loadEdgeInfo = function() {
  208 + attributeService.getEntityAttributesValues(
  209 + scope.entityType,
  210 + scope.entityId,
  211 + types.attributesScope.server.value,
  212 + types.edgeAttributeKeys.queueStartTs,
  213 + null).then(
  214 + function success(attributes) {
  215 + attributes.length > 0 ? scope.onEdgeAttributesUpdate(attributes) : scope.queueStartTs = 0;
  216 + });
  217 + scope.checkSubscription();
  218 + }
  219 +
  220 + scope.onEdgeAttributesUpdate = function(attributes) {
  221 + let edgeAttributes = attributes.reduce(function (map, attribute) {
  222 + map[attribute.key] = attribute;
  223 + return map;
  224 + }, {});
  225 + if (edgeAttributes.queueStartTs) {
  226 + scope.queueStartTs = edgeAttributes.queueStartTs.lastUpdateTs;
  227 + }
  228 + }
  229 +
  230 + scope.checkSubscription = function() {
  231 + var newSubscriptionId = null;
  232 + if (scope.entityId && scope.entityType && types.attributesScope.server.value) {
  233 + newSubscriptionId =
  234 + attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value);
  235 + }
  236 + if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {
  237 + attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
  238 + }
  239 + scope.subscriptionId = newSubscriptionId;
  240 + }
  241 +
  242 + scope.$on('$destroy', function () {
  243 + if (scope.subscriptionId) {
  244 + attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
  245 + }
  246 + });
  247 +
  248 + scope.reload();
  249 +
  250 + $compile(element.contents())(scope);
  251 + }
  252 + function prepareEdgeEventData(data) {
  253 +
  254 + data.forEach(
  255 + edgeEvent => {
  256 + edgeEvent.edgeEventActionText = $translate.instant(types.edgeEventActionType[edgeEvent.action].name);
  257 + edgeEvent.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeEvent.edgeId.entityType].name);
  258 + }
  259 + );
  260 + return data;
  261 + }
  262 +
  263 + return {
  264 + restrict: "E",
  265 + link: linker,
  266 + scope: {
  267 + entityType: '=',
  268 + entityId: '=',
  269 + tenantId: '='
  270 + }
  271 + };
  272 +}
  1 +<!--
  2 +
  3 + Copyright © 2016-2020 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 + <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow>
  21 + <md-button ng-disabled="$root.loading"
  22 + class="md-icon-button" ng-click="reload()">
  23 + <md-icon>refresh</md-icon>
  24 + <md-tooltip md-direction="top">
  25 + {{ 'action.refresh' | translate }}
  26 + </md-tooltip>
  27 + </md-button>
  28 + </section>
  29 + <md-list flex layout="column" class="md-whiteframe-z1 tb-edge-downlinks-table">
  30 + <md-list class="tb-row tb-header" layout="row" layout-align="start center" tb-event-header event-type="{{eventType}}">
  31 + </md-list>
  32 + <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading"
  33 + ng-show="$root.loading"></md-progress-linear>
  34 + <md-divider></md-divider>
  35 + <span translate layout-align="center center"
  36 + style="margin-top: 25px;"
  37 + class="tb-prompt" ng-show="noData()">event.no-events-prompt</span>
  38 + <md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer">
  39 + <md-list-item md-virtual-repeat="event in theEvents" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}">
  40 + <md-list class="tb-row" flex layout="row" layout-align="start center" tb-event-row event-type="{{eventType}}" event="{{event}}">
  41 + </md-list>
  42 + <md-divider flex></md-divider>
  43 + </md-list-item>
  44 + </md-virtual-repeat-container>
  45 + </md-list>
  46 +</md-content>
@@ -46,7 +46,7 @@ export default function EventHeaderDirective($compile, $templateCache, types) { @@ -46,7 +46,7 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
46 case types.debugEventType.debugRuleChain.value: 46 case types.debugEventType.debugRuleChain.value:
47 template = eventHeaderDebugRuleNodeTemplate; 47 template = eventHeaderDebugRuleNodeTemplate;
48 break; 48 break;
49 - case types.eventType.edgeEvent.value: 49 + case types.edgeDownlinks.value:
50 template = eventHeaderEdgeEventTemplate; 50 template = eventHeaderEdgeEventTemplate;
51 break; 51 break;
52 } 52 }
@@ -49,7 +49,7 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ @@ -49,7 +49,7 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
49 case types.debugEventType.debugRuleChain.value: 49 case types.debugEventType.debugRuleChain.value:
50 template = eventRowDebugRuleNodeTemplate; 50 template = eventRowDebugRuleNodeTemplate;
51 break; 51 break;
52 - case types.eventType.edgeEvent.value: 52 + case types.edgeDownlinks.value:
53 template = eventRowEdgeEventTemplate; 53 template = eventRowEdgeEventTemplate;
54 break; 54 break;
55 } 55 }
@@ -22,8 +22,7 @@ import eventTableTemplate from './event-table.tpl.html'; @@ -22,8 +22,7 @@ import eventTableTemplate from './event-table.tpl.html';
22 /* eslint-enable import/no-unresolved, import/default */ 22 /* eslint-enable import/no-unresolved, import/default */
23 23
24 /*@ngInject*/ 24 /*@ngInject*/
25 -export default function EventTableDirective($compile, $templateCache, $rootScope, $translate, types,  
26 - eventService, edgeService, attributeService) { 25 +export default function EventTableDirective($compile, $templateCache, $rootScope, types, eventService) {
27 26
28 var linker = function (scope, element, attrs) { 27 var linker = function (scope, element, attrs) {
29 28
@@ -31,16 +30,11 @@ export default function EventTableDirective($compile, $templateCache, $rootScope @@ -31,16 +30,11 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
31 30
32 element.html(template); 31 element.html(template);
33 32
34 - scope.eventTypeScope = angular.copy(types.eventType);  
35 - if (scope.entityType !== types.entityType.edge) {  
36 - delete scope.eventTypeScope.edgeEvent;  
37 - }  
38 -  
39 if (attrs.disabledEventTypes) { 33 if (attrs.disabledEventTypes) {
40 var disabledEventTypes = attrs.disabledEventTypes.split(','); 34 var disabledEventTypes = attrs.disabledEventTypes.split(',');
41 scope.eventTypes = {}; 35 scope.eventTypes = {};
42 - for (var type in scope.eventTypeScope) {  
43 - var eventType = scope.eventTypeScope[type]; 36 + for (var type in types.eventType) {
  37 + var eventType = types.eventType[type];
44 var enabled = true; 38 var enabled = true;
45 for (var i=0;i<disabledEventTypes.length;i++) { 39 for (var i=0;i<disabledEventTypes.length;i++) {
46 if (eventType.value === disabledEventTypes[i]) { 40 if (eventType.value === disabledEventTypes[i]) {
@@ -53,7 +47,7 @@ export default function EventTableDirective($compile, $templateCache, $rootScope @@ -53,7 +47,7 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
53 } 47 }
54 } 48 }
55 } else { 49 } else {
56 - scope.eventTypes = angular.copy(scope.eventTypeScope); 50 + scope.eventTypes = angular.copy(types.eventType);
57 } 51 }
58 52
59 if (attrs.debugEventTypes) { 53 if (attrs.debugEventTypes) {
@@ -106,23 +100,13 @@ export default function EventTableDirective($compile, $templateCache, $rootScope @@ -106,23 +100,13 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
106 fetchMoreItems_: function () { 100 fetchMoreItems_: function () {
107 if (scope.events.hasNext && !scope.events.pending) { 101 if (scope.events.hasNext && !scope.events.pending) {
108 if (scope.entityType && scope.entityId && scope.eventType && scope.tenantId) { 102 if (scope.entityType && scope.entityId && scope.eventType && scope.tenantId) {
109 - var promise = '';  
110 - if (scope.eventType !== types.eventType.edgeEvent.value) {  
111 - promise = eventService.getEvents(scope.entityType, scope.entityId,  
112 - scope.eventType, scope.tenantId, scope.events.nextPageLink);  
113 - } else {  
114 - promise = edgeService.getEdgeEvents(scope.entityId, scope.events.nextPageLink);  
115 - scope.loadEdgeInfo();  
116 - } 103 + var promise = eventService.getEvents(scope.entityType, scope.entityId,
  104 + scope.eventType, scope.tenantId, scope.events.nextPageLink);
117 if (promise) { 105 if (promise) {
118 scope.events.pending = true; 106 scope.events.pending = true;
119 promise.then( 107 promise.then(
120 function success(events) { 108 function success(events) {
121 - if (scope.eventType === types.eventType.edgeEvent.value) {  
122 - scope.events.data = scope.events.data.concat(prepareEdgeEventData(events.data));  
123 - } else {  
124 - scope.events.data = scope.events.data.concat(events.data);  
125 - } 109 + scope.events.data = scope.events.data.concat(events.data);
126 scope.events.nextPageLink = events.nextPageLink; 110 scope.events.nextPageLink = events.nextPageLink;
127 scope.events.hasNext = events.hasNext; 111 scope.events.hasNext = events.hasNext;
128 if (scope.events.hasNext) { 112 if (scope.events.hasNext) {
@@ -223,64 +207,10 @@ export default function EventTableDirective($compile, $templateCache, $rootScope @@ -223,64 +207,10 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
223 return false; 207 return false;
224 } 208 }
225 209
226 - scope.subscriptionId = null;  
227 -  
228 - scope.loadEdgeInfo = function() {  
229 - attributeService.getEntityAttributesValues(  
230 - scope.entityType,  
231 - scope.entityId,  
232 - types.attributesScope.server.value,  
233 - types.edgeAttributeKeys.queueStartTs,  
234 - null).then(  
235 - function success(attributes) {  
236 - attributes.length > 0 ? scope.onEdgeAttributesUpdate(attributes) : scope.queueStartTs = 0;  
237 - });  
238 - scope.checkSubscription();  
239 - }  
240 -  
241 - scope.onEdgeAttributesUpdate = function(attributes) {  
242 - let edgeAttributes = attributes.reduce(function (map, attribute) {  
243 - map[attribute.key] = attribute;  
244 - return map;  
245 - }, {});  
246 - if (edgeAttributes.queueStartTs) {  
247 - scope.queueStartTs = edgeAttributes.queueStartTs.lastUpdateTs;  
248 - }  
249 - }  
250 -  
251 - scope.checkSubscription = function() {  
252 - var newSubscriptionId = null;  
253 - if (scope.entityId && scope.entityType && types.attributesScope.server.value) {  
254 - newSubscriptionId =  
255 - attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value);  
256 - }  
257 - if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {  
258 - attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);  
259 - }  
260 - scope.subscriptionId = newSubscriptionId;  
261 - }  
262 -  
263 - scope.$on('$destroy', function () {  
264 - if (scope.subscriptionId) {  
265 - attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);  
266 - }  
267 - });  
268 -  
269 scope.reload(); 210 scope.reload();
270 211
271 $compile(element.contents())(scope); 212 $compile(element.contents())(scope);
272 } 213 }
273 -  
274 - function prepareEdgeEventData(data) {  
275 - data.forEach(  
276 - edgeEvent => {  
277 - edgeEvent.edgeEventActionText = $translate.instant(types.edgeEventActionType[edgeEvent.action].name);  
278 - edgeEvent.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeEvent.edgeId.entityType].name);  
279 - }  
280 - );  
281 - return data;  
282 - }  
283 -  
284 return { 214 return {
285 restrict: "E", 215 restrict: "E",
286 link: linker, 216 link: linker,
@@ -85,6 +85,78 @@ md-list.tb-event-table { @@ -85,6 +85,78 @@ md-list.tb-event-table {
85 } 85 }
86 } 86 }
87 87
  88 +md-list.tb-edge-downlinks-table {
  89 + padding: 0;
  90 +
  91 + md-list-item {
  92 + padding: 0;
  93 + }
  94 +
  95 + .tb-row {
  96 + height: 48px;
  97 + padding: 0;
  98 + overflow: hidden;
  99 +
  100 + .tb-cell {
  101 + text-overflow: ellipsis;
  102 +
  103 + &.tb-scroll {
  104 + overflow-x: auto;
  105 + overflow-y: hidden;
  106 + white-space: nowrap;
  107 + }
  108 +
  109 + &.tb-nowrap {
  110 + white-space: nowrap;
  111 + }
  112 + }
  113 + }
  114 +
  115 + .tb-row:hover {
  116 + background-color: #eee;
  117 + }
  118 +
  119 + .tb-header:hover {
  120 + background: none;
  121 + }
  122 +
  123 + .tb-header {
  124 + .tb-cell {
  125 + font-size: 12px;
  126 + font-weight: 700;
  127 + color: rgba(0, 0, 0, .54);
  128 + white-space: nowrap;
  129 + background: none;
  130 + }
  131 + }
  132 +
  133 + .tb-cell {
  134 + &:first-child {
  135 + padding-left: 14px;
  136 + }
  137 +
  138 + &:last-child {
  139 + padding-right: 14px;
  140 + }
  141 + padding: 0 6px;
  142 + margin: auto 0;
  143 + overflow: hidden;
  144 + font-size: 13px;
  145 + color: rgba(0, 0, 0, .87);
  146 + text-align: left;
  147 + vertical-align: middle;
  148 +
  149 + .md-button {
  150 + padding: 0;
  151 + margin: 0;
  152 + }
  153 + }
  154 +
  155 + .tb-cell.tb-number {
  156 + text-align: right;
  157 + }
  158 +}
  159 +
88 #tb-event-content { 160 #tb-event-content {
89 width: 100%; 161 width: 100%;
90 min-width: 400px; 162 min-width: 400px;
@@ -19,6 +19,7 @@ import EventContentDialogController from './event-content-dialog.controller'; @@ -19,6 +19,7 @@ import EventContentDialogController from './event-content-dialog.controller';
19 import EventHeaderDirective from './event-header.directive'; 19 import EventHeaderDirective from './event-header.directive';
20 import EventRowDirective from './event-row.directive'; 20 import EventRowDirective from './event-row.directive';
21 import EventTableDirective from './event-table.directive'; 21 import EventTableDirective from './event-table.directive';
  22 +import EdgeDownlinksDirective from "./edge-downlinks-table.directive";
22 23
23 export default angular.module('thingsboard.event', [ 24 export default angular.module('thingsboard.event', [
24 thingsboardApiEvent 25 thingsboardApiEvent
@@ -27,4 +28,5 @@ export default angular.module('thingsboard.event', [ @@ -27,4 +28,5 @@ export default angular.module('thingsboard.event', [
27 .directive('tbEventHeader', EventHeaderDirective) 28 .directive('tbEventHeader', EventHeaderDirective)
28 .directive('tbEventRow', EventRowDirective) 29 .directive('tbEventRow', EventRowDirective)
29 .directive('tbEventTable', EventTableDirective) 30 .directive('tbEventTable', EventTableDirective)
  31 + .directive('tbEdgeDownlinksTable', EdgeDownlinksDirective)
30 .name; 32 .name;
@@ -856,7 +856,9 @@ @@ -856,7 +856,9 @@
856 "license-key-hint": "To obtain your license please navigate to the <a href='https://thingsboard.io/pricing/?active=thingsboard-edge' target='_blank'>pricing page</a> and select the best license option for your case.", 856 "license-key-hint": "To obtain your license please navigate to the <a href='https://thingsboard.io/pricing/?active=thingsboard-edge' target='_blank'>pricing page</a> and select the best license option for your case.",
857 "cloud-endpoint-hint": "Edge requires HTTP(s) access to Cloud (ThingsBoard CE/PE) to verify the license key. Please specify Cloud URL that Edge is able to connect to.", 857 "cloud-endpoint-hint": "Edge requires HTTP(s) access to Cloud (ThingsBoard CE/PE) to verify the license key. Please specify Cloud URL that Edge is able to connect to.",
858 "missing-related-rule-chains-title": "Edge has missing related rule chain(s)", 858 "missing-related-rule-chains-title": "Edge has missing related rule chain(s)",
859 - "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge. <br><br> List of missing rule chain(s): <br> {{missingRuleChains}}" 859 + "missing-related-rule-chains-text": "Assigned to edge rule chain(s) use rule nodes that forward message(s) to rule chain(s) that are not assigned to this edge. <br><br> List of missing rule chain(s): <br> {{missingRuleChains}}",
  860 + "downlinks": "Downlinks",
  861 + "no-downlinks-prompt": "No downlinks found"
860 }, 862 },
861 "error": { 863 "error": {
862 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", 864 "unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
@@ -1097,7 +1099,6 @@ @@ -1097,7 +1099,6 @@
1097 "type-debug-rule-chain": "Debug", 1099 "type-debug-rule-chain": "Debug",
1098 "no-events-prompt": "No events found", 1100 "no-events-prompt": "No events found",
1099 "error": "Error", 1101 "error": "Error",
1100 - "type-edge-event": "Downlink",  
1101 "alarm": "Alarm", 1102 "alarm": "Alarm",
1102 "event-time": "Event time", 1103 "event-time": "Event time",
1103 "server": "Server", 1104 "server": "Server",