Commit 4bbcffdf637f7fca0aa2caa03c514f31a3872561

Authored by Igor Kulikov
1 parent 784de083

Rule Node Debug UI

@@ -25,6 +25,7 @@ import com.typesafe.config.Config; @@ -25,6 +25,7 @@ import com.typesafe.config.Config;
25 import com.typesafe.config.ConfigFactory; 25 import com.typesafe.config.ConfigFactory;
26 import lombok.Getter; 26 import lombok.Getter;
27 import lombok.Setter; 27 import lombok.Setter;
  28 +import lombok.extern.slf4j.Slf4j;
28 import org.springframework.beans.factory.annotation.Autowired; 29 import org.springframework.beans.factory.annotation.Autowired;
29 import org.springframework.beans.factory.annotation.Value; 30 import org.springframework.beans.factory.annotation.Value;
30 import org.springframework.stereotype.Component; 31 import org.springframework.stereotype.Component;
@@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -38,6 +39,7 @@ import org.thingsboard.server.common.data.id.EntityId;
38 import org.thingsboard.server.common.data.id.TenantId; 39 import org.thingsboard.server.common.data.id.TenantId;
39 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 40 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
40 import org.thingsboard.server.common.msg.TbMsg; 41 import org.thingsboard.server.common.msg.TbMsg;
  42 +import org.thingsboard.server.common.msg.TbMsgDataType;
41 import org.thingsboard.server.common.msg.cluster.ServerAddress; 43 import org.thingsboard.server.common.msg.cluster.ServerAddress;
42 import org.thingsboard.server.common.transport.auth.DeviceAuthService; 44 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
43 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; 45 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
@@ -60,11 +62,13 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; @@ -60,11 +62,13 @@ import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
60 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; 62 import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
61 import org.thingsboard.server.service.component.ComponentDiscoveryService; 63 import org.thingsboard.server.service.component.ComponentDiscoveryService;
62 64
  65 +import java.io.IOException;
63 import java.io.PrintWriter; 66 import java.io.PrintWriter;
64 import java.io.StringWriter; 67 import java.io.StringWriter;
65 import java.nio.charset.StandardCharsets; 68 import java.nio.charset.StandardCharsets;
66 import java.util.Optional; 69 import java.util.Optional;
67 70
  71 +@Slf4j
