Showing
11 changed files
with
133 additions
and
39 deletions
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
... | ... | @@ -69,14 +69,18 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod |
69 | 69 | && ruleNode.getConfiguration().equals(newRuleNode.getConfiguration())); |
70 | 70 | this.ruleNode = newRuleNode; |
71 | 71 | if (restartRequired) { |
72 | - tbNode.destroy(); | |
72 | + if (tbNode != null) { | |
73 | + tbNode.destroy(); | |
74 | + } | |
73 | 75 | start(context); |
74 | 76 | } |
75 | 77 | } |
76 | 78 | |
77 | 79 | @Override |
78 | 80 | public void stop(ActorContext context) throws Exception { |
79 | - tbNode.destroy(); | |
81 | + if (tbNode != null) { | |
82 | + tbNode.destroy(); | |
83 | + } | |
80 | 84 | context.stop(self); |
81 | 85 | } |
82 | 86 | ... | ... |
... | ... | @@ -15,13 +15,13 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div hide-xs hide-sm translate class="tb-cell" flex="30">event.event-time</div> | |
18 | +<div hide-xs hide-sm translate class="tb-cell" flex="25">event.event-time</div> | |
19 | 19 | <div translate class="tb-cell" flex="20">event.server</div> |
20 | -<div translate class="tb-cell" flex="20">event.type</div> | |
21 | -<div translate class="tb-cell" flex="20">event.entity</div> | |
20 | +<div translate class="tb-cell" flex="10">event.type</div> | |
21 | +<div translate class="tb-cell" flex="15">event.entity</div> | |
22 | 22 | <div translate class="tb-cell" flex="20">event.message-id</div> |
23 | 23 | <div translate class="tb-cell" flex="20">event.message-type</div> |
24 | -<div translate class="tb-cell" flex="20">event.data-type</div> | |
25 | -<div translate class="tb-cell" flex="20">event.data</div> | |
26 | -<div translate class="tb-cell" flex="20">event.metadata</div> | |
27 | -<div translate class="tb-cell" flex="20">event.error</div> | |
24 | +<div translate class="tb-cell" flex="15">event.data-type</div> | |
25 | +<div translate class="tb-cell" flex="10">event.data</div> | |
26 | +<div translate class="tb-cell" flex="10">event.metadata</div> | |
27 | +<div translate class="tb-cell" flex="10">event.error</div> | ... | ... |
... | ... | @@ -15,14 +15,14 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<div hide-xs hide-sm class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div> | |
18 | +<div hide-xs hide-sm class="tb-cell" flex="25">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div> | |
19 | 19 | <div class="tb-cell" flex="20">{{event.body.server}}</div> |
20 | -<div class="tb-cell" flex="20">{{event.body.type}}</div> | |
21 | -<div class="tb-cell" flex="20">{{event.body.entityName}}</div> | |
22 | -<div class="tb-cell" flex="20">{{event.body.msgId}}</div> | |
23 | -<div class="tb-cell" flex="20">{{event.body.msgType}}</div> | |
24 | -<div class="tb-cell" flex="20">{{event.body.dataType}}</div> | |
25 | -<div class="tb-cell" flex="20"> | |
20 | +<div class="tb-cell" flex="10">{{event.body.type}}</div> | |
21 | +<div class="tb-cell" flex="15">{{event.body.entityName}}</div> | |
22 | +<div class="tb-cell tb-nowrap" flex="20" ng-mouseenter="checkTooltip($event)">{{event.body.msgId}}</div> | |
23 | +<div class="tb-cell" flex="20" ng-mouseenter="checkTooltip($event)">{{event.body.msgType}}</div> | |
24 | +<div class="tb-cell" flex="15">{{event.body.dataType}}</div> | |
25 | +<div class="tb-cell" flex="10"> | |
26 | 26 | <md-button ng-if="event.body.data" class="md-icon-button md-primary" |
27 | 27 | ng-click="showContent($event, event.body.data, 'event.data', event.body.dataType)" |
28 | 28 | aria-label="{{ 'action.view' | translate }}"> |
... | ... | @@ -35,7 +35,7 @@ |
35 | 35 | </md-icon> |
36 | 36 | </md-button> |
37 | 37 | </div> |
38 | -<div class="tb-cell" flex="20"> | |
38 | +<div class="tb-cell" flex="10"> | |
39 | 39 | <md-button ng-if="event.body.metadata" class="md-icon-button md-primary" |
40 | 40 | ng-click="showContent($event, event.body.metadata, 'event.metadata', 'JSON')" |
41 | 41 | aria-label="{{ 'action.view' | translate }}"> |
... | ... | @@ -48,7 +48,7 @@ |
48 | 48 | </md-icon> |
49 | 49 | </md-button> |
50 | 50 | </div> |
51 | -<div class="tb-cell" flex="20"> | |
51 | +<div class="tb-cell" flex="10"> | |
52 | 52 | <md-button ng-if="event.body.error" class="md-icon-button md-primary" |
53 | 53 | ng-click="showContent($event, event.body.error, 'event.error')" |
54 | 54 | aria-label="{{ 'action.view' | translate }}"> | ... | ... |
... | ... | @@ -86,6 +86,14 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ |
86 | 86 | }); |
87 | 87 | } |
88 | 88 | |
89 | + scope.checkTooltip = function($event) { | |
90 | + var el = $event.target; | |
91 | + var $el = angular.element(el); | |
92 | + if(el.offsetWidth < el.scrollWidth && !$el.attr('title')){ | |
93 | + $el.attr('title', $el.text()); | |
94 | + } | |
95 | + } | |
96 | + | |
89 | 97 | $compile(element.contents())(scope); |
90 | 98 | } |
91 | 99 | ... | ... |
... | ... | @@ -24,6 +24,17 @@ md-list.tb-event-table { |
24 | 24 | height: 48px; |
25 | 25 | padding: 0px; |
26 | 26 | overflow: hidden; |
27 | + .tb-cell { | |
28 | + text-overflow: ellipsis; | |
29 | + &.tb-scroll { | |
30 | + white-space: nowrap; | |
31 | + overflow-y: hidden; | |
32 | + overflow-x: auto; | |
33 | + } | |
34 | + &.tb-nowrap { | |
35 | + white-space: nowrap; | |
36 | + } | |
37 | + } | |
27 | 38 | } |
28 | 39 | |
29 | 40 | .tb-row:hover { |
... | ... | @@ -39,13 +50,19 @@ md-list.tb-event-table { |
39 | 50 | color: rgba(0,0,0,.54); |
40 | 51 | font-size: 12px; |
41 | 52 | font-weight: 700; |
42 | - white-space: nowrap; | |
43 | 53 | background: none; |
54 | + white-space: nowrap; | |
44 | 55 | } |
45 | 56 | } |
46 | 57 | |
47 | 58 | .tb-cell { |
48 | - padding: 0 24px; | |
59 | + &:first-child { | |
60 | + padding-left: 14px; | |
61 | + } | |
62 | + &:last-child { | |
63 | + padding-right: 14px; | |
64 | + } | |
65 | + padding: 0 6px; | |
49 | 66 | margin: auto 0; |
50 | 67 | color: rgba(0,0,0,.87); |
51 | 68 | font-size: 13px; |
... | ... | @@ -53,8 +70,8 @@ md-list.tb-event-table { |
53 | 70 | text-align: left; |
54 | 71 | overflow: hidden; |
55 | 72 | .md-button { |
56 | - padding: 0; | |
57 | - margin: 0; | |
73 | + padding: 0; | |
74 | + margin: 0; | |
58 | 75 | } |
59 | 76 | } |
60 | 77 | ... | ... |
... | ... | @@ -43,6 +43,7 @@ export default angular.module('thingsboard.locale', []) |
43 | 43 | "update": "Update", |
44 | 44 | "remove": "Remove", |
45 | 45 | "search": "Search", |
46 | + "clear-search": "Clear search", | |
46 | 47 | "assign": "Assign", |
47 | 48 | "unassign": "Unassign", |
48 | 49 | "share": "Share", |
... | ... | @@ -1188,6 +1189,7 @@ export default angular.module('thingsboard.locale', []) |
1188 | 1189 | "details": "Details", |
1189 | 1190 | "events": "Events", |
1190 | 1191 | "search": "Search nodes", |
1192 | + "open-node-library": "Open node library", | |
1191 | 1193 | "add": "Add rule node", |
1192 | 1194 | "name": "Name", |
1193 | 1195 | "name-required": "Name is required.", | ... | ... |
... | ... | @@ -37,6 +37,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
37 | 37 | vm.$mdExpansionPanel = $mdExpansionPanel; |
38 | 38 | vm.types = types; |
39 | 39 | |
40 | + vm.isFullscreen = false; | |
41 | + | |
40 | 42 | vm.editingRuleNode = null; |
41 | 43 | vm.isEditingRuleNode = false; |
42 | 44 | |
... | ... | @@ -57,6 +59,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
57 | 59 | }; |
58 | 60 | |
59 | 61 | vm.ruleNodeTypesModel = {}; |
62 | + vm.ruleNodeTypesCanvasControl = {}; | |
60 | 63 | vm.ruleChainLibraryLoaded = false; |
61 | 64 | for (var type in types.ruleNodeType) { |
62 | 65 | if (!types.ruleNodeType[type].special) { |
... | ... | @@ -67,9 +70,12 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
67 | 70 | }, |
68 | 71 | selectedObjects: [] |
69 | 72 | }; |
73 | + vm.ruleNodeTypesCanvasControl[type] = {}; | |
70 | 74 | } |
71 | 75 | } |
72 | 76 | |
77 | + | |
78 | + | |
73 | 79 | vm.selectedObjects = []; |
74 | 80 | |
75 | 81 | vm.modelservice = Modelfactory(vm.ruleChainModel, vm.selectedObjects); |
... | ... | @@ -147,6 +153,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
147 | 153 | theForm.$setPristine(); |
148 | 154 | vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode; |
149 | 155 | vm.editingRuleNode = angular.copy(vm.editingRuleNode); |
156 | + updateRuleNodesHighlight(); | |
150 | 157 | } |
151 | 158 | }; |
152 | 159 | |
... | ... | @@ -313,12 +320,28 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
313 | 320 | } |
314 | 321 | }; |
315 | 322 | |
316 | - loadRuleChainLibrary(); | |
323 | + loadRuleChainLibrary(ruleNodeComponents, true); | |
324 | + | |
325 | + $scope.$watch('vm.ruleNodeSearch', | |
326 | + function (newVal, oldVal) { | |
327 | + if (!angular.equals(newVal, oldVal)) { | |
328 | + var res = $filter('filter')(ruleNodeComponents, {name: vm.ruleNodeSearch}); | |
329 | + loadRuleChainLibrary(res); | |
330 | + } | |
331 | + } | |
332 | + ); | |
317 | 333 | |
318 | - function loadRuleChainLibrary() { | |
334 | + $scope.$on('searchTextUpdated', function () { | |
335 | + updateRuleNodesHighlight(); | |
336 | + }); | |
337 | + | |
338 | + function loadRuleChainLibrary(ruleNodeComponents, loadRuleChain) { | |
339 | + for (var componentType in vm.ruleNodeTypesModel) { | |
340 | + vm.ruleNodeTypesModel[componentType].model.nodes.length = 0; | |
341 | + } | |
319 | 342 | for (var i=0;i<ruleNodeComponents.length;i++) { |
320 | 343 | var ruleNodeComponent = ruleNodeComponents[i]; |
321 | - var componentType = ruleNodeComponent.type; | |
344 | + componentType = ruleNodeComponent.type; | |
322 | 345 | var model = vm.ruleNodeTypesModel[componentType].model; |
323 | 346 | var node = { |
324 | 347 | id: 'node-lib-' + componentType + '-' + model.nodes.length, |
... | ... | @@ -349,7 +372,16 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
349 | 372 | model.nodes.push(node); |
350 | 373 | } |
351 | 374 | vm.ruleChainLibraryLoaded = true; |
352 | - prepareRuleChain(); | |
375 | + if (loadRuleChain) { | |
376 | + prepareRuleChain(); | |
377 | + } | |
378 | + $mdUtil.nextTick(() => { | |
379 | + for (componentType in vm.ruleNodeTypesCanvasControl) { | |
380 | + if (vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize) { | |
381 | + vm.ruleNodeTypesCanvasControl[componentType].adjustCanvasSize(true); | |
382 | + } | |
383 | + } | |
384 | + }); | |
353 | 385 | } |
354 | 386 | |
355 | 387 | function prepareRuleChain() { |
... | ... | @@ -519,6 +551,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
519 | 551 | |
520 | 552 | vm.isDirty = false; |
521 | 553 | |
554 | + updateRuleNodesHighlight(); | |
555 | + | |
522 | 556 | $mdUtil.nextTick(() => { |
523 | 557 | vm.ruleChainWatch = $scope.$watch('vm.ruleChainModel', |
524 | 558 | function (newVal, oldVal) { |
... | ... | @@ -530,6 +564,20 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
530 | 564 | }); |
531 | 565 | } |
532 | 566 | |
567 | + function updateRuleNodesHighlight() { | |
568 | + for (var i=0;i<vm.ruleChainModel.nodes.length;i++) { | |
569 | + vm.ruleChainModel.nodes[i].highlighted = false; | |
570 | + } | |
571 | + if ($scope.searchConfig.searchText) { | |
572 | + var res = $filter('filter')(vm.ruleChainModel.nodes, {name: $scope.searchConfig.searchText}); | |
573 | + if (res) { | |
574 | + for (i=0;i<res.length;i++) { | |
575 | + res[i].highlighted = true; | |
576 | + } | |
577 | + } | |
578 | + } | |
579 | + } | |
580 | + | |
533 | 581 | function saveRuleChain() { |
534 | 582 | var ruleChainMetaData = { |
535 | 583 | ruleChainId: vm.ruleChain.id, |
... | ... | @@ -642,6 +690,7 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, |
642 | 690 | ); |
643 | 691 | } |
644 | 692 | vm.ruleChainModel.nodes.push(ruleNode); |
693 | + updateRuleNodesHighlight(); | |
645 | 694 | }, function () { |
646 | 695 | }); |
647 | 696 | } | ... | ... |
... | ... | @@ -125,6 +125,13 @@ |
125 | 125 | color: #333; |
126 | 126 | border: solid 1px #777; |
127 | 127 | font-size: 12px; |
128 | + &.tb-rule-node-highlighted { | |
129 | + box-shadow: 0 0 10px 6px #51cbee; | |
130 | + .tb-node-title { | |
131 | + text-decoration: underline; | |
132 | + font-weight: bold; | |
133 | + } | |
134 | + } | |
128 | 135 | &.tb-input-type { |
129 | 136 | background-color: #a3eaa9; |
130 | 137 | user-select: none; |
... | ... | @@ -156,7 +163,7 @@ |
156 | 163 | |
157 | 164 | } |
158 | 165 | .tb-node-title { |
159 | - font-weight: 600; | |
166 | + font-weight: 500; | |
160 | 167 | } |
161 | 168 | .tb-node-type, .tb-node-title { |
162 | 169 | overflow: hidden; |
... | ... | @@ -184,6 +191,10 @@ |
184 | 191 | bottom: 0; |
185 | 192 | background-color: #000; |
186 | 193 | opacity: 0; |
194 | +/* &.tb-rule-node-highlighted { | |
195 | + background-color: green; | |
196 | + opacity: 0.15; | |
197 | + }*/ | |
187 | 198 | } |
188 | 199 | &.fc-hover { |
189 | 200 | .fc-node-overlay { | ... | ... |
... | ... | @@ -19,17 +19,17 @@ |
19 | 19 | <md-content flex tb-expand-fullscreen tb-confirm-on-exit is-dirty="vm.isDirty" |
20 | 20 | expand-tooltip-direction="bottom" layout="column" class="tb-rulechain" |
21 | 21 | ng-keydown="vm.keyDown($event)" |
22 | - ng-keyup="vm.keyUp($event)"> | |
22 | + ng-keyup="vm.keyUp($event)" on-fullscreen-changed="vm.isFullscreen = expanded"> | |
23 | 23 | <section class="tb-rulechain-container" flex layout="column"> |
24 | 24 | <div class="tb-rulechain-layout" flex layout="row"> |
25 | 25 | <section layout="row" layout-wrap |
26 | 26 | class="tb-header-buttons md-fab tb-library-open"> |
27 | 27 | <md-button ng-show="!vm.isLibraryOpen" |
28 | 28 | class="tb-btn-header tb-btn-open-library md-primary md-fab md-fab-top-left" |
29 | - aria-label="{{ 'action.apply' | translate }}" | |
29 | + aria-label="{{ 'rulenode.open-node-library' | translate }}" | |
30 | 30 | ng-click="vm.isLibraryOpen = true"> |
31 | - <md-tooltip md-direction="top"> | |
32 | - {{ 'action.apply-changes' | translate }} | |
31 | + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}"> | |
32 | + {{ 'rulenode.open-node-library' | translate }} | |
33 | 33 | </md-tooltip> |
34 | 34 | <ng-md-icon icon="menu"></ng-md-icon> |
35 | 35 | </md-button> |
... | ... | @@ -43,7 +43,7 @@ |
43 | 43 | <div class="md-toolbar-tools"> |
44 | 44 | <md-button class="md-icon-button tb-small" aria-label="{{ 'action.search' | translate }}"> |
45 | 45 | <md-icon aria-label="{{ 'action.search' | translate }}" class="material-icons">search</md-icon> |
46 | - <md-tooltip md-direction="top"> | |
46 | + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}"> | |
47 | 47 | {{'rulenode.search' | translate}} |
48 | 48 | </md-tooltip> |
49 | 49 | </md-button> |
... | ... | @@ -53,15 +53,17 @@ |
53 | 53 | <input ng-model="vm.ruleNodeSearch" placeholder="{{'rulenode.search' | translate}}"/> |
54 | 54 | </md-input-container> |
55 | 55 | </div> |
56 | - <md-button class="md-icon-button tb-small" aria-label="Close" ng-click="vm.ruleNodeSearch = ''"> | |
56 | + <md-button class="md-icon-button tb-small" aria-label="Close" | |
57 | + ng-show="vm.ruleNodeSearch" | |
58 | + ng-click="vm.ruleNodeSearch = ''"> | |
57 | 59 | <md-icon aria-label="Close" class="material-icons">close</md-icon> |
58 | - <md-tooltip md-direction="top"> | |
59 | - {{ 'action.close' | translate }} | |
60 | + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}"> | |
61 | + {{ 'action.clear-search' | translate }} | |
60 | 62 | </md-tooltip> |
61 | 63 | </md-button> |
62 | 64 | <md-button class="md-icon-button tb-small" aria-label="Close" ng-click="vm.isLibraryOpen = false"> |
63 | 65 | <md-icon aria-label="Close" class="material-icons">chevron_left</md-icon> |
64 | - <md-tooltip md-direction="top"> | |
66 | + <md-tooltip md-direction="{{vm.isFullscreen ? 'bottom' : 'top'}}"> | |
65 | 67 | {{ 'action.close' | translate }} |
66 | 68 | </md-tooltip> |
67 | 69 | </md-button> |
... | ... | @@ -90,6 +92,7 @@ |
90 | 92 | callbacks="vm.nodeLibCallbacks" |
91 | 93 | node-width="170" |
92 | 94 | node-height="50" |
95 | + control="vm.ruleNodeTypesCanvasControl[typeId]" | |
93 | 96 | drop-target-id="'tb-rulchain-canvas'"></fc-canvas> |
94 | 97 | </md-expansion-panel-content> |
95 | 98 | </md-expansion-panel-expanded> | ... | ... |
... | ... | @@ -22,8 +22,8 @@ |
22 | 22 | ng-mousedown="callbacks.mouseDown($event, node)" |
23 | 23 | ng-mouseenter="callbacks.mouseEnter($event, node)" |
24 | 24 | ng-mouseleave="callbacks.mouseLeave($event, node)"> |
25 | - <div class="{{flowchartConstants.nodeOverlayClass}}"></div> | |
26 | - <div class="tb-rule-node {{node.nodeClass}}"> | |
25 | + <div class="{{flowchartConstants.nodeOverlayClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}"></div> | |
26 | + <div class="tb-rule-node {{node.nodeClass}}" ng-class="{'tb-rule-node-highlighted' : node.highlighted}"> | |
27 | 27 | <md-icon aria-label="node-type-icon" flex="15" |
28 | 28 | class="material-icons">{{node.icon}}</md-icon> |
29 | 29 | <div layout="column" flex="85" layout-align="center"> | ... | ... |