diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index e43dd9aa8a..375adad109 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: {
@@ -1077,126 +1073,126 @@ export default angular.module('thingsboard.types', [])
translate: {
customTranslationsPrefix: "custom."
},
- edgeEventActionType: {
+ edgeEventActionTypeTranslations: {
"ADDED": {
- name: "audit-log.type-added"
+ name: "edge-event.action-type-added"
},
"DELETED": {
- name: "audit-log.type-deleted"
+ name: "edge-event.action-type-deleted"
},
"UPDATED": {
- name: "audit-log.type-updated"
+ name: "edge-event.action-type-updated"
+ },
+ "POST_ATTRIBUTES": {
+ name: "edge-event.action-type-post-attributes"
+ },
+ "TIMESERIES_UPDATED": {
+ name: "edge-event.action-type-timeseries-updated"
},
"ATTRIBUTES_UPDATED": {
- name: "audit-log.type-attributes-updated"
+ name: "edge-event.action-type-attributes-updated"
},
"ATTRIBUTES_DELETED": {
- name: "audit-log.type-attributes-deleted"
- },
- "RPC_CALL": {
- name: "audit-log.type-rpc-call"
+ name: "edge-event.action-type-attributes-deleted"
},
"CREDENTIALS_UPDATED": {
- name: "audit-log.type-credentials-updated"
+ name: "edge-event.action-type-credentials-updated"
+ },
+ "ASSIGNED_TO_CUSTOMER": {
+ name: "edge-event.action-type-assigned-to-customer"
+ },
+ "UNASSIGNED_FROM_CUSTOMER": {
+ name: "edge-event.action-type-unassigned-from-customer"
},
"RELATION_ADD_OR_UPDATE": {
- name: "audit-log.type-relation-add-or-update"
+ name: "edge-event.action-type-relation-add-or-update"
},
"RELATION_DELETED": {
- name: "audit-log.type-relation-delete"
+ name: "edge-event.action-type-relation-deleted"
+ },
+ "RPC_CALL": {
+ name: "edge-event.action-type-rpc-call"
},
"ALARM_ACK": {
- name: "audit-log.type-alarm-ack"
+ name: "edge-event.action-type-alarm-ack"
},
"ALARM_CLEAR": {
- name: "audit-log.type-alarm-clear"
+ name: "edge-event.action-type-alarm-clear"
},
"CREDENTIALS_REQUEST": {
- name: "edge.credentials-request"
- },
- "ATTRIBUTES_REQUEST": {
- name: "edge.attributes-request"
- },
- "TIMESERIES_UPDATED": {
- name: "edge.timeseries-updated"
- },
- "RELATION_REQUEST": {
- name: "edge.relation-request"
- },
- "RULE_CHAIN_METADATA_REQUEST": {
- name: "edge.rule-chain-metadata-request"
- },
- "GROUP_ENTITIES_REQUEST": {
- name: "edge.group_entities_request"
- },
- "GROUP_PERMISSIONS_REQUEST": {
- name: "edge.group-permissions-request"
+ name: "edge-event.action-type-credentials-request"
},
"ASSIGNED_TO_EDGE": {
- name: "audit-log.type-assigned-to-edge"
+ name: "edge-event.action-type-assigned-to-edge"
},
"UNASSIGNED_FROM_EDGE": {
- name: "audit-log.type-unassigned-from-edge"
+ name: "edge-event.action-type-unassigned-from-edge"
+ },
+ "ENTITY_MERGE_REQUEST": {
+ name: "edge-event.action-type-entity-merge-request"
}
},
edgeEventTypeTranslations:{
"DASHBOARD": {
- name: "entity.type-dashboard"
+ name: "edge-event.type-dashboard"
},
"ASSET": {
- name: "entity.type-asset"
+ name: "edge-event.type-asset"
},
"DEVICE": {
- name: "entity.type-device"
+ name: "edge-event.type-device"
},
"ENTITY_VIEW": {
- name: "entity.type-entity_view"
+ name: "edge-event.type-entity-view"
},
"ALARM": {
- name: "entity.type-alarm"
+ name: "edge-event.type-alarm"
},
"RULE_CHAIN": {
- name: "entity.type-rulechain"
+ name: "edge-event.type-rule-chain"
},
- "RULE_CHAIN_META_DATA": {
- name: "entity.type-rule-chain-metadata"
+ "RULE_CHAIN_METADATA": {
+ name: "edge-event.type-rule-chain-metadata"
},
"EDGE": {
- name: "entity.type-edge"
+ name: "edge-event.type-edge"
},
"USER": {
- name: "entity.type-user"
+ name: "edge-event.type-user"
+ },
+ "TENANT": {
+ name: "edge-event.type-tenant"
},
"CUSTOMER": {
- name: "entity.type-customer"
+ name: "edge-event.type-customer"
},
"RELATION": {
- name: "entity.type-relation"
+ name: "edge-event.type-relation"
+ },
+ "WIDGETS_BUNDLE": {
+ name: "edge-event.type-widgets-bundle"
+ },
+ "WIDGET_TYPE": {
+ name: "edge-event.type-widgets-type"
+ },
+ "ADMIN_SETTINGS": {
+ name: "edge-event.type-admin-settings"
},
"ENTITY_GROUP": {
- name: "entity.type-entity-group"
+ name: "edge-event.type-entity-group"
},
"SCHEDULER_EVENT": {
- name: "entity.type-scheduler-event"
+ name: "edge-event.type-scheduler-event"
},
"WHITE_LABELING": {
- name: "white-labeling.white-labeling"
+ name: "edge-event.type-white-labeling"
},
"LOGIN_WHITE_LABELING": {
- name: "white-labeling.login-white-labeling"
+ name: "edge-event.type-login-white-labeling"
},
"CUSTOM_TRANSLATION": {
- name: "custom-translation.custom-translation",
- },
- "WIDGETS_BUNDLE": {
- name: "widget.widget-bundle"
- },
- "WIDGET_TYPE": {
- name: "widget.type"
- },
- "ADMIN_SETTINGS": {
- name: "permission.resource.display-type.ADMIN_SETTINGS"
+ name: "edge-event.custom-translation",
}
- },
+ }
}
).name;
diff --git a/ui/src/app/edge/downlinks/downlink.scss b/ui/src/app/edge/downlinks/downlink.scss
new file mode 100644
index 0000000000..9f975fc1e1
--- /dev/null
+++ b/ui/src/app/edge/downlinks/downlink.scss
@@ -0,0 +1,93 @@
+/**
+ * Copyright © 2016-2020 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+md-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-edge-downlinks-content {
+ width: 100%;
+ min-width: 400px;
+ height: 100%;
+ min-height: 50px;
+}
diff --git a/ui/src/app/edge/downlinks/edge-downlinks-content-dialog.controller.js b/ui/src/app/edge/downlinks/edge-downlinks-content-dialog.controller.js
new file mode 100644
index 0000000000..432bdf4d50
--- /dev/null
+++ b/ui/src/app/edge/downlinks/edge-downlinks-content-dialog.controller.js
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2016-2020 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 $ from 'jquery';
+import 'brace/ext/language_tools';
+import 'brace/ext/searchbox';
+import 'brace/mode/java';
+import 'brace/theme/github';
+import beautify from 'js-beautify';
+
+/* eslint-disable angular/angularelement */
+
+const js_beautify = beautify.js;
+
+/*@ngInject*/
+export default function EdgeDownlinksContentDialogController($mdDialog, types, content, contentType, title, showingCallback) {
+
+ var vm = this;
+
+ showingCallback.onShowing = function(scope, element) {
+ updateEditorSize(element);
+ }
+
+ vm.content = content;
+ vm.title = title;
+
+ var mode;
+ if (contentType) {
+ mode = types.contentType[contentType].code;
+ if (contentType == types.contentType.JSON.value && vm.content) {
+ vm.content = js_beautify(vm.content, {indent_size: 4});
+ }
+ } else {
+ mode = 'java';
+ }
+
+ vm.contentOptions = {
+ useWrapMode: false,
+ mode: mode,
+ showGutter: false,
+ showPrintMargin: false,
+ theme: 'github',
+ advanced: {
+ enableSnippets: false,
+ enableBasicAutocompletion: false,
+ enableLiveAutocompletion: false
+ },
+ onLoad: function (_ace) {
+ vm.editor = _ace;
+ }
+ };
+
+ function updateEditorSize(element) {
+ var newHeight = 400;
+ var newWidth = 600;
+ if (vm.content && vm.content.length > 0) {
+ var lines = vm.content.split('\n');
+ newHeight = 16 * lines.length + 16;
+ var maxLineLength = 0;
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i].replace(/\t/g, ' ').replace(/\n/g, '');
+ var lineLength = line.length;
+ maxLineLength = Math.max(maxLineLength, lineLength);
+ }
+ newWidth = 8 * maxLineLength + 16;
+ }
+ $('#tb-edge-downlinks-content', element).height(newHeight.toString() + "px")
+ .width(newWidth.toString() + "px");
+ vm.editor.resize();
+ }
+
+ vm.close = close;
+
+ function close () {
+ $mdDialog.hide();
+ }
+
+}
+
+/* eslint-enable angular/angularelement */
diff --git a/ui/src/app/edge/downlinks/edge-downlinks-content-dialog.tpl.html b/ui/src/app/edge/downlinks/edge-downlinks-content-dialog.tpl.html
new file mode 100644
index 0000000000..798990ec07
--- /dev/null
+++ b/ui/src/app/edge/downlinks/edge-downlinks-content-dialog.tpl.html
@@ -0,0 +1,42 @@
+
+
+
+
+
{{ vm.title }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'action.close' |
+ translate }}
+
+
+
diff --git a/ui/src/app/edge/downlinks/edge-downlinks-header.directive.js b/ui/src/app/edge/downlinks/edge-downlinks-header.directive.js
new file mode 100644
index 0000000000..3af243ca48
--- /dev/null
+++ b/ui/src/app/edge/downlinks/edge-downlinks-header.directive.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2016-2020 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 edgeDownlinksHeaderTemplate from './edge-downlinks-header.tpl.html'
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function EdgeDownlinksHeaderDirective($compile, $templateCache) {
+
+ var linker = function (scope, element) {
+
+ var template = edgeDownlinksHeaderTemplate;
+
+ element.html($templateCache.get(template));
+ $compile(element.contents())(scope);
+ }
+
+ return {
+ restrict: "A",
+ replace: false,
+ link: linker,
+ scope: false
+ };
+}
diff --git a/ui/src/app/event/event-header-edge-event.tpl.html b/ui/src/app/edge/downlinks/edge-downlinks-header.tpl.html
similarity index 81%
rename from ui/src/app/event/event-header-edge-event.tpl.html
rename to ui/src/app/edge/downlinks/edge-downlinks-header.tpl.html
index 3de281ddc7..e08f688142 100644
--- a/ui/src/app/event/event-header-edge-event.tpl.html
+++ b/ui/src/app/edge/downlinks/edge-downlinks-header.tpl.html
@@ -16,8 +16,8 @@
-->
event.event-time
-event.event-type
+event.type
edge.event-action
edge.entity-id
-edge.status
-edge.entity-info
+event.status
+event.data
diff --git a/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js b/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js
new file mode 100644
index 0000000000..16015d12db
--- /dev/null
+++ b/ui/src/app/edge/downlinks/edge-downlinks-row.directive.js
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2016-2020 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 edgeDownlinksContentTemplate from './edge-downlinks-content-dialog.tpl.html';
+import edgeDownlinlsRowTemplate from './edge-downlinks-row.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function EdgeDownlinksRowDirective($compile, $templateCache, $mdDialog, $document, $translate,
+ types, utils, toast, entityService, ruleChainService) {
+
+ var linker = function (scope, element, attrs) {
+
+ var template = edgeDownlinlsRowTemplate;
+
+ element.html($templateCache.get(template));
+ $compile(element.contents())(scope);
+
+ scope.types = types;
+ scope.downlink = attrs.downlink;
+
+ scope.showEdgeEntityContent = function($event, title, contentType) {
+ var onShowingCallback = {
+ onShowing: function(){}
+ }
+ if (!contentType) {
+ contentType = null;
+ }
+ var content = '';
+ switch(scope.downlink.type) {
+ case types.edgeEventType.relation:
+ content = angular.toJson(scope.downlink.body);
+ showDialog();
+ break;
+ case types.edgeEventType.ruleChainMetaData:
+ content = ruleChainService.getRuleChainMetaData(scope.downlink.entityId, {ignoreErrors: true}).then(
+ function success(info) {
+ showDialog();
+ return angular.toJson(info);
+ }, function fail() {
+ showError();
+ });
+ break;
+ default:
+ content = entityService.getEntity(scope.downlink.type, scope.downlink.entityId, {ignoreErrors: true}).then(
+ function success(info) {
+ showDialog();
+ return angular.toJson(info);
+ }, function fail() {
+ showError();
+ });
+ break;
+ }
+ function showDialog() {
+ $mdDialog.show({
+ controller: 'EdgeDownlinksContentDialogController',
+ controllerAs: 'vm',
+ templateUrl: edgeDownlinksContentTemplate,
+ locals: {content: content, title: title, contentType: contentType, showingCallback: onShowingCallback},
+ parent: angular.element($document[0].body),
+ fullscreen: true,
+ targetEvent: $event,
+ multiple: true,
+ onShowing: function(scope, element) {
+ onShowingCallback.onShowing(scope, element);
+ }
+ });
+ }
+ function showError() {
+ toast.showError($translate.instant('edge.load-entity-error'));
+ }
+ }
+
+ scope.checkEdgeDownlinksType = function (type) {
+ return !(type === types.edgeEventType.widgetType ||
+ type === types.edgeEventType.adminSettings ||
+ type === types.edgeEventType.widgetsBundle );
+ }
+
+ scope.checkTooltip = function($event) {
+ var el = $event.target;
+ var $el = angular.element(el);
+ if(el.offsetWidth < el.scrollWidth && !$el.attr('title')){
+ $el.attr('title', $el.text());
+ }
+ }
+
+ $compile(element.contents())(scope);
+
+ scope.updateStatus = function(downlinkCreatedTime) {
+ var status;
+ if (downlinkCreatedTime < scope.queueStartTs) {
+ status = $translate.instant(types.edgeEventStatus.DEPLOYED.name);
+ scope.statusColor = types.edgeEventStatus.DEPLOYED.color;
+ } else {
+ status = $translate.instant(types.edgeEventStatus.PENDING.name);
+ scope.statusColor = types.edgeEventStatus.PENDING.color;
+ }
+ return status;
+ }
+ }
+
+ return {
+ restrict: "A",
+ replace: false,
+ link: linker,
+ scope: false
+ };
+}
diff --git a/ui/src/app/event/event-row-edge-event.tpl.html b/ui/src/app/edge/downlinks/edge-downlinks-row.tpl.html
similarity index 68%
rename from ui/src/app/event/event-row-edge-event.tpl.html
rename to ui/src/app/edge/downlinks/edge-downlinks-row.tpl.html
index a842b5d787..7fe595bc8c 100644
--- a/ui/src/app/event/event-row-edge-event.tpl.html
+++ b/ui/src/app/edge/downlinks/edge-downlinks-row.tpl.html
@@ -15,13 +15,13 @@
limitations under the License.
-->
-{{ event.createdTime | date : 'yyyy-MM-dd HH:mm:ss' }}
-{{ event.edgeEventTypeText }}
-{{ event.edgeEventActionText }}
-{{ event.entityId }}
-{{ updateStatus(event.createdTime) }}
+{{ downlink.createdTime | date : 'yyyy-MM-dd HH:mm:ss' }}
+{{ downlink.edgeEventTypeText }}
+{{ downlink.edgeEventActionText }}
+{{ downlink.entityId }}
+{{ updateStatus(downlink.createdTime) }}
-
diff --git a/ui/src/app/edge/downlinks/edge-downlinks-table.directive.js b/ui/src/app/edge/downlinks/edge-downlinks-table.directive.js
new file mode 100644
index 0000000000..98bd63abb0
--- /dev/null
+++ b/ui/src/app/edge/downlinks/edge-downlinks-table.directive.js
@@ -0,0 +1,230 @@
+/*
+ * Copyright © 2016-2020 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 './downlink.scss';
+
+/* eslint-disable import/no-unresolved, import/default */
+
+import edgeDownlinksTableTemplate from './edge-downlinks-table.tpl.html';
+
+/* eslint-enable import/no-unresolved, import/default */
+
+/*@ngInject*/
+export default function EdgeDownlinksDirective($compile, $templateCache, $rootScope, $translate, types, edgeService, attributeService) {
+
+ var linker = function (scope, element) {
+
+ var template = $templateCache.get(edgeDownlinksTableTemplate);
+ element.html(template);
+
+ var pageSize = 20;
+ var startTime = 0;
+ var endTime = 0;
+
+ scope.timewindow = {
+ history: {
+ timewindowMs: 24 * 60 * 60 * 1000 // 1 day
+ }
+ }
+
+ scope.topIndex = 0;
+
+ scope.theDownlinks = {
+ getItemAtIndex: function (index) {
+ if (index > scope.downlinks.data.length) {
+ scope.theDownlinks.fetchMoreItems_(index);
+ return null;
+ }
+ var item = scope.downlinks.data[index];
+ if (item) {
+ item.indexNumber = index + 1;
+ }
+ return item;
+ },
+
+ getLength: function () {
+ if (scope.downlinks.hasNext) {
+ return scope.downlinks.data.length + scope.downlinks.nextPageLink.limit;
+ } else {
+ return scope.downlinks.data.length;
+ }
+ },
+
+ fetchMoreItems_: function () {
+ if (scope.downlinks.hasNext && !scope.downlinks.pending) {
+ if (scope.entityType && scope.entityId && scope.tenantId) {
+ scope.loadEdgeInfo();
+ scope.downlinks.pending = true;
+ edgeService.getEdgeEvents(scope.entityId, scope.downlinks.nextPageLink).then(
+ function success(downlinks) {
+ scope.downlinks.data = scope.downlinks.data.concat(prepareEdgeDownlinksData(downlinks.data));
+ scope.downlinks.nextPageLink = downlinks.nextPageLink;
+ scope.downlinks.hasNext = downlinks.hasNext;
+ if (scope.downlinks.hasNext) {
+ scope.downlinks.nextPageLink.limit = pageSize;
+ }
+ scope.downlinks.pending = false;
+ },
+ function fail() {
+ scope.downlinks.hasNext = false;
+ scope.downlinks.pending = false;
+ });
+ } else {
+ scope.downlinks.hasNext = false;
+ }
+ }
+ }
+ };
+
+ scope.$watch("entityId", function(newVal, prevVal) {
+ if (newVal && !angular.equals(newVal, prevVal)) {
+ scope.resetFilter();
+ 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.downlinks = {
+ data: [],
+ nextPageLink: {
+ limit: pageSize,
+ startTime: startTime,
+ endTime: endTime
+ },
+ hasNext: true,
+ pending: false
+ };
+ scope.theDownlinks.getItemAtIndex(pageSize);
+ }
+
+ scope.noData = function() {
+ return scope.downlinks.data.length == 0 && !scope.downlinks.hasNext;
+ }
+
+ scope.hasData = function() {
+ return scope.downlinks.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 prepareEdgeDownlinksData(data) {
+
+ data.forEach(
+ edgeDownlink => {
+ edgeDownlink.edgeEventActionText = $translate.instant(types.edgeEventActionTypeTranslations[edgeDownlink.action].name);
+ edgeDownlink.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeDownlink.type].name);
+ }
+ );
+ return data;
+ }
+
+ return {
+ restrict: "E",
+ link: linker,
+ scope: {
+ entityType: '=',
+ entityId: '=',
+ tenantId: '='
+ }
+ };
+}
diff --git a/ui/src/app/edge/downlinks/edge-downlinks-table.tpl.html b/ui/src/app/edge/downlinks/edge-downlinks-table.tpl.html
new file mode 100644
index 0000000000..0e66b2b685
--- /dev/null
+++ b/ui/src/app/edge/downlinks/edge-downlinks-table.tpl.html
@@ -0,0 +1,46 @@
+
+
+
+
+
+ refresh
+
+ {{ 'action.refresh' | translate }}
+
+
+
+
+
+
+
+ edge.no-downlinks-prompt
+
+
+
+
+
+
+
+
+
diff --git a/ui/src/app/edge/edges.tpl.html b/ui/src/app/edge/edges.tpl.html
index 2600413255..ea40c80b06 100644
--- a/ui/src/app/edge/edges.tpl.html
+++ b/ui/src/app/edge/edges.tpl.html
@@ -65,6 +65,12 @@
default-event-type="{{vm.types.eventType.error.value}}">
+
+
+
+
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/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json
index 6c69c25910..1f495ccdf0 100644
--- a/ui/src/app/locale/locale.constant-de_DE.json
+++ b/ui/src/app/locale/locale.constant-de_DE.json
@@ -808,7 +808,6 @@
"deployed": "Bereitgestellt",
"pending": "Steht aus",
"entity-id": "Entität ID",
- "entity-info": "Entitätsinfo",
"event-action": "Ereignisaktion",
"load-entity-error": "Entität nicht gefunden. Fehler beim Laden der Informationen",
"unassign-edges-text": "Nach der Bestätigung werden alle ausgewählten Kanten nicht zugewiesen und sind für den Kunden nicht zugänglich.",
diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json
index 56f45586c3..c9522365b6 100644
--- a/ui/src/app/locale/locale.constant-en_US.json
+++ b/ui/src/app/locale/locale.constant-en_US.json
@@ -788,7 +788,6 @@
"cloud-endpoint": "Cloud Endpoint",
"cloud-endpoint-required": "Cloud Endpoint is required.",
"description": "Description",
- "entity-info": "Entity info",
"details": "Details",
"events": "Events",
"copy-id": "Copy Edge Id",
@@ -845,7 +844,6 @@
"set-root-rule-chain-text": "Please select root rule chain for edge(s)",
"set-root-rule-chain-to-edges": "Set root rule chain for Edge(s)",
"set-root-rule-chain-to-edges-text": "Set root rule chain for { count, plural, 1 {1 edge} other {# edges} }",
- "status": "Received by edge",
"deployed": "Deployed",
"pending": "Pending",
"unassign-edges-action-title": "Unassign { count, plural, 1 {1 edge} other {# edges} } from customer",
@@ -856,7 +854,51 @@
"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"
+ },
+ "edge-event": {
+ "type-dashboard": "Dashboard",
+ "type-asset": "Asset",
+ "type-device": "Device",
+ "type-device-profile": "Device Profile",
+ "type-entity-view": "Entity View",
+ "type-alarm": "Alar",
+ "type-rule-chain": "Rule Chain",
+ "type-rule-chain-metadata": "Rule Chain Metadata",
+ "type-edge": "Edge",
+ "type-entity-group": "Entity Group",
+ "type-scheduler-event": "Scheduler Event",
+ "type-white-labeling": "White Labeling",
+ "type-login-white-labeling": "White Labeling Login",
+ "type-user": "User",
+ "type-tenant": "Tenant",
+ "type-customer": "Customer",
+ "type-custom-translation": "Custom Translation",
+ "type-relation": "Relation",
+ "type-widgets-bundle": "Widgets Bundle",
+ "type-widgets-type": "Widgets Type",
+ "type-admin-settings": "Admin Settings",
+ "action-type-added": "Added",
+ "action-type-deleted": "Deleted",
+ "action-type-updated": "Updated",
+ "action-type-post-attributes": "Post Attributes",
+ "action-type-attributes-updated": "Attributes Updated",
+ "action-type-attributes-deleted": "Attributes Deleted",
+ "action-type-timeseries-updated": "Timeseries Updated",
+ "action-type-credentials-updated": "Credentials Updated",
+ "action-type-assigned-to-customer": "Assigned to Customer",
+ "action-type-unassigned-from-customer": "Unassigned from Customer",
+ "action-type-relation-add-or-update": "Relation Add or Update",
+ "action-type-relation-deleted": "Relation Deleted",
+ "action-type-rpc-call": "RPC Call",
+ "action-type-alarm-ack": "Alarm Ack",
+ "action-type-alarm-clear": "Alarm Clear",
+ "action-type-assigned-to-edge": "Assigned to Edge",
+ "action-type-unassigned-from-edge": "Unassigned from Edge",
+ "action-type-credentials-request": "Credentials Request",
+ "action-type-entity-merge-request": "Entity Merge Request"
},
"error": {
"unable-to-connect": "Unable to connect to the server! Please check your internet connection.",
@@ -1097,7 +1139,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",
diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json
index 301e292de2..07acd04e3a 100644
--- a/ui/src/app/locale/locale.constant-es_ES.json
+++ b/ui/src/app/locale/locale.constant-es_ES.json
@@ -821,7 +821,6 @@
"deployed": "Desplegada",
"pending": "Pendiente",
"entity-id": "ID de entidad",
- "entity-info": "Entity info",
"event-action": "Información de la entidad",
"load-entity-error": "Entidad no encontrada. No se pudo cargar la información",
"unassign-edges-text": "Después de la confirmación de todos los bordes seleccionados, se anulará la asignación y el cliente no podrá acceder a ellos.",