68 @Component 72 @Component
69 public class ActorSystemContext { 73 public class ActorSystemContext {
70 private static final String AKKA_CONF_FILE_NAME = "actor-system.conf"; 74 private static final String AKKA_CONF_FILE_NAME = "actor-system.conf";
@@ -292,38 +296,49 @@ public class ActorSystemContext { @@ -292,38 +296,49 @@ public class ActorSystemContext {
292 } 296 }
293 297
294 private void persistDebug(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, Throwable error) { 298 private void persistDebug(TenantId tenantId, EntityId entityId, String type, TbMsg tbMsg, Throwable error) {
295 - Event event = new Event();  
296 - event.setTenantId(tenantId);  
297 - event.setEntityId(entityId);  
298 - event.setType(DataConstants.DEBUG);  
299 -  
300 - ObjectNode node = mapper.createObjectNode()  
301 - .put("type", type)  
302 - .put("server", getServerAddress())  
303 - .put("entityId", tbMsg.getOriginator().getId().toString())  
304 - .put("entityName", tbMsg.getOriginator().getEntityType().name())  
305 - .put("msgId", tbMsg.getId().toString())  
306 - .put("msgType", tbMsg.getType())  
307 - .put("dataType", tbMsg.getDataType().name());  
308 -  
309 - ObjectNode mdNode = node.putObject("metadata");  
310 - tbMsg.getMetaData().getData().forEach(mdNode::put); 299 + try {
  300 + Event event = new Event();
  301 + event.setTenantId(tenantId);
  302 + event.setEntityId(entityId);
  303 + event.setType(DataConstants.DEBUG_RULE_NODE);
  304 +
  305 + String metadata = mapper.writeValueAsString(tbMsg.getMetaData().getData());
  306 +
  307 + ObjectNode node = mapper.createObjectNode()
  308 + .put("type", type)
  309 + .put("server", getServerAddress())
  310 + .put("entityId", tbMsg.getOriginator().getId().toString())
  311 + .put("entityName", tbMsg.getOriginator().getEntityType().name())
  312 + .put("msgId", tbMsg.getId().toString())
  313 + .put("msgType", tbMsg.getType())
  314 + .put("dataType", tbMsg.getDataType().name())
  315 + .put("data", convertToString(tbMsg.getDataType(), tbMsg.getData()))
  316 + .put("metadata", metadata);
  317 +
  318 + if (error != null) {
  319 + node = node.put("error", toString(error));
  320 + }
  321 +
  322 + event.setBody(node);
  323 + eventService.save(event);
  324 + } catch (IOException ex) {
  325 + log.warn("Failed to persist rule node debug message", ex);
  326 + }
  327 + }
311 328
312 - switch (tbMsg.getDataType()) { 329 + private String convertToString(TbMsgDataType messageType, byte[] data) {
  330 + if (data == null) {
  331 + return null;
  332 + }
  333 + switch (messageType) {
  334 + case JSON:
  335 + case TEXT:
  336 + return new String(data, StandardCharsets.UTF_8);
313 case BINARY: 337 case BINARY:
314 - node.put("data", Base64Utils.encodeUrlSafe(tbMsg.getData()));  
315 - break; 338 + return Base64Utils.encodeToString(data);
316 default: 339 default:
317 - node.put("data", new String(tbMsg.getData(), StandardCharsets.UTF_8));  
318 - break;  
319 - }  
320 -  
321 - if (error != null) {  
322 - node = node.put("error", toString(error)); 340 + throw new RuntimeException("Message type: " + messageType + " is not supported!");
323 } 341 }
324 -  
325 - event.setBody(node);  
326 - eventService.save(event);  
327 } 342 }
328 343
329 public static Exception toException(Throwable error) { 344 public static Exception toException(Throwable error) {
@@ -51,6 +51,6 @@ public class AbstractRuleEngineControllerTest extends AbstractControllerTest { @@ -51,6 +51,6 @@ public class AbstractRuleEngineControllerTest extends AbstractControllerTest {
51 TimePageLink pageLink = new TimePageLink(limit); 51 TimePageLink pageLink = new TimePageLink(limit);
52 return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&", 52 return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&",
53 new TypeReference<TimePageData<Event>>() { 53 new TypeReference<TimePageData<Event>>() {
54 - }, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG, tenantId.getId()); 54 + }, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG_RULE_NODE, tenantId.getId());
55 } 55 }
56 } 56 }
@@ -37,7 +37,7 @@ public class DataConstants { @@ -37,7 +37,7 @@ public class DataConstants {
37 public static final String ERROR = "ERROR"; 37 public static final String ERROR = "ERROR";
38 public static final String LC_EVENT = "LC_EVENT"; 38 public static final String LC_EVENT = "LC_EVENT";
39 public static final String STATS = "STATS"; 39 public static final String STATS = "STATS";
40 - public static final String DEBUG = "DEBUG"; 40 + public static final String DEBUG_RULE_NODE = "DEBUG_RULE_NODE";
41 41
42 public static final String ONEWAY = "ONEWAY"; 42 public static final String ONEWAY = "ONEWAY";
43 public static final String TWOWAY = "TWOWAY"; 43 public static final String TWOWAY = "TWOWAY";
@@ -279,6 +279,23 @@ export default angular.module('thingsboard.types', []) @@ -279,6 +279,23 @@ export default angular.module('thingsboard.types', [])
279 function: "function", 279 function: "function",
280 alarm: "alarm" 280 alarm: "alarm"
281 }, 281 },
  282 + contentType: {
  283 + "JSON": {
  284 + value: "JSON",
  285 + name: "content-type.json",
  286 + code: "json"
  287 + },
  288 + "TEXT": {
  289 + value: "TEXT",
  290 + name: "content-type.text",
  291 + code: "text"
  292 + },
  293 + "BINARY": {
  294 + value: "BINARY",
  295 + name: "content-type.binary",
  296 + code: "text"
  297 + }
  298 + },
