|
|
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 './edges-hierarchy-widget.scss';
|
|
|
17
|
+
|
|
|
18
|
+/* eslint-disable import/no-unresolved, import/default */
|
|
|
19
|
+
|
|
|
20
|
+import edgesHierarchyWidgetTemplate from './edges-hierarchy-widget.tpl.html';
|
|
|
21
|
+
|
|
|
22
|
+/* eslint-enable import/no-unresolved, import/default */
|
|
|
23
|
+
|
|
|
24
|
+export default angular.module('thingsboard.widgets.edgesHierarchyWidget', [])
|
|
|
25
|
+ .directive('tbEdgesHierarchyWidget', EdgesHierarchyWidget)
|
|
|
26
|
+ .name;
|
|
|
27
|
+/* eslint-disable no-unused-vars */
|
|
|
28
|
+/*@ngInject*/
|
|
|
29
|
+function EdgesHierarchyWidget() {
|
|
|
30
|
+ return {
|
|
|
31
|
+ restrict: "E",
|
|
|
32
|
+ scope: true,
|
|
|
33
|
+ bindToController: {
|
|
|
34
|
+ hierarchyId: '=',
|
|
|
35
|
+ ctx: '='
|
|
|
36
|
+ },
|
|
|
37
|
+ controller: EdgesHierarchyWidgetController,
|
|
|
38
|
+ controllerAs: 'vm',
|
|
|
39
|
+ templateUrl: edgesHierarchyWidgetTemplate
|
|
|
40
|
+ };
|
|
|
41
|
+}
|
|
|
42
|
+
|
|
|
43
|
+/*@ngInject*/
|
|
|
44
|
+function EdgesHierarchyWidgetController($element, $scope, $q, $timeout, toast, types, entityService, entityRelationService,
|
|
|
45
|
+ assetService, deviceService, entityViewService, dashboardService, ruleChainService /*$filter, $mdMedia, $mdPanel, $document, $translate, $timeout, utils, types*/) {
|
|
|
46
|
+ var vm = this;
|
|
|
47
|
+
|
|
|
48
|
+ vm.showData = true;
|
|
|
49
|
+
|
|
|
50
|
+ vm.nodeEditCallbacks = {};
|
|
|
51
|
+
|
|
|
52
|
+ vm.nodeIdCounter = 0;
|
|
|
53
|
+
|
|
|
54
|
+ vm.nodesMap = {};
|
|
|
55
|
+ vm.pendingUpdateNodeTasks = {};
|
|
|
56
|
+ vm.edgeGroupsNodesMap = {};
|
|
|
57
|
+
|
|
|
58
|
+ vm.query = {
|
|
|
59
|
+ search: null
|
|
|
60
|
+ };
|
|
|
61
|
+
|
|
|
62
|
+ vm.searchAction = {
|
|
|
63
|
+ name: 'action.search',
|
|
|
64
|
+ show: true,
|
|
|
65
|
+ onAction: function() {
|
|
|
66
|
+ vm.enterFilterMode();
|
|
|
67
|
+ },
|
|
|
68
|
+ icon: 'search'
|
|
|
69
|
+ };
|
|
|
70
|
+
|
|
|
71
|
+ vm.onNodesInserted = onNodesInserted;
|
|
|
72
|
+ vm.onNodeSelected = onNodeSelected;
|
|
|
73
|
+ vm.enterFilterMode = enterFilterMode;
|
|
|
74
|
+ vm.exitFilterMode = exitFilterMode;
|
|
|
75
|
+ vm.searchCallback = searchCallback;
|
|
|
76
|
+
|
|
|
77
|
+ $scope.$watch('vm.ctx', function() {
|
|
|
78
|
+ if (vm.ctx && vm.ctx.defaultSubscription) {
|
|
|
79
|
+ vm.settings = vm.ctx.settings;
|
|
|
80
|
+ vm.widgetConfig = vm.ctx.widgetConfig;
|
|
|
81
|
+ vm.subscription = vm.ctx.defaultSubscription;
|
|
|
82
|
+ vm.datasources = vm.subscription.datasources;
|
|
|
83
|
+ initializeConfig();
|
|
|
84
|
+ updateDatasources();
|
|
|
85
|
+ }
|
|
|
86
|
+ });
|
|
|
87
|
+
|
|
|
88
|
+ $scope.$watch("vm.query.search", function(newVal, prevVal) {
|
|
|
89
|
+ if (!angular.equals(newVal, prevVal) && vm.query.search != null) {
|
|
|
90
|
+ updateSearchNodes();
|
|
|
91
|
+ }
|
|
|
92
|
+ });
|
|
|
93
|
+
|
|
|
94
|
+ $scope.$on('edges-hierarchy-data-updated', function(event, hierarchyId) {
|
|
|
95
|
+ if (vm.hierarchyId == hierarchyId) {
|
|
|
96
|
+ if (vm.subscription) {
|
|
|
97
|
+ updateNodeData(vm.subscription.data);
|
|
|
98
|
+ }
|
|
|
99
|
+ }
|
|
|
100
|
+ });
|
|
|
101
|
+
|
|
|
102
|
+ function initializeConfig() {
|
|
|
103
|
+
|
|
|
104
|
+ vm.ctx.widgetActions = [ vm.searchAction ];
|
|
|
105
|
+
|
|
|
106
|
+ var testNodeCtx = {
|
|
|
107
|
+ entity: {
|
|
|
108
|
+ id: {
|
|
|
109
|
+ entityType: 'DEVICE',
|
|
|
110
|
+ id: '123'
|
|
|
111
|
+ },
|
|
|
112
|
+ name: 'TEST DEV1'
|
|
|
113
|
+ },
|
|
|
114
|
+ data: {},
|
|
|
115
|
+ level: 2
|
|
|
116
|
+ };
|
|
|
117
|
+ var parentNodeCtx = angular.copy(testNodeCtx);
|
|
|
118
|
+ parentNodeCtx.level = 1;
|
|
|
119
|
+ testNodeCtx.parentNodeCtx = parentNodeCtx;
|
|
|
120
|
+
|
|
|
121
|
+ var nodeRelationQueryFunction = loadNodeCtxFunction(vm.settings.nodeRelationQueryFunction, 'nodeCtx', testNodeCtx);
|
|
|
122
|
+ var nodeIconFunction = loadNodeCtxFunction(vm.settings.nodeIconFunction, 'nodeCtx', testNodeCtx);
|
|
|
123
|
+ var nodeTextFunction = loadNodeCtxFunction(vm.settings.nodeTextFunction, 'nodeCtx', testNodeCtx);
|
|
|
124
|
+ var nodeDisabledFunction = loadNodeCtxFunction(vm.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx);
|
|
|
125
|
+ var nodeOpenedFunction = loadNodeCtxFunction(vm.settings.nodeOpenedFunction, 'nodeCtx', testNodeCtx);
|
|
|
126
|
+ var nodeHasChildrenFunction = loadNodeCtxFunction(vm.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx);
|
|
|
127
|
+
|
|
|
128
|
+ var testNodeCtx2 = angular.copy(testNodeCtx);
|
|
|
129
|
+ testNodeCtx2.entity.name = 'TEST DEV2';
|
|
|
130
|
+
|
|
|
131
|
+ var nodesSortFunction = loadNodeCtxFunction(vm.settings.nodesSortFunction, 'nodeCtx1,nodeCtx2', testNodeCtx, testNodeCtx2);
|
|
|
132
|
+
|
|
|
133
|
+ vm.nodeRelationQueryFunction = nodeRelationQueryFunction || defaultNodeRelationQueryFunction;
|
|
|
134
|
+ vm.nodeIconFunction = nodeIconFunction || defaultNodeIconFunction;
|
|
|
135
|
+ vm.nodeTextFunction = nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name);
|
|
|
136
|
+ vm.nodeDisabledFunction = nodeDisabledFunction || (() => false);
|
|
|
137
|
+ vm.nodeOpenedFunction = nodeOpenedFunction || defaultNodeOpenedFunction;
|
|
|
138
|
+ vm.nodeHasChildrenFunction = nodeHasChildrenFunction || (() => true);
|
|
|
139
|
+ vm.nodesSortFunction = nodesSortFunction || defaultSortFunction;
|
|
|
140
|
+ }
|
|
|
141
|
+
|
|
|
142
|
+ function loadNodeCtxFunction(functionBody, argNames, ...args) {
|
|
|
143
|
+ var nodeCtxFunction = null;
|
|
|
144
|
+ if (angular.isDefined(functionBody) && functionBody.length) {
|
|
|
145
|
+ try {
|
|
|
146
|
+ nodeCtxFunction = new Function(argNames, functionBody);
|
|
|
147
|
+ var res = nodeCtxFunction.apply(null, args);
|
|
|
148
|
+ if (angular.isUndefined(res)) {
|
|
|
149
|
+ nodeCtxFunction = null;
|
|
|
150
|
+ }
|
|
|
151
|
+ } catch (e) {
|
|
|
152
|
+ nodeCtxFunction = null;
|
|
|
153
|
+ }
|
|
|
154
|
+ }
|
|
|
155
|
+ return nodeCtxFunction;
|
|
|
156
|
+ }
|
|
|
157
|
+
|
|
|
158
|
+ function enterFilterMode () {
|
|
|
159
|
+ vm.query.search = '';
|
|
|
160
|
+ vm.ctx.hideTitlePanel = true;
|
|
|
161
|
+ $timeout(()=>{
|
|
|
162
|
+ angular.element(vm.ctx.$container).find('.searchInput').focus();
|
|
|
163
|
+ })
|
|
|
164
|
+ }
|
|
|
165
|
+
|
|
|
166
|
+ function exitFilterMode () {
|
|
|
167
|
+ vm.query.search = null;
|
|
|
168
|
+ updateSearchNodes();
|
|
|
169
|
+ vm.ctx.hideTitlePanel = false;
|
|
|
170
|
+ }
|
|
|
171
|
+
|
|
|
172
|
+ function searchCallback (searchText, node) {
|
|
|
173
|
+ var theNode = vm.nodesMap[node.id];
|
|
|
174
|
+ if (theNode && theNode.data.searchText) {
|
|
|
175
|
+ return theNode.data.searchText.includes(searchText.toLowerCase());
|
|
|
176
|
+ }
|
|
|
177
|
+ return false;
|
|
|
178
|
+ }
|
|
|
179
|
+
|
|
|
180
|
+ function updateDatasources() {
|
|
|
181
|
+ vm.loadNodes = loadNodes;
|
|
|
182
|
+ }
|
|
|
183
|
+
|
|
|
184
|
+ function updateSearchNodes() {
|
|
|
185
|
+ if (vm.query.search != null) {
|
|
|
186
|
+ vm.nodeEditCallbacks.search(vm.query.search);
|
|
|
187
|
+ } else {
|
|
|
188
|
+ vm.nodeEditCallbacks.clearSearch();
|
|
|
189
|
+ }
|
|
|
190
|
+ }
|
|
|
191
|
+
|
|
|
192
|
+ function onNodesInserted(nodes/*, parent*/) {
|
|
|
193
|
+ if (nodes) {
|
|
|
194
|
+ nodes.forEach((nodeId) => {
|
|
|
195
|
+ var task = vm.pendingUpdateNodeTasks[nodeId];
|
|
|
196
|
+ if (task) {
|
|
|
197
|
+ task();
|
|
|
198
|
+ delete vm.pendingUpdateNodeTasks[nodeId];
|
|
|
199
|
+ }
|
|
|
200
|
+ });
|
|
|
201
|
+ }
|
|
|
202
|
+ }
|
|
|
203
|
+
|
|
|
204
|
+ function onNodeSelected(node, event) {
|
|
|
205
|
+ var nodeId;
|
|
|
206
|
+ if (!node) {
|
|
|
207
|
+ nodeId = -1;
|
|
|
208
|
+ } else {
|
|
|
209
|
+ nodeId = node.id;
|
|
|
210
|
+ }
|
|
|
211
|
+ if (nodeId !== -1) {
|
|
|
212
|
+ var selectedNode = vm.nodesMap[nodeId];
|
|
|
213
|
+ if (selectedNode) {
|
|
|
214
|
+ var descriptors = vm.ctx.actionsApi.getActionDescriptors('nodeSelected');
|
|
|
215
|
+ if (descriptors.length) {
|
|
|
216
|
+ var entity = selectedNode.data.nodeCtx.entity;
|
|
|
217
|
+ vm.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx });
|
|
|
218
|
+ }
|
|
|
219
|
+ }
|
|
|
220
|
+ }
|
|
|
221
|
+ }
|
|
|
222
|
+
|
|
|
223
|
+ function updateNodeData(subscriptionData) {
|
|
|
224
|
+ var affectedNodes = [];
|
|
|
225
|
+ if (subscriptionData) {
|
|
|
226
|
+ for (var i=0;i<subscriptionData.length;i++) {
|
|
|
227
|
+ var datasource = subscriptionData[i].datasource;
|
|
|
228
|
+ if (datasource.nodeId) {
|
|
|
229
|
+ var node = vm.nodesMap[datasource.nodeId];
|
|
|
230
|
+ var key = subscriptionData[i].dataKey.label;
|
|
|
231
|
+ var value = undefined;
|
|
|
232
|
+ if (subscriptionData[i].data && subscriptionData[i].data.length) {
|
|
|
233
|
+ value = subscriptionData[i].data[0][1];
|
|
|
234
|
+ }
|
|
|
235
|
+ if (node.data.nodeCtx.data[key] !== value) {
|
|
|
236
|
+ if (affectedNodes.indexOf(datasource.nodeId) === -1) {
|
|
|
237
|
+ affectedNodes.push(datasource.nodeId);
|
|
|
238
|
+ }
|
|
|
239
|
+ node.data.nodeCtx.data[key] = value;
|
|
|
240
|
+ }
|
|
|
241
|
+ }
|
|
|
242
|
+ }
|
|
|
243
|
+ }
|
|
|
244
|
+ affectedNodes.forEach((nodeId) => {
|
|
|
245
|
+ var node = vm.nodeEditCallbacks.getNode(nodeId);
|
|
|
246
|
+ if (node) {
|
|
|
247
|
+ updateNodeStyle(vm.nodesMap[nodeId]);
|
|
|
248
|
+ } else {
|
|
|
249
|
+ vm.pendingUpdateNodeTasks[nodeId] = () => {
|
|
|
250
|
+ updateNodeStyle(vm.nodesMap[nodeId]);
|
|
|
251
|
+ };
|
|
|
252
|
+ }
|
|
|
253
|
+ });
|
|
|
254
|
+ }
|
|
|
255
|
+
|
|
|
256
|
+ function updateNodeStyle(node) {
|
|
|
257
|
+ var newText = prepareNodeText(node);
|
|
|
258
|
+ if (!angular.equals(node.text, newText)) {
|
|
|
259
|
+ node.text = newText;
|
|
|
260
|
+ vm.nodeEditCallbacks.updateNode(node.id, node.text);
|
|
|
261
|
+ }
|
|
|
262
|
+ var newDisabled = vm.nodeDisabledFunction(node.data.nodeCtx);
|
|
|
263
|
+ if (!angular.equals(node.state.disabled, newDisabled)) {
|
|
|
264
|
+ node.state.disabled = newDisabled;
|
|
|
265
|
+ if (node.state.disabled) {
|
|
|
266
|
+ vm.nodeEditCallbacks.disableNode(node.id);
|
|
|
267
|
+ } else {
|
|
|
268
|
+ vm.nodeEditCallbacks.enableNode(node.id);
|
|
|
269
|
+ }
|
|
|
270
|
+ }
|
|
|
271
|
+ var newHasChildren = vm.nodeHasChildrenFunction(node.data.nodeCtx);
|
|
|
272
|
+ if (!angular.equals(node.children, newHasChildren)) {
|
|
|
273
|
+ node.children = newHasChildren;
|
|
|
274
|
+ vm.nodeEditCallbacks.setNodeHasChildren(node.id, node.children);
|
|
|
275
|
+ }
|
|
|
276
|
+ }
|
|
|
277
|
+
|
|
|
278
|
+ function prepareNodeText(node) {
|
|
|
279
|
+ var nodeIcon = prepareNodeIcon(node.data.nodeCtx);
|
|
|
280
|
+ var nodeText = vm.nodeTextFunction(node.data.nodeCtx);
|
|
|
281
|
+ node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : "";
|
|
|
282
|
+ return nodeIcon + nodeText;
|
|
|
283
|
+ }
|
|
|
284
|
+
|
|
|
285
|
+ function loadNodes(node, cb) {
|
|
|
286
|
+ if (node.id === '#') {
|
|
|
287
|
+ var tasks = [];
|
|
|
288
|
+ for (var i=0;i<vm.datasources.length;i++) {
|
|
|
289
|
+ var datasource = vm.datasources[i];
|
|
|
290
|
+ tasks.push(datasourceToNode(datasource));
|
|
|
291
|
+ }
|
|
|
292
|
+ $q.all(tasks).then((nodes) => {
|
|
|
293
|
+ cb(prepareNodes(nodes));
|
|
|
294
|
+ updateNodeData(vm.subscription.data);
|
|
|
295
|
+ });
|
|
|
296
|
+ } else {
|
|
|
297
|
+ if (node.data && node.data.nodeCtx.entity && node.data.nodeCtx.entity.id && node.data.nodeCtx.entity.id.entityType !== 'function') {
|
|
|
298
|
+ if (node.data.nodeCtx.entity.id.entityType === types.entityType.edge) {
|
|
|
299
|
+ /* assetService.getEdgeAssets(node.data.nodeCtx.entity.id.id, {limit: 20}, null).then(
|
|
|
300
|
+ (entities) => {
|
|
|
301
|
+ var tasks = [];
|
|
|
302
|
+ for (var i=0;i<entities.data.length;i++) {
|
|
|
303
|
+ var relation = entities.data[i];
|
|
|
304
|
+ var targetId = node.data.nodeCtx.entity.id.entityType === types.entityType.edge ? relation.id : node.data.nodeCtx.entity.id;
|
|
|
305
|
+ tasks.push(entityIdToNode(targetId.entityType, targetId.id, node.data.datasource, node.data.nodeCtx));
|
|
|
306
|
+ }
|
|
|
307
|
+ $q.all(tasks).then((nodes) => {
|
|
|
308
|
+ cb(prepareNodes(nodes));
|
|
|
309
|
+ });
|
|
|
310
|
+ }
|
|
|
311
|
+ );
|
|
|
312
|
+ deviceService.getEdgeDevices(node.data.nodeCtx.entity.id.id, {limit: 20}, null).then(
|
|
|
313
|
+ (entities) => {
|
|
|
314
|
+ var tasks = [];
|
|
|
315
|
+ for (var i=0;i<entities.data.length;i++) {
|
|
|
316
|
+ var relation = entities.data[i];
|
|
|
317
|
+ var targetId = node.data.nodeCtx.entity.id.entityType === types.entityType.edge ? relation.id : node.data.nodeCtx.entity.id;
|
|
|
318
|
+ tasks.push(entityIdToNode(targetId.entityType, targetId.id, node.data.datasource, node.data.nodeCtx));
|
|
|
319
|
+ }
|
|
|
320
|
+ $q.all(tasks).then((nodes) => {
|
|
|
321
|
+ cb(prepareNodes(nodes));
|
|
|
322
|
+ });
|
|
|
323
|
+ }
|
|
|
324
|
+ );
|
|
|
325
|
+ entityViewService.getEdgeEntityViews(node.data.nodeCtx.entity.id.id, {limit: 20}, null).then(
|
|
|
326
|
+ (entities) => {
|
|
|
327
|
+ var tasks = [];
|
|
|
328
|
+ for (var i=0;i<entities.data.length;i++) {
|
|
|
329
|
+ var relation = entities.data[i];
|
|
|
330
|
+ var targetId = node.data.nodeCtx.entity.id.entityType === types.entityType.edge ? relation.id : node.data.nodeCtx.entity.id;
|
|
|
331
|
+ tasks.push(entityIdToNode(targetId.entityType, targetId.id, node.data.datasource, node.data.nodeCtx));
|
|
|
332
|
+ }
|
|
|
333
|
+ $q.all(tasks).then((nodes) => {
|
|
|
334
|
+ cb(prepareNodes(nodes));
|
|
|
335
|
+ });
|
|
|
336
|
+ }
|
|
|
337
|
+ );
|
|
|
338
|
+ dashboardService.getEdgeDashboards(node.data.nodeCtx.entity.id.id, {limit: 20}, null).then(
|
|
|
339
|
+ (entities) => {
|
|
|
340
|
+ var tasks = [];
|
|
|
341
|
+ for (var i=0;i<entities.data.length;i++) {
|
|
|
342
|
+ var relation = entities.data[i];
|
|
|
343
|
+ var targetId = node.data.nodeCtx.entity.id.entityType === types.entityType.edge ? relation.id : node.data.nodeCtx.entity.id;
|
|
|
344
|
+ tasks.push(entityIdToNode(targetId.entityType, targetId.id, node.data.datasource, node.data.nodeCtx));
|
|
|
345
|
+ }
|
|
|
346
|
+ $q.all(tasks).then((nodes) => {
|
|
|
347
|
+ cb(prepareNodes(nodes));
|
|
|
348
|
+ });
|
|
|
349
|
+ }
|
|
|
350
|
+ )
|
|
|
351
|
+ ruleChainService.getEdgeRuleChains(node.data.nodeCtx.entity.id.id, {limit: 20}, null).then(
|
|
|
352
|
+ (entities) => {
|
|
|
353
|
+ var tasks = [];
|
|
|
354
|
+ for (var i=0;i<entities.data.length;i++) {
|
|
|
355
|
+ var relation = entities.data[i];
|
|
|
356
|
+ var targetId = node.data.nodeCtx.entity.id.entityType === types.entityType.edge ? relation.id : node.data.nodeCtx.entity.id;
|
|
|
357
|
+ tasks.push(entityIdToNode(targetId.entityType, targetId.id, node.data.datasource, node.data.nodeCtx));
|
|
|
358
|
+ }
|
|
|
359
|
+ $q.all(tasks).then((nodes) => {
|
|
|
360
|
+ cb(prepareNodes(nodes));
|
|
|
361
|
+ });
|
|
|
362
|
+ }
|
|
|
363
|
+ )
|
|
|
364
|
+ */
|
|
|
365
|
+
|
|
|
366
|
+ entityIdToNodeEdge("edgeGroup", "001", node.data.datasource, )
|
|
|
367
|
+
|
|
|
368
|
+ } else {
|
|
|
369
|
+ var relationQuery = prepareNodeRelationQuery(node.data.nodeCtx);
|
|
|
370
|
+ entityRelationService.findByQuery(relationQuery, {ignoreErrors: true, ignoreLoading: true}).then(
|
|
|
371
|
+ (entityRelations) => {
|
|
|
372
|
+ var tasks = [];
|
|
|
373
|
+ for (var i=0;i<entityRelations.length;i++) {
|
|
|
374
|
+ var relation = entityRelations[i];
|
|
|
375
|
+ var targetId = relationQuery.parameters.direction === types.entitySearchDirection.from ? relation.to : relation.from;
|
|
|
376
|
+ tasks.push(entityIdToNode(targetId.entityType, targetId.id, node.data.datasource, node.data.nodeCtx));
|
|
|
377
|
+ }
|
|
|
378
|
+ $q.all(tasks).then((nodes) => {
|
|
|
379
|
+ cb(prepareNodes(nodes));
|
|
|
380
|
+ });
|
|
|
381
|
+ },
|
|
|
382
|
+ (error) => {
|
|
|
383
|
+ var errorText = "Failed to get relations!";
|
|
|
384
|
+ if (error && error.status === 400) {
|
|
|
385
|
+ errorText = "Invalid relations query returned by 'Node relations query function'! Please check widget configuration!";
|
|
|
386
|
+ }
|
|
|
387
|
+ showError(errorText);
|
|
|
388
|
+ }
|
|
|
389
|
+ );
|
|
|
390
|
+ }
|
|
|
391
|
+ } else {
|
|
|
392
|
+ cb([]);
|
|
|
393
|
+ }
|
|
|
394
|
+ }
|
|
|
395
|
+ }
|
|
|
396
|
+
|
|
|
397
|
+ function showError(errorText) {
|
|
|
398
|
+ var toastParent = angular.element('.tb-edges-hierarchy', $element);
|
|
|
399
|
+ toast.showError(errorText, toastParent, 'bottom left');
|
|
|
400
|
+ }
|
|
|
401
|
+
|
|
|
402
|
+ function prepareNodes(nodes) {
|
|
|
403
|
+ nodes = nodes.filter((node) => node !== null);
|
|
|
404
|
+ nodes.sort((node1, node2) => vm.nodesSortFunction(node1.data.nodeCtx, node2.data.nodeCtx));
|
|
|
405
|
+ return nodes;
|
|
|
406
|
+ }
|
|
|
407
|
+
|
|
|
408
|
+ function datasourceToNode(datasource, parentNodeCtx) {
|
|
|
409
|
+ var deferred = $q.defer();
|
|
|
410
|
+ resolveEntity(datasource).then(
|
|
|
411
|
+ (entity) => {
|
|
|
412
|
+ if (entity != null) {
|
|
|
413
|
+ var node = {
|
|
|
414
|
+ id: ++vm.nodeIdCounter
|
|
|
415
|
+ };
|
|
|
416
|
+ vm.nodesMap[node.id] = node;
|
|
|
417
|
+ datasource.nodeId = node.id;
|
|
|
418
|
+ node.icon = false;
|
|
|
419
|
+ var nodeCtx = {
|
|
|
420
|
+ parentNodeCtx: parentNodeCtx,
|
|
|
421
|
+ entity: entity,
|
|
|
422
|
+ data: {}
|
|
|
423
|
+ };
|
|
|
424
|
+ nodeCtx.level = parentNodeCtx ? parentNodeCtx.level + 1 : 1;
|
|
|
425
|
+ node.data = {
|
|
|
426
|
+ datasource: datasource,
|
|
|
427
|
+ nodeCtx: nodeCtx
|
|
|
428
|
+ };
|
|
|
429
|
+ node.state = {
|
|
|
430
|
+ disabled: vm.nodeDisabledFunction(node.data.nodeCtx),
|
|
|
431
|
+ opened: vm.nodeOpenedFunction(node.data.nodeCtx)
|
|
|
432
|
+ };
|
|
|
433
|
+ node.text = prepareNodeText(node);
|
|
|
434
|
+ node.children = vm.nodeHasChildrenFunction(node.data.nodeCtx);
|
|
|
435
|
+ deferred.resolve(node);
|
|
|
436
|
+ } else {
|
|
|
437
|
+ deferred.resolve(null);
|
|
|
438
|
+ }
|
|
|
439
|
+ }
|
|
|
440
|
+ );
|
|
|
441
|
+ return deferred.promise;
|
|
|
442
|
+ }
|
|
|
443
|
+
|
|
|
444
|
+ function datasourceToNodeEdge(datasource, parentNodeCtx) {
|
|
|
445
|
+ var deferred = $q.defer();
|
|
|
446
|
+ resolveEntity(datasource).then(
|
|
|
447
|
+ (entity) => {
|
|
|
448
|
+ if (entity != null) {
|
|
|
449
|
+ var node = {
|
|
|
450
|
+ id: ++vm.nodeIdCounter
|
|
|
451
|
+ };
|
|
|
452
|
+ vm.nodesMap[node.id] = node;
|
|
|
453
|
+ datasource.nodeId = node.id;
|
|
|
454
|
+ node.icon = false;
|
|
|
455
|
+ var nodeCtx = {
|
|
|
456
|
+ parentNodeCtx: parentNodeCtx,
|
|
|
457
|
+ entity: entity,
|
|
|
458
|
+ data: {}
|
|
|
459
|
+ };
|
|
|
460
|
+ nodeCtx.level = parentNodeCtx ? parentNodeCtx.level + 1 : 1;
|
|
|
461
|
+ node.data = {
|
|
|
462
|
+ datasource: datasource,
|
|
|
463
|
+ nodeCtx: nodeCtx
|
|
|
464
|
+ };
|
|
|
465
|
+ node.state = {
|
|
|
466
|
+ disabled: vm.nodeDisabledFunction(node.data.nodeCtx),
|
|
|
467
|
+ opened: vm.nodeOpenedFunction(node.data.nodeCtx)
|
|
|
468
|
+ };
|
|
|
469
|
+ node.text = prepareNodeText(node);
|
|
|
470
|
+ node.children = vm.nodeHasChildrenFunction(node.data.nodeCtx);
|
|
|
471
|
+ deferred.resolve(node);
|
|
|
472
|
+ } else {
|
|
|
473
|
+ deferred.resolve(null);
|
|
|
474
|
+ }
|
|
|
475
|
+ }
|
|
|
476
|
+ );
|
|
|
477
|
+ return deferred.promise;
|
|
|
478
|
+ }
|
|
|
479
|
+
|
|
|
480
|
+
|
|
|
481
|
+ function entityIdToNodeEdge(entityType, entityId, parentDatasource, parentNodeCtx) {
|
|
|
482
|
+ var deferred = $q.defer();
|
|
|
483
|
+ var datasource = {
|
|
|
484
|
+ dataKeys: parentDatasource.dataKeys,
|
|
|
485
|
+ type: types.datasourceType.entity,
|
|
|
486
|
+ entityType: entityType,
|
|
|
487
|
+ entityId: entityId
|
|
|
488
|
+ };
|
|
|
489
|
+ datasourceToNodeEdge(datasource, parentNodeCtx).then(
|
|
|
490
|
+ (node) => {
|
|
|
491
|
+ if (node != null) {
|
|
|
492
|
+ var subscriptionOptions = {
|
|
|
493
|
+ type: types.widgetType.latest.value,
|
|
|
494
|
+ datasources: [datasource],
|
|
|
495
|
+ callbacks: {
|
|
|
496
|
+ onDataUpdated: (subscription) => {
|
|
|
497
|
+ updateNodeData(subscription.data);
|
|
|
498
|
+ }
|
|
|
499
|
+ }
|
|
|
500
|
+ };
|
|
|
501
|
+ vm.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).then(
|
|
|
502
|
+ (/*subscription*/) => {
|
|
|
503
|
+ deferred.resolve(node);
|
|
|
504
|
+ }
|
|
|
505
|
+ );
|
|
|
506
|
+ } else {
|
|
|
507
|
+ deferred.resolve(node);
|
|
|
508
|
+ }
|
|
|
509
|
+ }
|
|
|
510
|
+ );
|
|
|
511
|
+ return deferred.promise;
|
|
|
512
|
+ }
|
|
|
513
|
+
|
|
|
514
|
+ function entityIdToNode(entityType, entityId, parentDatasource, parentNodeCtx) {
|
|
|
515
|
+ var deferred = $q.defer();
|
|
|
516
|
+ var datasource = {
|
|
|
517
|
+ dataKeys: parentDatasource.dataKeys,
|
|
|
518
|
+ type: types.datasourceType.entity,
|
|
|
519
|
+ entityType: entityType,
|
|
|
520
|
+ entityId: entityId
|
|
|
521
|
+ };
|
|
|
522
|
+ datasourceToNode(datasource, parentNodeCtx).then(
|
|
|
523
|
+ (node) => {
|
|
|
524
|
+ if (node != null) {
|
|
|
525
|
+ var subscriptionOptions = {
|
|
|
526
|
+ type: types.widgetType.latest.value,
|
|
|
527
|
+ datasources: [datasource],
|
|
|
528
|
+ callbacks: {
|
|
|
529
|
+ onDataUpdated: (subscription) => {
|
|
|
530
|
+ updateNodeData(subscription.data);
|
|
|
531
|
+ }
|
|
|
532
|
+ }
|
|
|
533
|
+ };
|
|
|
534
|
+ vm.ctx.subscriptionApi.createSubscription(subscriptionOptions, true).then(
|
|
|
535
|
+ (/*subscription*/) => {
|
|
|
536
|
+ deferred.resolve(node);
|
|
|
537
|
+ }
|
|
|
538
|
+ );
|
|
|
539
|
+ } else {
|
|
|
540
|
+ deferred.resolve(node);
|
|
|
541
|
+ }
|
|
|
542
|
+ }
|
|
|
543
|
+ );
|
|
|
544
|
+ return deferred.promise;
|
|
|
545
|
+ }
|
|
|
546
|
+
|
|
|
547
|
+ function resolveEntity(datasource) {
|
|
|
548
|
+ var deferred = $q.defer();
|
|
|
549
|
+ if (datasource.type === types.datasourceType.function) {
|
|
|
550
|
+ var entity = {
|
|
|
551
|
+ id: {
|
|
|
552
|
+ entityType: "function"
|
|
|
553
|
+ },
|
|
|
554
|
+ name: datasource.name
|
|
|
555
|
+ }
|
|
|
556
|
+ deferred.resolve(entity);
|
|
|
557
|
+ } else {
|
|
|
558
|
+ entityService.getEntity(datasource.entityType, datasource.entityId, {ignoreLoading: true}).then(
|
|
|
559
|
+ (entity) => {
|
|
|
560
|
+ deferred.resolve(entity);
|
|
|
561
|
+ },
|
|
|
562
|
+ () => {
|
|
|
563
|
+ deferred.resolve(null);
|
|
|
564
|
+ }
|
|
|
565
|
+ );
|
|
|
566
|
+ }
|
|
|
567
|
+ return deferred.promise;
|
|
|
568
|
+ }
|
|
|
569
|
+
|
|
|
570
|
+
|
|
|
571
|
+ function prepareNodeRelationQuery(nodeCtx) {
|
|
|
572
|
+ var relationQuery = vm.nodeRelationQueryFunction(nodeCtx);
|
|
|
573
|
+ if (relationQuery && relationQuery === 'default') {
|
|
|
574
|
+ relationQuery = defaultNodeRelationQueryFunction(nodeCtx);
|
|
|
575
|
+ }
|
|
|
576
|
+ return relationQuery;
|
|
|
577
|
+ }
|
|
|
578
|
+
|
|
|
579
|
+ function defaultNodeRelationQueryFunction(nodeCtx) {
|
|
|
580
|
+ var entity = nodeCtx.entity;
|
|
|
581
|
+ var query = {
|
|
|
582
|
+ parameters: {
|
|
|
583
|
+ rootId: entity.id.id,
|
|
|
584
|
+ rootType: entity.id.entityType,
|
|
|
585
|
+ direction: types.entitySearchDirection.from,
|
|
|
586
|
+ relationTypeGroup: "COMMON",
|
|
|
587
|
+ maxLevel: 1
|
|
|
588
|
+ },
|
|
|
589
|
+ filters: [
|
|
|
590
|
+ {
|
|
|
591
|
+ relationType: "Contains",
|
|
|
592
|
+ entityTypes: []
|
|
|
593
|
+ }
|
|
|
594
|
+ ]
|
|
|
595
|
+ };
|
|
|
596
|
+ return query;
|
|
|
597
|
+ }
|
|
|
598
|
+
|
|
|
599
|
+ function prepareNodeIcon(nodeCtx) {
|
|
|
600
|
+ var iconInfo = vm.nodeIconFunction(nodeCtx);
|
|
|
601
|
+ if (iconInfo && iconInfo === 'default') {
|
|
|
602
|
+ iconInfo = defaultNodeIconFunction(nodeCtx);
|
|
|
603
|
+ }
|
|
|
604
|
+ if (iconInfo && (iconInfo.iconUrl || iconInfo.materialIcon)) {
|
|
|
605
|
+ if (iconInfo.materialIcon) {
|
|
|
606
|
+ return materialIconHtml(iconInfo.materialIcon);
|
|
|
607
|
+ } else {
|
|
|
608
|
+ return iconUrlHtml(iconInfo.iconUrl);
|
|
|
609
|
+ }
|
|
|
610
|
+ } else {
|
|
|
611
|
+ return "";
|
|
|
612
|
+ }
|
|
|
613
|
+ }
|
|
|
614
|
+
|
|
|
615
|
+ function materialIconHtml(materialIcon) {
|
|
|
616
|
+ return '<md-icon aria-label="'+materialIcon+'" class="node-icon material-icons" role="img" aria-hidden="false">'+materialIcon+'</md-icon>';
|
|
|
617
|
+ }
|
|
|
618
|
+
|
|
|
619
|
+ function iconUrlHtml(iconUrl) {
|
|
|
620
|
+ return '<div class="node-icon" style="background-image: url('+iconUrl+');"> </div>';
|
|
|
621
|
+ }
|
|
|
622
|
+
|
|
|
623
|
+ function defaultNodeIconFunction(nodeCtx) {
|
|
|
624
|
+ var materialIcon = 'insert_drive_file';
|
|
|
625
|
+ var entity = nodeCtx.entity;
|
|
|
626
|
+ if (entity && entity.id && entity.id.entityType) {
|
|
|
627
|
+ switch (entity.id.entityType) {
|
|
|
628
|
+ case 'function':
|
|
|
629
|
+ materialIcon = 'functions';
|
|
|
630
|
+ break;
|
|
|
631
|
+ case types.entityType.device:
|
|
|
632
|
+ materialIcon = 'devices_other';
|
|
|
633
|
+ break;
|
|
|
634
|
+ case types.entityType.asset:
|
|
|
635
|
+ materialIcon = 'domain';
|
|
|
636
|
+ break;
|
|
|
637
|
+ case types.entityType.tenant:
|
|
|
638
|
+ materialIcon = 'supervisor_account';
|
|
|
639
|
+ break;
|
|
|
640
|
+ case types.entityType.customer:
|
|
|
641
|
+ materialIcon = 'supervisor_account';
|
|
|
642
|
+ break;
|
|
|
643
|
+ case types.entityType.user:
|
|
|
644
|
+ materialIcon = 'account_circle';
|
|
|
645
|
+ break;
|
|
|
646
|
+ case types.entityType.dashboard:
|
|
|
647
|
+ materialIcon = 'dashboards';
|
|
|
648
|
+ break;
|
|
|
649
|
+ case types.entityType.alarm:
|
|
|
650
|
+ materialIcon = 'notifications_active';
|
|
|
651
|
+ break;
|
|
|
652
|
+ case types.entityType.entityView:
|
|
|
653
|
+ materialIcon = 'view_quilt';
|
|
|
654
|
+ break;
|
|
|
655
|
+ case types.entityType.edge:
|
|
|
656
|
+ materialIcon = 'router';
|
|
|
657
|
+ break;
|
|
|
658
|
+ case types.entityType.rulechain:
|
|
|
659
|
+ materialIcon = 'settings_ethernet';
|
|
|
660
|
+ break;
|
|
|
661
|
+ }
|
|
|
662
|
+ }
|
|
|
663
|
+ return {
|
|
|
664
|
+ materialIcon: materialIcon
|
|
|
665
|
+ };
|
|
|
666
|
+ }
|
|
|
667
|
+
|
|
|
668
|
+ function defaultNodeOpenedFunction(nodeCtx) {
|
|
|
669
|
+ return nodeCtx.level <= 4;
|
|
|
670
|
+ }
|
|
|
671
|
+
|
|
|
672
|
+ function defaultSortFunction(nodeCtx1, nodeCtx2) {
|
|
|
673
|
+ var result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);
|
|
|
674
|
+ if (result === 0) {
|
|
|
675
|
+ result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);
|
|
|
676
|
+ }
|
|
|
677
|
+ return result;
|
|
|
678
|
+ }
|
|
|
679
|
+} |