diff --git a/ui/src/app/api/edge.service.js b/ui/src/app/api/edge.service.js
index debd49069a..809b0b12af 100644
--- a/ui/src/app/api/edge.service.js
+++ b/ui/src/app/api/edge.service.js
@@ -35,7 +35,7 @@ function EdgeService($http, $q, customerService) {
unassignEdgeFromCustomer: unassignEdgeFromCustomer,
makeEdgePublic: makeEdgePublic,
setRootRuleChain: setRootRuleChain,
- getEdgeEvents: getEdgeEvents,
+ getEdgeDownlinks: getEdgeDownlinks,
syncEdge: syncEdge,
findMissingToRelatedRuleChains: findMissingToRelatedRuleChains
};
@@ -276,7 +276,7 @@ function EdgeService($http, $q, customerService) {
return deferred.promise;
}
- function getEdgeEvents(edgeId, pageLink) {
+ function getEdgeDownlinks(edgeId, pageLink) {
var deferred = $q.defer();
var url = '/api/edge/' + edgeId + '/events' + '?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.startTime) && pageLink.startTime != null) {
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 7dbc10de6d..0bdb4fae71 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -661,10 +661,6 @@ export default angular.module('thingsboard.types', [])
stats: {
value: "STATS",
name: "event.type-stats"
- },
- edgeEvent: {
- value: "EDGE_EVENT",
- name: "event.type-edge-event"
}
},
debugEventType: {
@@ -1197,6 +1193,14 @@ export default angular.module('thingsboard.types', [])
"ADMIN_SETTINGS": {
name: "permission.resource.display-type.ADMIN_SETTINGS"
}
+ },
+ edgeEvent: {
+ value: "EDGE_EVENT",
+ name: "edge.downlink"
+ },
+ edgeDownlinks: {
+ value: "EDGE_DOWNLINKS",
+ name: "edge.downlinks"
}
}
).name;
diff --git a/ui/src/app/edge/edges.tpl.html b/ui/src/app/edge/edges.tpl.html
index 2600413255..016634fea9 100644
--- a/ui/src/app/edge/edges.tpl.html
+++ b/ui/src/app/edge/edges.tpl.html
@@ -65,6 +65,13 @@
default-event-type="{{vm.types.eventType.error.value}}">
+
+
+
+
scope.events.data.length) {
+ scope.theEvents.fetchMoreItems_(index);
+ return null;
+ }
+ var item = scope.events.data[index];
+ if (item) {
+ item.indexNumber = index + 1;
+ }
+ return item;
+ },
+
+ getLength: function () {
+ if (scope.events.hasNext) {
+ return scope.events.data.length + scope.events.nextPageLink.limit;
+ } else {
+ return scope.events.data.length;
+ }
+ },
+
+ fetchMoreItems_: function () {
+ if (scope.events.hasNext && !scope.events.pending) {
+ if (scope.entityType && scope.entityId && scope.eventType && scope.tenantId) {
+ scope.loadEdgeInfo();
+ scope.events.pending = true;
+ edgeService.getEdgeDownlinks(scope.entityId, scope.events.nextPageLink).then(
+ function success(events) {
+ scope.events.data = scope.events.data.concat(prepareEdgeEventData(events.data));
+ scope.events.nextPageLink = events.nextPageLink;
+ scope.events.hasNext = events.hasNext;
+ if (scope.events.hasNext) {
+ scope.events.nextPageLink.limit = pageSize;
+ }
+ scope.events.pending = false;
+ },
+ function fail() {
+ scope.events.hasNext = false;
+ scope.events.pending = false;
+ });
+ } else {
+ scope.events.hasNext = false;
+ }
+ }
+ }
+ };
+
+ scope.$watch("entityId", function(newVal, prevVal) {
+ if (newVal && !angular.equals(newVal, prevVal)) {
+ scope.resetFilter();
+ scope.reload();
+ }
+ });
+
+ scope.$watch("eventType", function(newVal, prevVal) {
+ if (newVal && !angular.equals(newVal, prevVal)) {
+ scope.reload();
+ }
+ });
+
+ scope.$watch("timewindow", function(newVal, prevVal) {
+ if (newVal && !angular.equals(newVal, prevVal)) {
+ scope.reload();
+ }
+ }, true);
+
+ scope.resetFilter = function() {
+ scope.timewindow = {
+ history: {
+ timewindowMs: 24 * 60 * 60 * 1000 // 1 day
+ }
+ };
+ }
+
+ scope.updateTimeWindowRange = function() {
+ 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;
+ }
+ }
+
+ scope.reload = function() {
+ scope.topIndex = 0;
+ scope.selected = [];
+ scope.updateTimeWindowRange();
+ scope.events = {
+ data: [],
+ nextPageLink: {
+ limit: pageSize,
+ startTime: startTime,
+ endTime: endTime
+ },
+ hasNext: true,
+ pending: false
+ };
+ scope.theEvents.getItemAtIndex(pageSize);
+ }
+
+ scope.noData = function() {
+ return scope.events.data.length == 0 && !scope.events.hasNext;
+ }
+
+ scope.hasData = function() {
+ return scope.events.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;
+ }
+
+ scope.subscriptionId = null;
+
+ scope.loadEdgeInfo = function() {
+ attributeService.getEntityAttributesValues(
+ scope.entityType,
+ scope.entityId,
+ types.attributesScope.server.value,
+ types.edgeAttributeKeys.queueStartTs,
+ null).then(
+ function success(attributes) {
+ attributes.length > 0 ? scope.onEdgeAttributesUpdate(attributes) : scope.queueStartTs = 0;
+ });
+ scope.checkSubscription();
+ }
+
+ scope.onEdgeAttributesUpdate = function(attributes) {
+ let edgeAttributes = attributes.reduce(function (map, attribute) {
+ map[attribute.key] = attribute;
+ return map;
+ }, {});
+ if (edgeAttributes.queueStartTs) {
+ scope.queueStartTs = edgeAttributes.queueStartTs.lastUpdateTs;
+ }
+ }
+
+ scope.checkSubscription = function() {
+ var newSubscriptionId = null;
+ if (scope.entityId && scope.entityType && types.attributesScope.server.value) {
+ newSubscriptionId =
+ attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value);
+ }
+ if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {
+ attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
+ }
+ scope.subscriptionId = newSubscriptionId;
+ }
+
+ scope.$on('$destroy', function () {
+ if (scope.subscriptionId) {
+ attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
+ }
+ });
+
+ scope.reload();
+
+ $compile(element.contents())(scope);
+ }
+ function prepareEdgeEventData(data) {
+
+ data.forEach(
+ edgeEvent => {
+ edgeEvent.edgeEventActionText = $translate.instant(types.edgeEventActionType[edgeEvent.action].name);
+ edgeEvent.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeEvent.edgeId.entityType].name);
+ }
+ );
+ return data;
+ }
+
+ return {
+ restrict: "E",
+ link: linker,
+ scope: {
+ entityType: '=',
+ entityId: '=',
+ tenantId: '='
+ }
+ };
+}
diff --git a/ui/src/app/event/edge-downlinks-table.tpl.html b/ui/src/app/event/edge-downlinks-table.tpl.html
new file mode 100644
index 0000000000..e0281a2cf0
--- /dev/null
+++ b/ui/src/app/event/edge-downlinks-table.tpl.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+ refresh
+
+ {{ 'action.refresh' | translate }}
+
+
+
+
+
+
+
+ event.no-events-prompt
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/app/event/event-header.directive.js b/ui/src/app/event/event-header.directive.js
index efe3dab21c..ac5e968745 100644
--- a/ui/src/app/event/event-header.directive.js
+++ b/ui/src/app/event/event-header.directive.js
@@ -46,7 +46,7 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
case types.debugEventType.debugRuleChain.value:
template = eventHeaderDebugRuleNodeTemplate;
break;
- case types.eventType.edgeEvent.value:
+ case types.edgeDownlinks.value:
template = eventHeaderEdgeEventTemplate;
break;
}
diff --git a/ui/src/app/event/event-row.directive.js b/ui/src/app/event/event-row.directive.js
index 77718a5968..b4d20fd72a 100644
--- a/ui/src/app/event/event-row.directive.js
+++ b/ui/src/app/event/event-row.directive.js
@@ -49,7 +49,7 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
case types.debugEventType.debugRuleChain.value:
template = eventRowDebugRuleNodeTemplate;
break;
- case types.eventType.edgeEvent.value:
+ case types.edgeDownlinks.value:
template = eventRowEdgeEventTemplate;
break;
}
diff --git a/ui/src/app/event/event-table.directive.js b/ui/src/app/event/event-table.directive.js
index ba41da8c34..7537c808f3 100644
--- a/ui/src/app/event/event-table.directive.js
+++ b/ui/src/app/event/event-table.directive.js
@@ -22,8 +22,7 @@ import eventTableTemplate from './event-table.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
-export default function EventTableDirective($compile, $templateCache, $rootScope, $translate, types,
- eventService, edgeService, attributeService) {
+export default function EventTableDirective($compile, $templateCache, $rootScope, types, eventService) {
var linker = function (scope, element, attrs) {
@@ -31,16 +30,11 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
element.html(template);
- scope.eventTypeScope = angular.copy(types.eventType);
- if (scope.entityType !== types.entityType.edge) {
- delete scope.eventTypeScope.edgeEvent;
- }
-
if (attrs.disabledEventTypes) {
var disabledEventTypes = attrs.disabledEventTypes.split(',');
scope.eventTypes = {};
- for (var type in scope.eventTypeScope) {
- var eventType = scope.eventTypeScope[type];
+ for (var type in types.eventType) {
+ var eventType = types.eventType[type];
var enabled = true;
for (var i=0;i 0 ? scope.onEdgeAttributesUpdate(attributes) : scope.queueStartTs = 0;
- });
- scope.checkSubscription();
- }
-
- scope.onEdgeAttributesUpdate = function(attributes) {
- let edgeAttributes = attributes.reduce(function (map, attribute) {
- map[attribute.key] = attribute;
- return map;
- }, {});
- if (edgeAttributes.queueStartTs) {
- scope.queueStartTs = edgeAttributes.queueStartTs.lastUpdateTs;
- }
- }
-
- scope.checkSubscription = function() {
- var newSubscriptionId = null;
- if (scope.entityId && scope.entityType && types.attributesScope.server.value) {
- newSubscriptionId =
- attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value);
- }
- if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) {
- attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
- }
- scope.subscriptionId = newSubscriptionId;
- }
-
- scope.$on('$destroy', function () {
- if (scope.subscriptionId) {
- attributeService.unsubscribeForEntityAttributes(scope.subscriptionId);
- }
- });
-
scope.reload();
$compile(element.contents())(scope);
}
-
- function prepareEdgeEventData(data) {
- data.forEach(
- edgeEvent => {
- edgeEvent.edgeEventActionText = $translate.instant(types.edgeEventActionType[edgeEvent.action].name);
- edgeEvent.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeEvent.edgeId.entityType].name);
- }
- );
- return data;
- }
-
return {
restrict: "E",
link: linker,
diff --git a/ui/src/app/event/event.scss b/ui/src/app/event/event.scss
index f1b5eb0d46..ffdb4217bd 100644
--- a/ui/src/app/event/event.scss
+++ b/ui/src/app/event/event.scss
@@ -85,6 +85,78 @@ md-list.tb-event-table {
}
}
+md-list.tb-edge-downlinks-table {
+ padding: 0;
+
+ md-list-item {
+ padding: 0;
+ }
+
+ .tb-row {
+ height: 48px;
+ padding: 0;
+ overflow: hidden;
+
+ .tb-cell {
+ text-overflow: ellipsis;
+
+ &.tb-scroll {
+ overflow-x: auto;
+ overflow-y: hidden;
+ white-space: nowrap;
+ }
+
+ &.tb-nowrap {
+ white-space: nowrap;
+ }
+ }
+ }
+
+ .tb-row:hover {
+ background-color: #eee;
+ }
+
+ .tb-header:hover {
+ background: none;
+ }
+
+ .tb-header {
+ .tb-cell {
+ font-size: 12px;
+ font-weight: 700;
+ color: rgba(0, 0, 0, .54);
+ white-space: nowrap;
+ background: none;
+ }
+ }
+
+ .tb-cell {
+ &:first-child {
+ padding-left: 14px;
+ }
+
+ &:last-child {
+ padding-right: 14px;
+ }
+ padding: 0 6px;
+ margin: auto 0;
+ overflow: hidden;
+ font-size: 13px;
+ color: rgba(0, 0, 0, .87);
+ text-align: left;
+ vertical-align: middle;
+
+ .md-button {
+ padding: 0;
+ margin: 0;
+ }
+ }
+
+ .tb-cell.tb-number {
+ text-align: right;
+ }
+}
+
#tb-event-content {
width: 100%;
min-width: 400px;
diff --git a/ui/src/app/event/index.js b/ui/src/app/event/index.js
index 69227eef8b..370f31a17b 100644
--- a/ui/src/app/event/index.js
+++ b/ui/src/app/event/index.js
@@ -19,6 +19,7 @@ import EventContentDialogController from './event-content-dialog.controller';
import EventHeaderDirective from './event-header.directive';
import EventRowDirective from './event-row.directive';
import EventTableDirective from './event-table.directive';
+import EdgeDownlinksDirective from "./edge-downlinks-table.directive";
export default angular.module('thingsboard.event', [
thingsboardApiEvent
@@ -27,4 +28,5 @@ export default angular.module('thingsboard.event', [
.directive('tbEventHeader', EventHeaderDirective)
.directive('tbEventRow', EventRowDirective)
.directive('tbEventTable', EventTableDirective)
+ .directive('tbEdgeDownlinksTable', EdgeDownlinksDirective)
.name;
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index 56f45586c3..781ceced3f 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -856,7 +856,9 @@
"license-key-hint": "To obtain your license please navigate to the pricing page and select the best license option for your case.",
"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.",
"missing-related-rule-chains-title": "Edge has missing related rule chain(s)",
- "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.
List of missing rule chain(s):
{{missingRuleChains}}"
+ "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.
List of missing rule chain(s):
{{missingRuleChains}}",
+ "downlinks": "Downlinks",
+ "no-downlinks-prompt": "No downlinks found"
},
"error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
@@ -1097,7 +1099,6 @@
"type-debug-rule-chain": "Debug",
"no-events-prompt": "No events found",
"error": "Error",
- "type-edge-event": "Downlink",
"alarm": "Alarm",
"event-time": "Event time",
"server": "Server",