282 componentType: { 299 componentType: {
283 filter: "FILTER", 300 filter: "FILTER",
284 processor: "PROCESSOR", 301 processor: "PROCESSOR",
@@ -295,7 +312,8 @@ export default angular.module('thingsboard.types', []) @@ -295,7 +312,8 @@ export default angular.module('thingsboard.types', [])
295 user: "USER", 312 user: "USER",
296 dashboard: "DASHBOARD", 313 dashboard: "DASHBOARD",
297 alarm: "ALARM", 314 alarm: "ALARM",
298 - rulechain: "RULE_CHAIN" 315 + rulechain: "RULE_CHAIN",
  316 + rulenode: "RULE_NODE"
299 }, 317 },
300 aliasEntityType: { 318 aliasEntityType: {
301 current_customer: "CURRENT_CUSTOMER" 319 current_customer: "CURRENT_CUSTOMER"
@@ -388,6 +406,16 @@ export default angular.module('thingsboard.types', []) @@ -388,6 +406,16 @@ export default angular.module('thingsboard.types', [])
388 name: "event.type-stats" 406 name: "event.type-stats"
389 } 407 }
390 }, 408 },
  409 + debugEventType: {
  410 + debugRuleNode: {
  411 + value: "DEBUG_RULE_NODE",
  412 + name: "event.type-debug-rule-node"
  413 + },
  414 + debugRuleChain: {
  415 + value: "DEBUG_RULE_CHAIN",
  416 + name: "event.type-debug-rule-chain"
  417 + }
  418 + },
