Showing
11 changed files
with
421 additions
and
87 deletions
... | ... | @@ -35,7 +35,7 @@ function EdgeService($http, $q, customerService) { |
35 | 35 | unassignEdgeFromCustomer: unassignEdgeFromCustomer, |
36 | 36 | makeEdgePublic: makeEdgePublic, |
37 | 37 | setRootRuleChain: setRootRuleChain, |
38 | - getEdgeEvents: getEdgeEvents, | |
38 | + getEdgeDownlinks: getEdgeDownlinks, | |
39 | 39 | syncEdge: syncEdge, |
40 | 40 | findMissingToRelatedRuleChains: findMissingToRelatedRuleChains |
41 | 41 | }; |
... | ... | @@ -276,7 +276,7 @@ function EdgeService($http, $q, customerService) { |
276 | 276 | return deferred.promise; |
277 | 277 | } |
278 | 278 | |
279 | - function getEdgeEvents(edgeId, pageLink) { | |
279 | + function getEdgeDownlinks(edgeId, pageLink) { | |
280 | 280 | var deferred = $q.defer(); |
281 | 281 | var url = '/api/edge/' + edgeId + '/events' + '?limit=' + pageLink.limit; |
282 | 282 | if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) { | ... | ... |
... | ... | @@ -661,10 +661,6 @@ export default angular.module('thingsboard.types', []) |
661 | 661 | stats: { |
662 | 662 | value: "STATS", |
663 | 663 | name: "event.type-stats" |
664 | - }, | |
665 | - edgeEvent: { | |
666 | - value: "EDGE_EVENT", | |
667 | - name: "event.type-edge-event" | |
668 | 664 | } |
669 | 665 | }, |
670 | 666 | debugEventType: { |
... | ... | @@ -1197,6 +1193,14 @@ export default angular.module('thingsboard.types', []) |
1197 | 1193 | "ADMIN_SETTINGS": { |
1198 | 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 | 1206 | ).name; | ... | ... |
... | ... | @@ -65,6 +65,13 @@ |
65 | 65 | default-event-type="{{vm.types.eventType.error.value}}"> |
66 | 66 | </tb-event-table> |
67 | 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 | 75 | <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}"> |
69 | 76 | <tb-relation-table flex |
70 | 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 | 46 | case types.debugEventType.debugRuleChain.value: |
47 | 47 | template = eventHeaderDebugRuleNodeTemplate; |
48 | 48 | break; |
49 | - case types.eventType.edgeEvent.value: | |
49 | + case types.edgeDownlinks.value: | |
50 | 50 | template = eventHeaderEdgeEventTemplate; |
51 | 51 | break; |
52 | 52 | } | ... | ... |
... | ... | @@ -49,7 +49,7 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ |
49 | 49 | case types.debugEventType.debugRuleChain.value: |
50 | 50 | template = eventRowDebugRuleNodeTemplate; |
51 | 51 | break; |
52 | - case types.eventType.edgeEvent.value: | |
52 | + case types.edgeDownlinks.value: | |
53 | 53 | template = eventRowEdgeEventTemplate; |
54 | 54 | break; |
55 | 55 | } | ... | ... |
... | ... | @@ -22,8 +22,7 @@ import eventTableTemplate from './event-table.tpl.html'; |
22 | 22 | /* eslint-enable import/no-unresolved, import/default */ |
23 | 23 | |
24 | 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 | 27 | var linker = function (scope, element, attrs) { |
29 | 28 | |
... | ... | @@ -31,16 +30,11 @@ export default function EventTableDirective($compile, $templateCache, $rootScope |
31 | 30 | |
32 | 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 | 33 | if (attrs.disabledEventTypes) { |
40 | 34 | var disabledEventTypes = attrs.disabledEventTypes.split(','); |
41 | 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 | 38 | var enabled = true; |
45 | 39 | for (var i=0;i<disabledEventTypes.length;i++) { |
46 | 40 | if (eventType.value === disabledEventTypes[i]) { |
... | ... | @@ -53,7 +47,7 @@ export default function EventTableDirective($compile, $templateCache, $rootScope |
53 | 47 | } |
54 | 48 | } |
55 | 49 | } else { |
56 | - scope.eventTypes = angular.copy(scope.eventTypeScope); | |
50 | + scope.eventTypes = angular.copy(types.eventType); | |
57 | 51 | } |
58 | 52 | |
59 | 53 | if (attrs.debugEventTypes) { |
... | ... | @@ -106,23 +100,13 @@ export default function EventTableDirective($compile, $templateCache, $rootScope |
106 | 100 | fetchMoreItems_: function () { |
107 | 101 | if (scope.events.hasNext && !scope.events.pending) { |
108 | 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 | 105 | if (promise) { |
118 | 106 | scope.events.pending = true; |
119 | 107 | promise.then( |
120 | 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 | 110 | scope.events.nextPageLink = events.nextPageLink; |
127 | 111 | scope.events.hasNext = events.hasNext; |
128 | 112 | if (scope.events.hasNext) { |
... | ... | @@ -223,64 +207,10 @@ export default function EventTableDirective($compile, $templateCache, $rootScope |
223 | 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 | 210 | scope.reload(); |
270 | 211 | |
271 | 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 | 214 | return { |
285 | 215 | restrict: "E", |
286 | 216 | link: linker, | ... | ... |
... | ... | @@ -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 | 160 | #tb-event-content { |
89 | 161 | width: 100%; |
90 | 162 | min-width: 400px; | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import EventContentDialogController from './event-content-dialog.controller'; |
19 | 19 | import EventHeaderDirective from './event-header.directive'; |
20 | 20 | import EventRowDirective from './event-row.directive'; |
21 | 21 | import EventTableDirective from './event-table.directive'; |
22 | +import EdgeDownlinksDirective from "./edge-downlinks-table.directive"; | |
22 | 23 | |
23 | 24 | export default angular.module('thingsboard.event', [ |
24 | 25 | thingsboardApiEvent |
... | ... | @@ -27,4 +28,5 @@ export default angular.module('thingsboard.event', [ |
27 | 28 | .directive('tbEventHeader', EventHeaderDirective) |
28 | 29 | .directive('tbEventRow', EventRowDirective) |
29 | 30 | .directive('tbEventTable', EventTableDirective) |
31 | + .directive('tbEdgeDownlinksTable', EdgeDownlinksDirective) | |
30 | 32 | .name; | ... | ... |
... | ... | @@ -856,7 +856,9 @@ |
856 | 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 | 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 | 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 | 863 | "error": { |
862 | 864 | "unable-to-connect": "Unable to connect to the server! Please check your internet connection.", |
... | ... | @@ -1097,7 +1099,6 @@ |
1097 | 1099 | "type-debug-rule-chain": "Debug", |
1098 | 1100 | "no-events-prompt": "No events found", |
1099 | 1101 | "error": "Error", |
1100 | - "type-edge-event": "Downlink", | |
1101 | 1102 | "alarm": "Alarm", |
1102 | 1103 | "event-time": "Event time", |
1103 | 1104 | "server": "Server", | ... | ... |