391 extensionType: { 419 extensionType: {
392 http: "HTTP", 420 http: "HTTP",
393 mqtt: "MQTT", 421 mqtt: "MQTT",
@@ -26,7 +26,7 @@ export default angular.module('thingsboard.directives.detailsSidenav', []) @@ -26,7 +26,7 @@ export default angular.module('thingsboard.directives.detailsSidenav', [])
26 .name; 26 .name;
27 27
28 /*@ngInject*/ 28 /*@ngInject*/
29 -function DetailsSidenav($timeout) { 29 +function DetailsSidenav($timeout, $window) {
30 30
31 var linker = function (scope, element, attrs) { 31 var linker = function (scope, element, attrs) {
32 32
@@ -42,6 +42,23 @@ function DetailsSidenav($timeout) { @@ -42,6 +42,23 @@ function DetailsSidenav($timeout) {
42 scope.isEdit = true; 42 scope.isEdit = true;
43 } 43 }
44 44
  45 + if (angular.isDefined(attrs.closeOnClickOutside && attrs.closeOnClickOutside)) {
  46 + scope.closeOnClickOutside = true;
  47 + var clickOutsideHandler = function() {
  48 + scope.closeDetails();
  49 + };
  50 + angular.element($window).click(clickOutsideHandler);
  51 + scope.$on("$destroy", function () {
  52 + angular.element($window).unbind('click', clickOutsideHandler);
  53 + });
  54 + }
  55 +
  56 + scope.onClick = function($event) {
  57 + if (scope.closeOnClickOutside) {
  58 + $event.stopPropagation();
  59 + }
  60 + };
  61 +
45 scope.toggleDetailsEditMode = function () { 62 scope.toggleDetailsEditMode = function () {
46 if (!scope.isAlwaysEdit) { 63 if (!scope.isAlwaysEdit) {
47 if (!scope.isEdit) { 64 if (!scope.isEdit) {
@@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
19 md-disable-backdrop="true" 19 md-disable-backdrop="true"
20 md-is-open="isOpen" 20 md-is-open="isOpen"
21 md-component-id="right" 21 md-component-id="right"
  22 + ng-click="onClick($event)"
22 layout="column"> 23 layout="column">
23 <header> 24 <header>
24 <md-toolbar class="md-theme-light" ng-style="{'height':headerHeightPx+'px'}"> 25 <md-toolbar class="md-theme-light" ng-style="{'height':headerHeightPx+'px'}">
@@ -17,11 +17,14 @@ import $ from 'jquery'; @@ -17,11 +17,14 @@ import $ from 'jquery';
17 import 'brace/ext/language_tools'; 17 import 'brace/ext/language_tools';
18 import 'brace/mode/java'; 18 import 'brace/mode/java';
19 import 'brace/theme/github'; 19 import 'brace/theme/github';
  20 +import beautify from 'js-beautify';
20 21
21 /* eslint-disable angular/angularelement */ 22 /* eslint-disable angular/angularelement */
22 23
  24 +const js_beautify = beautify.js;
  25 +
23 /*@ngInject*/ 26 /*@ngInject*/
24 -export default function EventContentDialogController($mdDialog, content, title, showingCallback) { 27 +export default function EventContentDialogController($mdDialog, types, content, contentType, title, showingCallback) {
25 28
26 var vm = this; 29 var vm = this;
27 30
@@ -32,9 +35,19 @@ export default function EventContentDialogController($mdDialog, content, title, @@ -32,9 +35,19 @@ export default function EventContentDialogController($mdDialog, content, title,
32 vm.content = content; 35 vm.content = content;
33 vm.title = title; 36 vm.title = title;
34 37
  38 + var mode;
  39 + if (contentType) {
  40 + mode = types.contentType[contentType].code;
  41 + if (contentType == types.contentType.JSON.value && vm.content) {
  42 + vm.content = js_beautify(vm.content, {indent_size: 4});
  43 + }
  44 + } else {
  45 + mode = 'java';
  46 + }
  47 +
35 vm.contentOptions = { 48 vm.contentOptions = {
36 useWrapMode: false, 49 useWrapMode: false,
37 - mode: 'java', 50 + mode: mode,
38 showGutter: false, 51 showGutter: false,
39 showPrintMargin: false, 52 showPrintMargin: false,
40 theme: 'github', 53 theme: 'github',
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 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 +<div hide-xs hide-sm translate class="tb-cell" flex="30">event.event-time</div>
  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>
  22 +<div translate class="tb-cell" flex="20">event.message-id</div>
  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>
@@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
18 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html'; 18 import eventHeaderLcEventTemplate from './event-header-lc-event.tpl.html';
19 import eventHeaderStatsTemplate from './event-header-stats.tpl.html'; 19 import eventHeaderStatsTemplate from './event-header-stats.tpl.html';
20 import eventHeaderErrorTemplate from './event-header-error.tpl.html'; 20 import eventHeaderErrorTemplate from './event-header-error.tpl.html';
  21 +import eventHeaderDebugRuleNodeTemplate from './event-header-debug-rulenode.tpl.html';
21 22
22 /* eslint-enable import/no-unresolved, import/default */ 23 /* eslint-enable import/no-unresolved, import/default */
23 24
@@ -38,6 +39,12 @@ export default function EventHeaderDirective($compile, $templateCache, types) { @@ -38,6 +39,12 @@ export default function EventHeaderDirective($compile, $templateCache, types) {
38 case types.eventType.error.value: 39 case types.eventType.error.value:
39 template = eventHeaderErrorTemplate; 40 template = eventHeaderErrorTemplate;
40 break; 41 break;
  42 + case types.debugEventType.debugRuleNode.value:
  43 + template = eventHeaderDebugRuleNodeTemplate;
  44 + break;
  45 + case types.debugEventType.debugRuleChain.value:
  46 + template = eventHeaderDebugRuleNodeTemplate;
  47 + break;
41 } 48 }
42 return $templateCache.get(template); 49 return $templateCache.get(template);
43 } 50 }
  1 +<!--
  2 +
  3 + Copyright © 2016-2018 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 +<div hide-xs hide-sm class="tb-cell" flex="30">{{event.createdTime | date : 'yyyy-MM-dd HH:mm:ss'}}</div>
  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">
  26 + <md-button ng-if="event.body.data" class="md-icon-button md-primary"
  27 + ng-click="showContent($event, event.body.data, 'event.data', event.body.msgType)"
  28 + aria-label="{{ 'action.view' | translate }}">
  29 + <md-tooltip md-direction="top">
  30 + {{ 'action.view' | translate }}
  31 + </md-tooltip>
  32 + <md-icon aria-label="{{ 'action.view' | translate }}"
  33 + class="material-icons">
  34 + more_horiz
  35 + </md-icon>
  36 + </md-button>
  37 +</div>
  38 +<div class="tb-cell" flex="20">
  39 + <md-button ng-if="event.body.metadata" class="md-icon-button md-primary"
  40 + ng-click="showContent($event, event.body.metadata, 'event.metadata', 'JSON')"
  41 + aria-label="{{ 'action.view' | translate }}">
  42 + <md-tooltip md-direction="top">
  43 + {{ 'action.view' | translate }}
  44 + </md-tooltip>
  45 + <md-icon aria-label="{{ 'action.view' | translate }}"
  46 + class="material-icons">
  47 + more_horiz
  48 + </md-icon>
  49 + </md-button>
  50 +</div>
  51 +<div class="tb-cell" flex="20">
  52 + <md-button ng-if="event.body.error" class="md-icon-button md-primary"
  53 + ng-click="showContent($event, event.body.error, 'event.error')"
  54 + aria-label="{{ 'action.view' | translate }}">
  55 + <md-tooltip md-direction="top">
  56 + {{ 'action.view' | translate }}
  57 + </md-tooltip>
  58 + <md-icon aria-label="{{ 'action.view' | translate }}"
  59 + class="material-icons">
  60 + more_horiz
  61 + </md-icon>
  62 + </md-button>
  63 +</div>
@@ -20,6 +20,7 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html'; @@ -20,6 +20,7 @@ import eventErrorDialogTemplate from './event-content-dialog.tpl.html';
20 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html'; 20 import eventRowLcEventTemplate from './event-row-lc-event.tpl.html';
21 import eventRowStatsTemplate from './event-row-stats.tpl.html'; 21 import eventRowStatsTemplate from './event-row-stats.tpl.html';
22 import eventRowErrorTemplate from './event-row-error.tpl.html'; 22 import eventRowErrorTemplate from './event-row-error.tpl.html';
  23 +import eventRowDebugRuleNodeTemplate from './event-row-debug-rulenode.tpl.html';
23 24
24 /* eslint-enable import/no-unresolved, import/default */ 25 /* eslint-enable import/no-unresolved, import/default */
25 26
@@ -40,6 +41,12 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ @@ -40,6 +41,12 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
40 case types.eventType.error.value: 41 case types.eventType.error.value:
41 template = eventRowErrorTemplate; 42 template = eventRowErrorTemplate;
42 break; 43 break;
  44 + case types.debugEventType.debugRuleNode.value:
  45 + template = eventRowDebugRuleNodeTemplate;
  46 + break;
  47 + case types.debugEventType.debugRuleChain.value:
  48 + template = eventRowDebugRuleNodeTemplate;
  49 + break;
43 } 50 }
44 return $templateCache.get(template); 51 return $templateCache.get(template);
45 } 52 }
@@ -53,17 +60,22 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $ @@ -53,17 +60,22 @@ export default function EventRowDirective($compile, $templateCache, $mdDialog, $
53 scope.loadTemplate(); 60 scope.loadTemplate();
54 }); 61 });
55 62
  63 + scope.types = types;
  64 +
56 scope.event = attrs.event; 65 scope.event = attrs.event;
57 66
58 - scope.showContent = function($event, content, title) { 67 + scope.showContent = function($event, content, title, contentType) {
59 var onShowingCallback = { 68 var onShowingCallback = {
60 onShowing: function(){} 69 onShowing: function(){}
61 } 70 }
  71 + if (!contentType) {
  72 + contentType = null;
  73 + }
62 $mdDialog.show({ 74 $mdDialog.show({
63 controller: 'EventContentDialogController', 75 controller: 'EventContentDialogController',
64 controllerAs: 'vm', 76 controllerAs: 'vm',
65 templateUrl: eventErrorDialogTemplate, 77 templateUrl: eventErrorDialogTemplate,
66 - locals: {content: content, title: title, showingCallback: onShowingCallback}, 78 + locals: {content: content, title: title, contentType: contentType, showingCallback: onShowingCallback},
67 parent: angular.element($document[0].body), 79 parent: angular.element($document[0].body),
68 fullscreen: true, 80 fullscreen: true,
69 targetEvent: $event, 81 targetEvent: $event,
@@ -36,8 +36,8 @@ export default function EventTableDirective($compile, $templateCache, $rootScope @@ -36,8 +36,8 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
36 for (var type in types.eventType) { 36 for (var type in types.eventType) {
37 var eventType = types.eventType[type]; 37 var eventType = types.eventType[type];
38 var enabled = true; 38 var enabled = true;
39 - for (var disabledType in disabledEventTypes) {  
40 - if (eventType.value === disabledEventTypes[disabledType]) { 39 + for (var i=0;i<disabledEventTypes.length;i++) {
  40 + if (eventType.value === disabledEventTypes[i]) {
41 enabled = false; 41 enabled = false;
42 break; 42 break;
43 } 43 }
@@ -47,7 +47,19 @@ export default function EventTableDirective($compile, $templateCache, $rootScope @@ -47,7 +47,19 @@ export default function EventTableDirective($compile, $templateCache, $rootScope
47 } 47 }
48 } 48 }
49 } else { 49 } else {
50 - scope.eventTypes = types.eventType; 50 + scope.eventTypes = angular.copy(types.eventType);
  51 + }
  52 +
  53 + if (attrs.debugEventTypes) {
  54 + var debugEventTypes = attrs.debugEventTypes.split(',');
  55 + for (i=0;i<debugEventTypes.length;i++) {
  56 + for (type in types.debugEventType) {
  57 + eventType = types.debugEventType[type];
  58 + if (eventType.value === debugEventTypes[i]) {
  59 + scope.eventTypes[type] = eventType;
  60 + }
  61 + }
  62 + }
51 } 63 }
52 64
53 scope.eventType = attrs.defaultEventType; 65 scope.eventType = attrs.defaultEventType;
@@ -341,6 +341,11 @@ export default angular.module('thingsboard.locale', []) @@ -341,6 +341,11 @@ export default angular.module('thingsboard.locale', [])
341 "enter-password": "Enter password", 341 "enter-password": "Enter password",
342 "enter-search": "Enter search" 342 "enter-search": "Enter search"
343 }, 343 },
  344 + "content-type": {
  345 + "json": "Json",
  346 + "text": "Text",
  347 + "binary": "Binary (Base64)"
  348 + },
344 "customer": { 349 "customer": {
345 "customer": "Customer", 350 "customer": "Customer",
346 "customers": "Customers", 351 "customers": "Customers",
@@ -762,6 +767,8 @@ export default angular.module('thingsboard.locale', []) @@ -762,6 +767,8 @@ export default angular.module('thingsboard.locale', [])
762 "type-error": "Error", 767 "type-error": "Error",
763 "type-lc-event": "Lifecycle event", 768 "type-lc-event": "Lifecycle event",
764 "type-stats": "Statistics", 769 "type-stats": "Statistics",
  770 + "type-debug-rule-node": "Debug",
  771 + "type-debug-rule-chain": "Debug",
765 "no-events-prompt": "No events found", 772 "no-events-prompt": "No events found",
766 "error": "Error", 773 "error": "Error",
767 "alarm": "Alarm", 774 "alarm": "Alarm",
@@ -769,6 +776,13 @@ export default angular.module('thingsboard.locale', []) @@ -769,6 +776,13 @@ export default angular.module('thingsboard.locale', [])
769 "server": "Server", 776 "server": "Server",
770 "body": "Body", 777 "body": "Body",
771 "method": "Method", 778 "method": "Method",
  779 + "type": "Type",
  780 + "entity": "Entity",
  781 + "message-id": "Message Id",
  782 + "message-type": "Message Type",
  783 + "data-type": "Data Type",
  784 + "metadata": "Metadata",
  785 + "data": "Data",
772 "event": "Event", 786 "event": "Event",
773 "status": "Status", 787 "status": "Status",
774 "success": "Success", 788 "success": "Success",
@@ -1172,6 +1186,7 @@ export default angular.module('thingsboard.locale', []) @@ -1172,6 +1186,7 @@ export default angular.module('thingsboard.locale', [])
1172 }, 1186 },
1173 "rulenode": { 1187 "rulenode": {
1174 "details": "Details", 1188 "details": "Details",
  1189 + "events": "Events",
1175 "add": "Add rule node", 1190 "add": "Add rule node",
1176 "name": "Name", 1191 "name": "Name",
1177 "name-required": "Name is required.", 1192 "name-required": "Name is required.",
@@ -28,7 +28,7 @@ import addRuleNodeLinkTemplate from './add-link.tpl.html'; @@ -28,7 +28,7 @@ import addRuleNodeLinkTemplate from './add-link.tpl.html';
28 /* eslint-enable import/no-unresolved, import/default */ 28 /* eslint-enable import/no-unresolved, import/default */
29 29
30 /*@ngInject*/ 30 /*@ngInject*/
31 -export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $document, $mdDialog, 31 +export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, $timeout, $mdExpansionPanel, $window, $document, $mdDialog,
32 $filter, $translate, hotkeys, types, ruleChainService, Modelfactory, flowchartConstants, 32 $filter, $translate, hotkeys, types, ruleChainService, Modelfactory, flowchartConstants,
33 ruleChain, ruleChainMetaData, ruleNodeComponents) { 33 ruleChain, ruleChainMetaData, ruleNodeComponents) {
34 34
@@ -77,6 +77,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, @@ -77,6 +77,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
77 vm.objectsSelected = objectsSelected; 77 vm.objectsSelected = objectsSelected;
78 vm.deleteSelected = deleteSelected; 78 vm.deleteSelected = deleteSelected;
79 79
  80 + vm.triggerResize = triggerResize;
  81 +
80 initHotKeys(); 82 initHotKeys();
81 83
82 function initHotKeys() { 84 function initHotKeys() {
@@ -129,18 +131,17 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, @@ -129,18 +131,17 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
129 } 131 }
130 132
131 vm.onEditRuleNodeClosed = function() { 133 vm.onEditRuleNodeClosed = function() {
132 - vm.editingRuleNode = null; 134 + //vm.editingRuleNode = null;
133 }; 135 };
134 136
135 vm.onEditRuleNodeLinkClosed = function() { 137 vm.onEditRuleNodeLinkClosed = function() {
136 - vm.editingRuleNodeLink = null; 138 + //vm.editingRuleNodeLink = null;
137 }; 139 };
138 140
139 vm.saveRuleNode = function(theForm) { 141 vm.saveRuleNode = function(theForm) {
140 $scope.$broadcast('form-submit'); 142 $scope.$broadcast('form-submit');
141 if (theForm.$valid) { 143 if (theForm.$valid) {
142 theForm.$setPristine(); 144 theForm.$setPristine();
143 - vm.isEditingRuleNode = false;  
144 vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode; 145 vm.ruleChainModel.nodes[vm.editingRuleNodeIndex] = vm.editingRuleNode;
145 vm.editingRuleNode = angular.copy(vm.editingRuleNode); 146 vm.editingRuleNode = angular.copy(vm.editingRuleNode);
146 } 147 }
@@ -148,7 +149,6 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, @@ -148,7 +149,6 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
148 149
149 vm.saveRuleNodeLink = function(theForm) { 150 vm.saveRuleNodeLink = function(theForm) {
150 theForm.$setPristine(); 151 theForm.$setPristine();
151 - vm.isEditingRuleNodeLink = false;  
152 vm.ruleChainModel.edges[vm.editingRuleNodeLinkIndex] = vm.editingRuleNodeLink; 152 vm.ruleChainModel.edges[vm.editingRuleNodeLinkIndex] = vm.editingRuleNodeLink;
153 vm.editingRuleNodeLink = angular.copy(vm.editingRuleNodeLink); 153 vm.editingRuleNodeLink = angular.copy(vm.editingRuleNodeLink);
154 }; 154 };
@@ -663,6 +663,11 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, @@ -663,6 +663,11 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
663 function deleteSelected() { 663 function deleteSelected() {
664 vm.modelservice.deleteSelected(); 664 vm.modelservice.deleteSelected();
665 } 665 }
  666 +
  667 + function triggerResize() {
  668 + var w = angular.element($window);
  669 + w.triggerHandler('resize');
  670 + }
666 } 671 }
667 672
668 /*@ngInject*/ 673 /*@ngInject*/
@@ -67,8 +67,9 @@ @@ -67,8 +67,9 @@
67 header-title="{{vm.editingRuleNode.name}}" 67 header-title="{{vm.editingRuleNode.name}}"
68 header-subtitle="{{(vm.types.ruleNodeType[vm.editingRuleNode.component.type].name | translate) 68 header-subtitle="{{(vm.types.ruleNodeType[vm.editingRuleNode.component.type].name | translate)
69 + ' - ' + vm.editingRuleNode.component.name}}" 69 + ' - ' + vm.editingRuleNode.component.name}}"
70 - is-read-only="false" 70 + is-read-only="vm.selectedRuleNodeTabIndex > 0"
71 is-open="vm.isEditingRuleNode" 71 is-open="vm.isEditingRuleNode"
  72 + close-on-click-outside="true"
72 is-always-edit="true" 73 is-always-edit="true"
73 on-close-details="vm.onEditRuleNodeClosed()" 74 on-close-details="vm.onEditRuleNodeClosed()"
74 on-toggle-details-edit-mode="vm.onRevertRuleNodeEdit(vm.ruleNodeForm)" 75 on-toggle-details-edit-mode="vm.onRevertRuleNodeEdit(vm.ruleNodeForm)"
@@ -77,9 +78,10 @@ @@ -77,9 +78,10 @@
77 <details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container"> 78 <details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container">
78 <div id="help-container"></div> 79 <div id="help-container"></div>
79 </details-buttons> 80 </details-buttons>
80 - <md-tabs id="ruleNodeTabs" md-border-bottom flex class="tb-absolute-fill"> 81 + <md-tabs md-selected="vm.selectedRuleNodeTabIndex"
  82 + id="ruleNodeTabs" md-border-bottom flex class="tb-absolute-fill" ng-if="vm.isEditingRuleNode">
81 <md-tab label="{{ 'rulenode.details' | translate }}"> 83 <md-tab label="{{ 'rulenode.details' | translate }}">
82 - <form name="vm.ruleNodeForm" ng-if="vm.isEditingRuleNode"> 84 + <form name="vm.ruleNodeForm">
83 <tb-rule-node 85 <tb-rule-node
84 rule-node="vm.editingRuleNode" 86 rule-node="vm.editingRuleNode"
85 rule-chain-id="vm.ruleChain.id.id" 87 rule-chain-id="vm.ruleChain.id.id"
@@ -90,6 +92,15 @@ @@ -90,6 +92,15 @@
90 </tb-rule-node> 92 </tb-rule-node>
91 </form> 93 </form>
92 </md-tab> 94 </md-tab>
  95 + <md-tab ng-if="vm.isEditingRuleNode && vm.editingRuleNode.ruleNodeId"
  96 + md-on-select="vm.triggerResize()" label="{{ 'rulenode.events' | translate }}">
  97 + <tb-event-table flex entity-type="vm.types.entityType.rulenode"
  98 + entity-id="vm.editingRuleNode.ruleNodeId.id"
  99 + tenant-id="vm.ruleChain.tenantId.id"
  100 + debug-event-types="{{vm.types.debugEventType.debugRuleNode.value}}"
  101 + default-event-type="{{vm.types.debugEventType.debugRuleNode.value}}">
  102 + </tb-event-table>
  103 + </md-tab>
93 </md-tabs> 104 </md-tabs>
94 </tb-details-sidenav> 105 </tb-details-sidenav>
95 <tb-details-sidenav class="tb-rulenode-link-details-sidenav" 106 <tb-details-sidenav class="tb-rulenode-link-details-sidenav"
@@ -97,6 +108,7 @@ @@ -97,6 +108,7 @@
97 header-subtitle="{{'rulenode.link-details' | translate}}" 108 header-subtitle="{{'rulenode.link-details' | translate}}"
98 is-read-only="false" 109 is-read-only="false"
99 is-open="vm.isEditingRuleNodeLink" 110 is-open="vm.isEditingRuleNodeLink"
  111 + close-on-click-outside="true"
100 is-always-edit="true" 112 is-always-edit="true"
101 on-close-details="vm.onEditRuleNodeLinkClosed()" 113 on-close-details="vm.onEditRuleNodeLinkClosed()"
102 on-toggle-details-edit-mode="vm.onRevertRuleNodeLinkEdit(vm.ruleNodeLinkForm)" 114 on-toggle-details-edit-mode="vm.onRevertRuleNodeLinkEdit(vm.ruleNodeLinkForm)"
@@ -55,7 +55,8 @@ @@ -55,7 +55,8 @@
55 <tb-event-table flex entity-type="vm.types.entityType.rulechain" 55 <tb-event-table flex entity-type="vm.types.entityType.rulechain"
56 entity-id="vm.grid.operatingItem().id.id" 56 entity-id="vm.grid.operatingItem().id.id"
57 tenant-id="vm.grid.operatingItem().tenantId.id" 57 tenant-id="vm.grid.operatingItem().tenantId.id"
58 - default-event-type="{{vm.types.eventType.lcEvent.value}}"> 58 + debug-event-types="{{vm.types.debugEventType.debugRuleChain.value}}"
  59 + default-event-type="{{vm.types.debugEventType.debugRuleChain.value}}">
59 </tb-event-table> 60 </tb-event-table>
60 </md-tab> 61 </md-tab>
61 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}"> 62 <md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode && vm.isRuleChainEditable(vm.grid.operatingItem())" md-on-select="vm.grid.triggerResize()" label="{{ 'relation.relations' | translate }}">