diff --git a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json index 6b7603c026..9adeb4f49e 100644 --- a/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json +++ b/application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json @@ -20,7 +20,6 @@ }, "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", "name": "Device Profile Node", - "debugMode": false, "configuration": { "persistAlarmRulesState": false, "fetchAlarmRulesStateOnStart": false @@ -34,7 +33,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", - "debugMode": false, "configuration": { "defaultTTL": 0 }, @@ -47,7 +45,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", - "debugMode": false, "configurationVersion": 2, "configuration": { "scope": "CLIENT_SCOPE", @@ -64,7 +61,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "Message Type Switch", - "debugMode": false, "configuration": { "version": 0 }, @@ -77,7 +73,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log RPC from Device", - "debugMode": false, "configuration": { "scriptLang": "TBEL", "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", @@ -92,7 +87,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log Other", - "debugMode": false, "configuration": { "scriptLang": "TBEL", "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", @@ -107,7 +101,6 @@ }, "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "name": "RPC Call Request", - "debugMode": false, "configuration": { "timeoutInSeconds": 60 }, @@ -120,7 +113,6 @@ }, "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", "name": "Push to cloud", - "debugMode": false, "configuration": { "scope": "SERVER_SCOPE" }, @@ -133,7 +125,6 @@ }, "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", "name": "Push to cloud", - "debugMode": false, "configuration": { "scope": "SERVER_SCOPE" }, diff --git a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg index 5a886556f8..7d22c90abf 100644 --- a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg @@ -120,7 +120,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -135,6 +135,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -155,7 +164,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -170,6 +179,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/centrifugal-pump.svg b/application/src/main/data/json/system/scada_symbols/centrifugal-pump.svg index 3e2524bd2f..5a67d37e07 100644 --- a/application/src/main/data/json/system/scada_symbols/centrifugal-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/centrifugal-pump.svg @@ -105,7 +105,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -120,6 +120,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -140,7 +149,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -155,6 +164,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/control-panel-hp.svg b/application/src/main/data/json/system/scada_symbols/control-panel-hp.svg index 202ad16eb0..630d0faa28 100644 --- a/application/src/main/data/json/system/scada_symbols/control-panel-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/control-panel-hp.svg @@ -19,7 +19,7 @@ }, { "tag": "offButton", - "stateRenderFunction": "var offButtonChildren = element.children();\nif(ctx.values.value){\n ctx.api.enable(element);\n offButtonChildren[0].fill(ctx.properties.defaultButtonColor);\n} else {\n ctx.api.disable(element);\n offButtonChildren[0].fill(ctx.properties.activeButtonColor);\n}\n\noffButtonChildren[1].stroke(ctx.properties.borderButtonColor);\n\nif (!ctx.properties.label) {\n element.transform({translateY: -60});\n}", + "stateRenderFunction": "var offButtonChildren = element.children();\nif(ctx.values.value){\n ctx.api.enable(element);\n offButtonChildren[0].fill(ctx.properties.defaultButtonColor);\n} else {\n ctx.api.disable(element);\n offButtonChildren[0].fill(ctx.properties.activeButtonColor);\n}\n\noffButtonChildren[1].stroke(ctx.properties.borderButtonColor);\n\nif (!ctx.properties.label) {\n element.transform({translateY: -37});\n}", "actions": { "click": { "actionFunction": "ctx.api.disable(element);\nctx.api.callAction(event, 'offUpdateState', undefined, {\n next: () => {\n ctx.api.setValue('value', false);\n },\n error: () => {\n ctx.api.enable(element);\n } \n});" @@ -33,7 +33,7 @@ }, { "tag": "onButton", - "stateRenderFunction": "var onButtonChildren = element.children();\nif(ctx.values.value){\n ctx.api.disable(element);\n onButtonChildren[0].fill(ctx.properties.activeButtonColor);\n} else {\n ctx.api.enable(element);\n onButtonChildren[0].fill(ctx.properties.defaultButtonColor);\n}\nonButtonChildren[1].stroke(ctx.properties.borderButtonColor);\nif (!ctx.properties.label) {\n element.transform({translateY: -60});\n}", + "stateRenderFunction": "var onButtonChildren = element.children();\nif(ctx.values.value){\n ctx.api.disable(element);\n onButtonChildren[0].fill(ctx.properties.activeButtonColor);\n} else {\n ctx.api.enable(element);\n onButtonChildren[0].fill(ctx.properties.defaultButtonColor);\n}\nonButtonChildren[1].stroke(ctx.properties.borderButtonColor);\nif (!ctx.properties.label) {\n element.transform({translateY: -37});\n}", "actions": { "click": { "actionFunction": "ctx.api.disable(element);\nctx.api.callAction(event, 'onUpdateState', undefined, {\n next: () => {\n ctx.api.setValue('value', true);\n },\n error: () => {\n ctx.api.enable(element);\n } \n});" @@ -320,13 +320,13 @@ } ] }]]> -Heat pump - - - On - - - - Off +Heat pump + + + On + + + + Off \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg index fa23dbbe93..513809cca5 100644 --- a/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg @@ -142,7 +142,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -151,16 +151,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -177,7 +186,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -186,16 +195,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg index 00c7fa9ca2..5576c939c4 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg @@ -1,5 +1,4 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="400" fill="none" version="1.1" viewBox="0 0 800 400"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Dynamic horizontal scale", "description": "Dynamic horizontal scale with current volume value and dynamically configurable warning and critical scale.", "searchTags": [ @@ -19,22 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalState = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.values.highCriticalState;\n\nif (showHighCriticalState && value !== null && highCriticalScale !== null) {\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.values.highWarningState;\nif (showHighWarningScale && highWarningValue !== null) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\n\nif (showHighWarningScale && value !== null) {\n var highWarningScale = ctx.values.highWarningState;\n var highCriticalScale = ctx.values.highCriticalState;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n \n if (highWarningScale !== null && highCriticalScale !== null) {\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n \n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -44,22 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.values.lowWarningState;\nif (showLowWarningScale && lowWarningValue !== null) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n var lowWarningScale = ctx.values.lowWarningState;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (lowCriticalScale !== null && lowWarningScale !== null) {\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -84,7 +63,7 @@ }, { "tag": "target", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 640;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateX: -(640 - (Math.min(Math.max(offset+17, 36), 640)))});\n} else {\n element.hide();\n}\n\n", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateX: Math.min(Math.max(offset-17, 0), 619)});\n} else {\n element.hide();\n}", "actions": null }, { @@ -99,7 +78,12 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "valueArrowPosition", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: offset});\n}\n", "actions": null }, { @@ -108,8 +92,8 @@ "actions": null }, { - "tag": "valuePosition", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 638;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: -(638 - offset)});\n}\n", + "tag": "valueTextPositon", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: offset});\n}\n", "actions": null } ], @@ -792,39 +776,20 @@ } ] } - Outdoor - °C - - 100 - 0 - - - - 26 - - - - - - - - - - - - - - - - - - - - - - - - - - - +Outdoor°C + 0 + 100 + + + + 26 + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg index fe5fd56564..b7d5e0184b 100644 --- a/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg @@ -1,5 +1,4 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="800" fill="none" version="1.1" viewBox="0 0 400 800"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Dynamic vertical scale", "description": "Dynamic vertical scale with current volume value and dynamically configurable warning and critical scale.", "searchTags": [ @@ -19,22 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalState = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.values.highCriticalState;\n\nif (showHighCriticalState && value !== null && highCriticalScale !== null) {\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalState !== null) {\n element.show();\n var offset = calculateOffset(highCriticalState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null && highCriticalState !== null) {\n if (value >= highCriticalState && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.values.highWarningState;\nif (showHighWarningScale && highWarningValue !== null) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\n\nif (showHighWarningScale && value !== null) {\n var highWarningScale = ctx.values.highWarningState;\n var highCriticalScale = ctx.values.highCriticalState;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n \n if (highWarningScale !== null && highCriticalScale !== null) {\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningState = ctx.values.highWarningState;\nvar highCriticalState = ctx.values.highCriticalState;\nif (showHighWarningScale && highWarningState !== null) {\n element.show();\n var offset = calculateOffset(highWarningState, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n if (!showHighCriticalScale) {\n highCriticalState = ctx.properties.maxValue;\n }\n if (highWarningState !== null && highCriticalState !== null) {\n if (value < highCriticalState && value >= highWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -44,22 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.values.lowWarningState;\nif (showLowWarningScale && lowWarningValue !== null) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n var lowWarningScale = ctx.values.lowWarningState;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (lowCriticalScale !== null && lowWarningScale !== null) {\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningState = ctx.values.lowWarningState;\nvar lowCriticalState = ctx.values.lowCriticalState;\nif (showLowWarningScale && lowWarningState !== null) {\n element.show();\n var offset = calculateOffset(lowWarningState, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\nif (showLowWarningScale && value !== null) {\n if (!showLowCriticalScale) {\n lowCriticalState = ctx.properties.minValue;\n }\n if (lowCriticalState !== null && lowWarningState !== null) {\n if (value > lowCriticalState && value <= lowWarningState) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}", "actions": null }, { @@ -84,7 +63,7 @@ }, { "tag": "target", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateY: -(Math.min(Math.max(offset-17, 0), 617))});\n} else {\n element.hide();\n}\n\n", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateY: -Math.min(Math.max(offset-17, 0), 619)});\n} else {\n element.hide();\n}", "actions": null }, { @@ -99,7 +78,12 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "valueArrowPosition", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n", "actions": null }, { @@ -108,8 +92,8 @@ "actions": null }, { - "tag": "valuePosition", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n", + "tag": "valueTextPositon", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n", "actions": null } ], @@ -792,39 +776,20 @@ } ] } - Outdoor - °C - - - - - - - - - - - - - - - - - - - - - - 100 - 0 - - - - 26 - - - - - - - +Outdoor°C + + + + + + + 100 + 0 + + + + 26 + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/elevated-tank.svg b/application/src/main/data/json/system/scada_symbols/elevated-tank.svg index e3731ab979..9db211457e 100644 --- a/application/src/main/data/json/system/scada_symbols/elevated-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/elevated-tank.svg @@ -138,7 +138,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -147,16 +147,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -173,7 +182,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -182,16 +191,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/filter-hp.svg b/application/src/main/data/json/system/scada_symbols/filter-hp.svg index 3bec273373..02328a62ef 100644 --- a/application/src/main/data/json/system/scada_symbols/filter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/filter-hp.svg @@ -1,5 +1,4 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="400" fill="none" version="1.1" viewBox="0 0 200 400"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Filter", "description": "Filter", "searchTags": [ @@ -8,11 +7,6 @@ "widgetSizeX": 1, "widgetSizeY": 2, "tags": [ - { - "tag": "background", - "stateRenderFunction": "element.attr({fill: ctx.properties.runningColor});", - "actions": null - }, { "tag": "clickArea", "stateRenderFunction": null, @@ -44,16 +38,16 @@ "behavior": [ { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -62,33 +56,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -97,21 +120,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -147,46 +196,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -211,12 +220,12 @@ "properties": [ { "id": "runningColor", - "name": "{i18n:scada.symbol.running-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -227,12 +236,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -243,11 +252,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -259,19 +268,15 @@ } ] } - - - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg b/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg index 1c663c9b29..003c30df02 100644 --- a/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg @@ -1,9 +1,9 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="600" fill="none" version="1.1" viewBox="0 0 800 600"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Heat pump", "description": "Heat pump", "searchTags": [ - "pump" + "pump", + "high performance" ], "widgetSizeX": 4, "widgetSizeY": 3, @@ -57,7 +57,7 @@ }, { "tag": "value-box-background", - "stateRenderFunction": "if (ctx.values.running) {\n var color = ctx.properties.valueBoxBackground;\n element.attr({fill: color});\n}\n", + "stateRenderFunction": "if (ctx.values.running) {\n var color = ctx.properties.valueBoxBackground;\n element.attr({fill: color});\n} else {\n var color = ctx.properties.stoppedColor;\n element.attr({fill: color});\n}\n", "actions": null }, { @@ -181,16 +181,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -199,33 +199,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -234,21 +263,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -284,46 +339,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -348,12 +363,12 @@ "properties": [ { "id": "runningColor", - "name": "{i18n:scada.symbol.running-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -364,11 +379,11 @@ }, { "id": "stoppedColor", - "name": "{i18n:scada.symbol.stopped-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#666666", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.stopped}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -380,12 +395,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -396,11 +411,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -516,7 +531,7 @@ "id": "units", "name": "{i18n:scada.symbol.value-units}", "type": "units", - "default": "&#176;C", + "default": "°C", "required": null, "subLabel": null, "divider": null, @@ -546,7 +561,7 @@ } ] } - + @@ -572,33 +587,27 @@ - - - + - - + - - + - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg index 3e13dc1126..4ad766764a 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg @@ -120,7 +120,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -135,6 +135,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -155,7 +164,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -170,6 +179,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg index 232159f2a5..9ee3ebda66 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg @@ -1,9 +1,9 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1e3" height="600" fill="none" version="1.1" viewBox="0 0 1e3 600"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Horizontal tank", - "description": "Horizontal tank", + "description": "Horizontal tank with level visualizations.", "searchTags": [ - "horizontal tank" + "horizontal tank", + "high performance" ], "widgetSizeX": 5, "widgetSizeY": 3, @@ -122,16 +122,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -140,33 +140,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -175,21 +204,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -225,46 +280,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -342,7 +357,7 @@ "default": 10, "required": null, "subLabel": "{i18n:scada.symbol.intervals}", - "divider": true, + "divider": false, "fieldSuffix": null, "disableOnProperty": "scale", "rowClass": "", @@ -375,7 +390,7 @@ }, { "id": "majorColor", - "name": "{i18n:scada.symbol.major-ticks-color}", + "name": "{i18n:scada.symbol.major-ticks}", "type": "color", "default": "#00000061", "required": null, @@ -407,7 +422,7 @@ }, { "id": "minorColor", - "name": "{i18n:scada.symbol.minor-ticks-color}", + "name": "{i18n:scada.symbol.minor-ticks}", "type": "color", "default": "#0000001F", "required": null, @@ -423,12 +438,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -439,11 +454,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -455,16 +470,12 @@ } ] } - - - + - - + - - + @@ -527,18 +538,15 @@ - - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg b/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg index 6452715db4..7875b5dde0 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg @@ -142,7 +142,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -151,16 +151,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -177,7 +186,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -186,16 +195,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg b/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg index c032a4b957..2e542cecae 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg @@ -1,9 +1,9 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Horizontal valve", "description": "Horizontal valve with various states.", "searchTags": [ - "valve" + "valve", + "high performance" ], "widgetSizeX": 1, "widgetSizeY": 1, @@ -147,16 +147,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -165,33 +165,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -200,21 +229,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -249,57 +304,17 @@ }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null - }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } } ], "properties": [ { "id": "openedColor", - "name": "{i18n:scada.symbol.running-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.opened}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -310,11 +325,11 @@ }, { "id": "closedColor", - "name": "{i18n:scada.symbol.closed-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#666666", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.closed-mode}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -326,12 +341,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -342,12 +357,12 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.critical}", + "divider": false, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -358,26 +373,20 @@ } ] } - - + - - - - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg index eaea959550..772e34f0f7 100644 --- a/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg @@ -142,7 +142,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -151,16 +151,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -177,7 +186,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -186,16 +195,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg index 3d2708ef11..dc9cb47268 100644 --- a/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg index 89d3095953..b7cb67f2a0 100644 --- a/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg index 7eebad7d7a..3b9420e04d 100644 --- a/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg @@ -142,7 +142,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -151,16 +151,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -177,7 +186,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -186,16 +195,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg b/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg index 6eef9bf474..18394d3d2c 100644 --- a/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg @@ -135,7 +135,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -144,16 +144,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -170,7 +179,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -179,16 +188,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg index faa5153531..12ec888e66 100644 --- a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg @@ -120,7 +120,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -135,6 +135,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -155,7 +164,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -170,6 +179,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg b/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg index 251dfe6729..f1e982bac0 100644 --- a/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg @@ -259,7 +259,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -268,16 +268,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -294,7 +303,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -303,16 +312,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/left-motor-pump.svg b/application/src/main/data/json/system/scada_symbols/left-motor-pump.svg index 998501a59f..40ad35adf4 100644 --- a/application/src/main/data/json/system/scada_symbols/left-motor-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/left-motor-pump.svg @@ -70,7 +70,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -85,6 +85,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -105,7 +114,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -120,6 +129,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg b/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg index 1eafa846f8..86235ff1c1 100644 --- a/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg @@ -1,4 +1,4 @@ -{ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="400" fill="none" version="1.1" viewBox="0 0 200 400"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Long vertical connector", "description": "Long vertical connector", "widgetSizeX": 1, @@ -170,5 +170,5 @@ } ] } - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/pool-hp.svg b/application/src/main/data/json/system/scada_symbols/pool-hp.svg index d6aed15dbf..e67280d617 100644 --- a/application/src/main/data/json/system/scada_symbols/pool-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/pool-hp.svg @@ -1,9 +1,9 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="2400" height="800" fill="none" version="1.1" viewBox="0 0 2400 800"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Pool", - "description": "Pool", + "description": "Pool with level visualizations.", "searchTags": [ - "pool" + "pool", + "high performance" ], "widgetSizeX": 12, "widgetSizeY": 4, @@ -122,16 +122,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -140,33 +140,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -175,21 +204,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -225,46 +280,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -342,7 +357,7 @@ "default": 10, "required": null, "subLabel": "{i18n:scada.symbol.intervals}", - "divider": true, + "divider": false, "fieldSuffix": null, "disableOnProperty": "scale", "rowClass": "", @@ -375,7 +390,7 @@ }, { "id": "majorColor", - "name": "{i18n:scada.symbol.major-ticks-color}", + "name": "{i18n:scada.symbol.major-ticks}", "type": "color", "default": "#00000061", "required": null, @@ -407,7 +422,7 @@ }, { "id": "minorColor", - "name": "{i18n:scada.symbol.minor-ticks-color}", + "name": "{i18n:scada.symbol.minor-ticks}", "type": "color", "default": "#0000001F", "required": null, @@ -423,12 +438,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -439,11 +454,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -455,15 +470,11 @@ } ] } - - - + - - + - - + @@ -526,18 +537,15 @@ - - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/pump-hp.svg b/application/src/main/data/json/system/scada_symbols/pump-hp.svg index d3b309c409..daaca4ae9c 100644 --- a/application/src/main/data/json/system/scada_symbols/pump-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/pump-hp.svg @@ -3,7 +3,8 @@ "description": "Centrifugal pump with configurable connectors and various states.", "searchTags": [ "pump", - "centrifugal" + "centrifugal", + "high performance" ], "widgetSizeX": 2, "widgetSizeY": 2, @@ -80,16 +81,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -98,33 +99,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -133,21 +163,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -183,46 +239,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -247,12 +263,12 @@ "properties": [ { "id": "runningColor", - "name": "{i18n:scada.symbol.running-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -263,11 +279,11 @@ }, { "id": "stoppedColor", - "name": "{i18n:scada.symbol.stopped-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#666666", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.stopped}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -279,12 +295,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -295,11 +311,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "Critical", "divider": null, "fieldSuffix": null, "disableOnProperty": null, diff --git a/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg b/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg index bc271bbdcf..4bd6754f5c 100644 --- a/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg @@ -135,7 +135,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -144,16 +144,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -170,7 +179,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -179,16 +188,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg index 71ade4c774..9be5ec5857 100644 --- a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg @@ -120,7 +120,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -135,6 +135,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -155,7 +164,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -170,6 +179,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg b/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg index c2b4042671..b1aa081b82 100644 --- a/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg @@ -259,7 +259,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -268,16 +268,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -294,7 +303,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -303,16 +312,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/right-motor-pump.svg b/application/src/main/data/json/system/scada_symbols/right-motor-pump.svg index bb52315f1b..e1193a4df9 100644 --- a/application/src/main/data/json/system/scada_symbols/right-motor-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/right-motor-pump.svg @@ -70,7 +70,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -85,6 +85,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -105,7 +114,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -120,6 +129,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg b/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg index 76041feef5..01cc98ce0f 100644 --- a/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg @@ -1,10 +1,10 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="1e3" fill="none" version="1.1" viewBox="0 0 600 1e3"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Sand filter", "description": "Sand filter with configurable filtration mode option and various states.", "searchTags": [ "filter", - "sand" + "sand", + "high performance" ], "widgetSizeX": 3, "widgetSizeY": 5, @@ -154,16 +154,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -172,33 +172,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -207,21 +236,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -257,46 +312,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -417,12 +432,12 @@ }, { "id": "runningColor", - "name": "{i18n:scada.symbol.running-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.running}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -433,11 +448,11 @@ }, { "id": "stoppedColor", - "name": "{i18n:scada.symbol.stopped-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#666666", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.stopped}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -449,12 +464,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -465,11 +480,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -577,7 +592,7 @@ } ] } - + @@ -592,38 +607,46 @@ + + + + + + + + + + + + + + + Filter + + + + + Backwash + + + + + Rinse + + + + + Waste + + + + + Recirculate + + + + + Closed + - - - - - - - - - - - - - - - - - Filter - - - Backwash - - - Rinse - - - Waste - - - Recirculate - - - Closed - - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg index 3d32e49ab8..8a86100091 100644 --- a/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg @@ -1,9 +1,9 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="600" fill="none" version="1.1" viewBox="0 0 600 600"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Short vertical tank", - "description": "Short vertical tank", + "description": "Short vertical tank with level visualizations.", "searchTags": [ - "short tank" + "short tank", + "high performance" ], "widgetSizeX": 3, "widgetSizeY": 3, @@ -122,16 +122,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -140,33 +140,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -175,21 +204,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -225,46 +280,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -342,7 +357,7 @@ "default": 10, "required": null, "subLabel": "{i18n:scada.symbol.intervals}", - "divider": true, + "divider": false, "fieldSuffix": null, "disableOnProperty": "scale", "rowClass": "", @@ -375,7 +390,7 @@ }, { "id": "majorColor", - "name": "{i18n:scada.symbol.major-ticks-color}", + "name": "{i18n:scada.symbol.major-ticks}", "type": "color", "default": "#00000061", "required": null, @@ -407,7 +422,7 @@ }, { "id": "minorColor", - "name": "{i18n:scada.symbol.minor-ticks-color}", + "name": "{i18n:scada.symbol.minor-ticks}", "type": "color", "default": "#0000001F", "required": null, @@ -423,12 +438,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -439,11 +454,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -455,16 +470,12 @@ } ] } - - - + - - + - - + @@ -527,18 +538,15 @@ - - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg index 88850cee1d..e69daae548 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg @@ -1,5 +1,4 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="400" fill="none" version="1.1" viewBox="0 0 800 400"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Simple horizontal scale", "description": "Simple horizontal scale with current volume value and configurable warning and critical scale.", "searchTags": [ @@ -19,22 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 638;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\n\nif (showHighCriticalValue) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(638-offset);\n childrenElement[1].height(638-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\n\nif (showHighCriticalValue && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\nvar value = ctx.values.value;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 638;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(638-offset);\n childrenElement[1].height(638-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nvar value = ctx.values.value;\n\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.width(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -44,22 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 638;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\n\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n var childrenElement = element.children();\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 638;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n var lowWarningScale = ctx.properties.lowWarningScale;\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.width(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -84,7 +63,7 @@ }, { "tag": "target", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 640;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateX: -(640 - (Math.min(Math.max(offset+12, 30), 635)))});\n} else {\n element.hide();\n}\n\n", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateX: Math.min(Math.max(offset-17, 0), 619)});\n} else {\n element.hide();\n}\n\n", "actions": null }, { @@ -99,7 +78,12 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "valueArrowPosition", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: offset});\n}\n", "actions": null }, { @@ -108,8 +92,8 @@ "actions": null }, { - "tag": "valuePosition", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 638;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: -(638 - offset)});\n}\n", + "tag": "valueTextPositon", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: offset});\n}\n", "actions": null } ], @@ -720,39 +704,20 @@ } ] } - Outdoor - °C - - 100 - 0 - - - - 26 - - - - - - - - - - - - - - - - - - - - - - - - - - - +Outdoor°C + 0 + 100 + + + + 26 + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg index 30b36e6e76..7f9244eaea 100644 --- a/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg @@ -1,5 +1,4 @@ - - { +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="800" fill="none" version="1.1" viewBox="0 0 400 800"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Simple vertical scale", "description": "Simple vertical scale with current volume value and configurable warning and critical scale.", "searchTags": [ @@ -19,22 +18,12 @@ }, { "tag": "highCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\n\nif (showHighCriticalValue) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\n\nif (showHighCriticalValue && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.properties.highCriticalScale;\n\nif (showHighCriticalScale) {\n element.show();\n var offset = calculateOffset(highCriticalScale, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\n\nif (showHighCriticalScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "highWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "highWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n element.height(653-offset);\n} else {\n element.hide();\n}\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -44,22 +33,12 @@ }, { "tag": "lowCriticalScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowCriticalScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalScale = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowCriticalScale && value !== null) {\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}", "actions": null }, { "tag": "lowWarningScale", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}", - "actions": null - }, - { - "tag": "lowWarningScaleBackground", - "stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n var lowWarningScale = ctx.properties.lowWarningScale;\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar value = ctx.values.value;\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowWarningScale = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningScale, minValue, maxValue);\n element.height(offset);\n} else {\n element.hide();\n}\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}", "actions": null }, { @@ -84,7 +63,7 @@ }, { "tag": "target", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateY: -(Math.min(Math.max(offset-17, 0), 617))});\n} else {\n element.hide();\n}\n\n", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateY: -Math.min(Math.max(offset-17, 0), 619)});\n} else {\n element.hide();\n}", "actions": null }, { @@ -99,7 +78,12 @@ }, { "tag": "value", - "stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "stateRenderFunction": "if (ctx.properties.value) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}", + "actions": null + }, + { + "tag": "valueArrowPosition", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n", "actions": null }, { @@ -108,8 +92,8 @@ "actions": null }, { - "tag": "valuePosition", - "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n", + "tag": "valueTextPositon", + "stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n", "actions": null } ], @@ -720,39 +704,20 @@ } ] } - Outdoor - °C - - - - - - - - - - - - - - - - - - - - - - 100 - 0 - - - - 26 - - - - - - - +Outdoor°C + + + + + + + 100 + 0 + + + + 26 + + + + \ No newline at end of file diff --git a/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg index 153994b2c7..85bad5d9e0 100644 --- a/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/small-cylindrical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/small-left-motor-pump.svg b/application/src/main/data/json/system/scada_symbols/small-left-motor-pump.svg index 196f032511..21b9c1ba02 100644 --- a/application/src/main/data/json/system/scada_symbols/small-left-motor-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/small-left-motor-pump.svg @@ -70,7 +70,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -85,6 +85,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -105,7 +114,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -120,6 +129,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/small-right-motor-pump.svg b/application/src/main/data/json/system/scada_symbols/small-right-motor-pump.svg index 171fbdfca1..61c679e3fd 100644 --- a/application/src/main/data/json/system/scada_symbols/small-right-motor-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/small-right-motor-pump.svg @@ -70,7 +70,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -85,6 +85,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -105,7 +114,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -120,6 +129,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg b/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg index 2d2a5ca76c..7e6e4fa8a6 100644 --- a/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/spherical-tank.svg b/application/src/main/data/json/system/scada_symbols/spherical-tank.svg index 2c4128bdeb..4184bcf593 100644 --- a/application/src/main/data/json/system/scada_symbols/spherical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/spherical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg index a1b8ad7072..2ad4ac9ec8 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg index 014ea43994..8c3745ec3d 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg index 92a1c6b8d5..43eac50d35 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg @@ -144,7 +144,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -153,16 +153,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -179,7 +188,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -188,16 +197,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg index 8b8038fde0..07c3ea050c 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg index 2162610cfc..619bb48ef8 100644 --- a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg @@ -120,7 +120,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -135,6 +135,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -155,7 +164,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -170,6 +179,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg index 4d5017a6c4..fff4819e1e 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg @@ -120,7 +120,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -135,6 +135,15 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", @@ -155,7 +164,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -170,6 +179,12 @@ "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", "dataToValueFunction": "/* Should return boolean value */\nreturn data;", diff --git a/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg b/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg index 82d33a2e2c..d4358e8fa7 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg @@ -143,7 +143,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -152,16 +152,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -178,7 +187,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -187,16 +196,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg index f7b77082f2..4a7bbb3fdf 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg @@ -1,8 +1,9 @@ { "title": "HP Vertical tank", - "description": "Vertical tank", + "description": "Vertical tank with level visualizations.", "searchTags": [ - "vertical tank" + "vertical tank", + "high performance" ], "widgetSizeX": 3, "widgetSizeY": 5, @@ -121,16 +122,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -139,33 +140,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -174,21 +204,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -224,46 +280,6 @@ "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, { "id": "click", "name": "{i18n:scada.symbol.on-click}", @@ -341,7 +357,7 @@ "default": 10, "required": null, "subLabel": "{i18n:scada.symbol.intervals}", - "divider": true, + "divider": false, "fieldSuffix": null, "disableOnProperty": "scale", "rowClass": "", @@ -374,7 +390,7 @@ }, { "id": "majorColor", - "name": "{i18n:scada.symbol.major-ticks-color}", + "name": "{i18n:scada.symbol.major-ticks}", "type": "color", "default": "#00000061", "required": null, @@ -406,7 +422,7 @@ }, { "id": "minorColor", - "name": "{i18n:scada.symbol.minor-ticks-color}", + "name": "{i18n:scada.symbol.minor-ticks}", "type": "color", "default": "#0000001F", "required": null, @@ -422,12 +438,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -438,11 +454,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, diff --git a/application/src/main/data/json/system/scada_symbols/vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/vertical-tank.svg index 207a406a93..de7f907e76 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-tank.svg @@ -142,7 +142,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -151,16 +151,25 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, @@ -177,7 +186,7 @@ "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -186,16 +195,22 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, diff --git a/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg b/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg index 370bcb38ba..200e93cb95 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg @@ -1,9 +1,9 @@ -<svg width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg"> - <tb:metadata><![CDATA[{ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{ "title": "HP Vertical valve", "description": "Vertical valve with various states.", "searchTags": [ - "valve" + "valve", + "high performance" ], "widgetSizeX": 1, "widgetSizeY": 1, @@ -147,16 +147,16 @@ }, { "id": "warning", - "name": "{i18n:scada.symbol.warning-state}", + "name": "{i18n:scada.symbol.warning}", "hint": "{i18n:scada.symbol.warning-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.warning-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.warning}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -165,33 +165,62 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "MAJOR", + "MINOR", + "WARNING", + "INDETERMINATE" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "warningClick", + "name": "{i18n:scada.symbol.warning-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.warning-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "critical", - "name": "{i18n:scada.symbol.critical-state}", + "name": "{i18n:scada.symbol.critical}", "hint": "{i18n:scada.symbol.critical-state-hint}", - "group": null, + "group": "{i18n:scada.symbol.critical-state}", "type": "value", "valueType": "BOOLEAN", "trueLabel": null, "falseLabel": null, "stateLabel": "{i18n:scada.symbol.critical}", "defaultGetValueSettings": { - "action": "DO_NOTHING", + "action": "GET_ALARM_STATUS", "defaultValue": false, "executeRpc": { "method": "getState", @@ -200,21 +229,47 @@ "persistentPollingInterval": 1000 }, "getAttribute": { - "key": "state", - "scope": null + "scope": null, + "key": "state" }, "getTimeSeries": { "key": "state" }, + "getAlarmStatus": { + "severityList": [ + "CRITICAL" + ], + "typeList": null + }, "dataToValue": { "type": "NONE", - "compareToValue": true, - "dataToValueFunction": "/* Should return boolean value */\nreturn data;" + "dataToValueFunction": "/* Should return boolean value */\nreturn data;", + "compareToValue": true } }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null }, + { + "id": "criticalClick", + "name": "{i18n:scada.symbol.critical-click}", + "hint": "{i18n:scada.symbol.on-click-hint}", + "group": "{i18n:scada.symbol.critical-state}", + "type": "widgetAction", + "valueType": "BOOLEAN", + "trueLabel": null, + "falseLabel": null, + "stateLabel": null, + "defaultGetValueSettings": null, + "defaultSetValueSettings": null, + "defaultWidgetActionSettings": { + "type": "doNothing", + "targetDashboardStateId": null, + "openRightLayout": false, + "setEntityId": false, + "stateEntityParamName": null + } + }, { "id": "criticalAnimation", "name": "{i18n:scada.symbol.critical-state-animation}", @@ -249,57 +304,17 @@ }, "defaultSetValueSettings": null, "defaultWidgetActionSettings": null - }, - { - "id": "warningClick", - "name": "{i18n:scada.symbol.warning-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } - }, - { - "id": "criticalClick", - "name": "{i18n:scada.symbol.critical-click}", - "hint": "{i18n:scada.symbol.on-click-hint}", - "group": null, - "type": "widgetAction", - "valueType": "BOOLEAN", - "trueLabel": null, - "falseLabel": null, - "stateLabel": null, - "defaultGetValueSettings": null, - "defaultSetValueSettings": null, - "defaultWidgetActionSettings": { - "type": "doNothing", - "targetDashboardStateId": null, - "openRightLayout": false, - "setEntityId": false, - "stateEntityParamName": null - } } ], "properties": [ { "id": "openedColor", - "name": "{i18n:scada.symbol.opened-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#FFFFFF", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.opened}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -310,11 +325,11 @@ }, { "id": "closedColor", - "name": "{i18n:scada.symbol.closed-color}", + "name": "{i18n:scada.symbol.colors}", "type": "color", "default": "#666666", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.closed-mode}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -326,12 +341,12 @@ }, { "id": "warningColor", - "name": "{i18n:scada.symbol.warning-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#FAA405", "required": null, - "subLabel": null, - "divider": null, + "subLabel": "{i18n:scada.symbol.warning}", + "divider": true, "fieldSuffix": null, "disableOnProperty": null, "rowClass": "", @@ -342,11 +357,11 @@ }, { "id": "criticalColor", - "name": "{i18n:scada.symbol.critical-color}", + "name": "{i18n:scada.symbol.alarm-colors}", "type": "color", "default": "#D12730", "required": null, - "subLabel": null, + "subLabel": "{i18n:scada.symbol.critical}", "divider": null, "fieldSuffix": null, "disableOnProperty": null, @@ -358,26 +373,20 @@ } ] } - - + - - - - - + - - + - + \ No newline at end of file diff --git a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json index 0256a2ccf2..bce88d62b0 100644 --- a/application/src/main/data/json/tenant/device_profile/rule_chain_template.json +++ b/application/src/main/data/json/tenant/device_profile/rule_chain_template.json @@ -19,7 +19,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", - "debugMode": false, "configuration": { "defaultTTL": 0 } @@ -31,7 +30,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", - "debugMode": false, "configurationVersion": 2, "configuration": { "scope": "CLIENT_SCOPE", @@ -47,7 +45,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "Message Type Switch", - "debugMode": false, "configuration": { "version": 0 } @@ -59,7 +56,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log RPC from Device", - "debugMode": false, "configuration": { "scriptLang": "TBEL", "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", @@ -73,7 +69,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log Other", - "debugMode": false, "configuration": { "scriptLang": "TBEL", "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", @@ -87,7 +82,6 @@ }, "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "name": "RPC Call Request", - "debugMode": false, "configuration": { "timeoutInSeconds": 60 } @@ -100,7 +94,6 @@ }, "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", "name": "Device Profile Node", - "debugMode": false, "configuration": { "persistAlarmRulesState": false, "fetchAlarmRulesStateOnStart": false diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json index 0b70d087e7..ee38849c1b 100644 --- a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json +++ b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json @@ -18,7 +18,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", - "debugMode": false, "configuration": { "defaultTTL": 0 } @@ -30,7 +29,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Client Attributes", - "debugMode": false, "configurationVersion": 2, "configuration": { "scope": "CLIENT_SCOPE", @@ -46,7 +44,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "Message Type Switch", - "debugMode": false, "configuration": { "version": 0 } @@ -58,7 +55,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log RPC from Device", - "debugMode": false, "configuration": { "scriptLang": "TBEL", "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", @@ -72,7 +68,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log Other", - "debugMode": false, "configuration": { "scriptLang": "TBEL", "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);", @@ -86,7 +81,6 @@ }, "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "name": "RPC Call Request", - "debugMode": false, "configuration": { "timeoutInSeconds": 60 } @@ -99,7 +93,6 @@ }, "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", "name": "Device Profile Node", - "debugMode": false, "configuration": { "persistAlarmRulesState": false, "fetchAlarmRulesStateOnStart": false @@ -140,4 +133,4 @@ ], "ruleChainConnections": null } -} \ No newline at end of file +} diff --git a/application/src/main/data/upgrade/basic/schema_update.sql b/application/src/main/data/upgrade/basic/schema_update.sql index ed084c809c..9390238139 100644 --- a/application/src/main/data/upgrade/basic/schema_update.sql +++ b/application/src/main/data/upgrade/basic/schema_update.sql @@ -25,6 +25,22 @@ UPDATE user_credentials c SET failed_login_attempts = (SELECT (additional_info:: UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts' - 'userCredentialsEnabled')::text WHERE additional_info IS NOT NULL AND additional_info != 'null'; +-- UPDATE RULE NODE DEBUG MODE TO DEBUG STRATEGY START + +ALTER TABLE rule_node ADD COLUMN IF NOT EXISTS debug_settings varchar(1024) DEFAULT null; +DO +$$ + BEGIN + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'rule_node' AND column_name = 'debug_mode') + THEN + UPDATE rule_node SET debug_settings = '{"failuresEnabled": true, "allEnabledUntil": ' || cast((extract(epoch from now()) + 900) * 1000 as bigint) || '}' WHERE debug_mode = true; -- 15 minutes according to thingsboard.yml default settings. + ALTER TABLE rule_node DROP COLUMN debug_mode; + END IF; + END +$$; + +-- UPDATE RULE NODE DEBUG MODE TO DEBUG STRATEGY END + -- CREATE MOBILE APP BUNDLES FROM EXISTING APPS diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index d27bf40650..0070eba4a1 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -64,6 +64,7 @@ import org.thingsboard.server.common.data.msg.TbNodeConnectionType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.common.data.rule.RuleNodeState; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbActorMsg; @@ -131,7 +132,7 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.ENTITY_CREATED; * Created by ashvayka on 19.03.18. */ @Slf4j -class DefaultTbContext implements TbContext { +public class DefaultTbContext implements TbContext { private final ActorSystemContext mainCtx; private final String ruleChainName; @@ -145,25 +146,20 @@ class DefaultTbContext implements TbContext { @Override public void tellSuccess(TbMsg msg) { - tellNext(msg, Collections.singleton(TbNodeConnectionType.SUCCESS), null); + tellNext(msg, Collections.singleton(TbNodeConnectionType.SUCCESS)); } @Override public void tellNext(TbMsg msg, String relationType) { - tellNext(msg, Collections.singleton(relationType), null); + tellNext(msg, Collections.singleton(relationType)); } @Override public void tellNext(TbMsg msg, Set relationTypes) { - tellNext(msg, relationTypes, null); - } - - private void tellNext(TbMsg msg, Set relationTypes, Throwable th) { - if (nodeCtx.getSelf().isDebugMode()) { - relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); - } - msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId()); - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null)); + RuleNode ruleNode = nodeCtx.getSelf(); + persistDebugOutput(msg, relationTypes); + msg.getCallback().onProcessingEnd(ruleNode.getId()); + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(ruleNode.getRuleChainId(), ruleNode.getId(), relationTypes, msg, null)); } @Override @@ -189,9 +185,7 @@ class DefaultTbContext implements TbContext { if (item == null) { ack(msg); } else { - if (nodeCtx.getSelf().isDebugMode()) { - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType); - } + persistDebugOutput(msg, relationType); nodeCtx.getChainActor().tell(new RuleChainOutputMsg(item.getRuleChainId(), item.getRuleNodeId(), relationType, msg)); } } @@ -216,11 +210,9 @@ class DefaultTbContext implements TbContext { } return; } - if (nodeCtx.getSelf().isDebugMode()) { - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "To Root Rule Chain"); - } doEnqueue(tpi, tbMsg, new SimpleTbQueueCallback( metadata -> { + persistDebugOutput(tbMsg, TbNodeConnectionType.TO_ROOT_RULE_CHAIN); if (onSuccess != null) { onSuccess.run(); } @@ -310,8 +302,9 @@ class DefaultTbContext implements TbContext { } return; } - RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId(); - RuleNodeId ruleNodeId = nodeCtx.getSelf().getId(); + RuleNode ruleNode = nodeCtx.getSelf(); + RuleChainId ruleChainId = ruleNode.getRuleChainId(); + RuleNodeId ruleNodeId = ruleNode.getId(); TbMsg tbMsg = TbMsg.newMsg(source, queueName, ruleChainId, ruleNodeId); TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder() .setTenantIdMSB(getTenantId().getId().getMostSignificantBits()) @@ -321,12 +314,9 @@ class DefaultTbContext implements TbContext { if (failureMessage != null) { msg.setFailureMessage(failureMessage); } - if (nodeCtx.getSelf().isDebugMode()) { - relationTypes.forEach(relationType -> - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, relationType, null, failureMessage)); - } mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback( metadata -> { + persistDebugOutput(tbMsg, relationTypes, null, failureMessage); if (onSuccess != null) { onSuccess.run(); } @@ -342,10 +332,9 @@ class DefaultTbContext implements TbContext { @Override public void ack(TbMsg tbMsg) { - if (nodeCtx.getSelf().isDebugMode()) { - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "ACK", null); - } - tbMsg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId()); + RuleNode ruleNode = nodeCtx.getSelf(); + persistDebugOutput(tbMsg, TbNodeConnectionType.ACK); + tbMsg.getCallback().onProcessingEnd(ruleNode.getId()); tbMsg.getCallback().onSuccess(); } @@ -360,12 +349,11 @@ class DefaultTbContext implements TbContext { @Override public void tellFailure(TbMsg msg, Throwable th) { - if (nodeCtx.getSelf().isDebugMode()) { - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbNodeConnectionType.FAILURE, th); - } + RuleNode ruleNode = nodeCtx.getSelf(); + persistDebugOutput(msg, Set.of(TbNodeConnectionType.FAILURE), th, null); String failureMessage = getFailureMessage(th); - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getRuleChainId(), - nodeCtx.getSelf().getId(), Collections.singleton(TbNodeConnectionType.FAILURE), + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(ruleNode.getRuleChainId(), + ruleNode.getId(), Collections.singleton(TbNodeConnectionType.FAILURE), msg, failureMessage)); } @@ -1020,4 +1008,21 @@ class DefaultTbContext implements TbContext { return failureMessage; } + private void persistDebugOutput(TbMsg msg, String relationType) { + persistDebugOutput(msg, Set.of(relationType)); + } + + private void persistDebugOutput(TbMsg msg, Set relationTypes) { + persistDebugOutput(msg, relationTypes, null, null); + } + + private void persistDebugOutput(TbMsg msg, Set relationTypes, Throwable error, String failureMessage) { + RuleNode ruleNode = nodeCtx.getSelf(); + if (DebugModeUtil.isDebugAllAvailable(ruleNode)) { + relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(getTenantId(), ruleNode.getId(), msg, relationType, error, failureMessage)); + } else if (DebugModeUtil.isDebugFailuresAvailable(ruleNode, relationTypes)) { + mainCtx.persistDebugOutput(getTenantId(), ruleNode.getId(), msg, TbNodeConnectionType.FAILURE, error, failureMessage); + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index eaa2218116..460da228c3 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.plugin.RuleNodeUpdatedMsg; @@ -43,7 +44,6 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; import org.thingsboard.server.common.msg.queue.RuleEngineException; import org.thingsboard.server.common.msg.queue.RuleNodeException; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.queue.TbQueueCallback; @@ -72,7 +72,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor> nodeRoutes; private final RuleChainService service; private final TbClusterService clusterService; - private final TbApiUsageReportClient apiUsageClient; private String ruleChainName; private RuleNodeId firstId; @@ -81,7 +80,6 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor existingNodes = ruleNodeList.stream().map(RuleNode::getId).collect(Collectors.toSet()); - List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).collect(Collectors.toList()); + List removedRules = nodeActors.keySet().stream().filter(node -> !existingNodes.contains(node)).toList(); removedRules.forEach(ruleNodeId -> { log.trace("[{}][{}] Removing rule node [{}]", tenantId, entityId, ruleNodeId); RuleNodeCtx removed = nodeActors.remove(ruleNodeId); @@ -177,7 +175,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor relations = service.getRuleNodeRelations(TenantId.SYS_TENANT_ID, ruleNode.getId()); log.trace("[{}][{}][{}] Processing rule node relations [{}]", tenantId, entityId, ruleNode.getId(), relations.size()); - if (relations.size() == 0) { + if (relations.isEmpty()) { nodeRoutes.put(ruleNode.getId(), Collections.emptyList()); } else { for (EntityRelation relation : relations) { @@ -238,45 +236,47 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor relationTypes; - @Getter private final String failureMessage; public RuleNodeToRuleChainTellNextMsg(RuleChainId ruleChainId, RuleNodeId originator, Set relationTypes, TbMsg tbMsg, String failureMessage) { diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java index 9443e58a04..8b958df68b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java +++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java @@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.SystemParams; @@ -70,6 +71,15 @@ public class SystemInfoController extends BaseController { @Value("${ui.dashboard.max_datapoints_limit}") private long maxDatapointsLimit; + @Value("${debug.settings.default_duration:15}") + private int defaultDebugDurationMinutes; + + @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled:true}") + private boolean ruleChainDebugPerTenantLimitsEnabled; + + @Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.configuration:50000:3600}") + private String ruleChainDebugPerTenantLimitsConfiguration; + @Autowired(required = false) private BuildProperties buildProperties; @@ -141,6 +151,10 @@ public class SystemInfoController extends BaseController { if (!currentUser.isSystemAdmin()) { DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize()); + systemParams.setMaxDebugModeDurationMinutes(DebugModeUtil.getMaxDebugAllDuration(tenantProfileConfiguration.getMaxDebugModeDurationMinutes(), defaultDebugDurationMinutes)); + if (ruleChainDebugPerTenantLimitsEnabled) { + systemParams.setRuleChainDebugPerTenantLimitsConfiguration(ruleChainDebugPerTenantLimitsConfiguration); + } } systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID)) .map(QrCodeSettings::getQrCodeConfig).map(QRCodeConfig::isShowOnHomePage) diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index 007f77aa20..258014d6fe 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -151,6 +151,7 @@ public class TenantProfileController extends BaseController { " \"maxJSExecutions\": 5000000,\n" + " \"maxDPStorageDays\": 0,\n" + " \"maxRuleNodeExecutionsPerMessage\": 50,\n" + + " \"maxDebugModeDurationMinutes\": 15,\n" + " \"maxEmails\": 0,\n" + " \"maxSms\": 0,\n" + " \"maxCreatedAlarms\": 1000,\n" + diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java index cb66992565..efa541ca54 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/BaseRuleChainMetadataConstructor.java @@ -88,7 +88,7 @@ public abstract class BaseRuleChainMetadataConstructor implements RuleChainMetad .setIdLSB(node.getId().getId().getLeastSignificantBits()) .setType(node.getType()) .setName(node.getName()) - .setDebugMode(node.isDebugMode()) + .setDebugSettings(JacksonUtil.toString(node.getDebugSettings())) .setConfiguration(JacksonUtil.toString(node.getConfiguration())) .setAdditionalInfo(JacksonUtil.toString(node.getAdditionalInfo())) .setSingletonMode(node.isSingletonMode()) diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java index fc244417bc..a9accf572d 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/tenant/TenantMsgConstructorV1.java @@ -88,6 +88,7 @@ public class TenantMsgConstructorV1 implements TenantMsgConstructor { configuration.setMaxTransportDataPoints(0); configuration.setRuleEngineExceptionsTtlDays(0); configuration.setMaxRuleNodeExecutionsPerMessage(0); + configuration.setMaxDebugModeDurationMinutes(0); tenantProfileData.setConfiguration(configuration); tenantProfile.setProfileData(tenantProfileData); diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java index 86bddd8ea2..aa6795b123 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/tenant/TenantEdgeProcessor.java @@ -43,7 +43,7 @@ public class TenantEdgeProcessor extends BaseEdgeProcessor { private TenantMsgConstructorFactory tenantMsgConstructorFactory; public DownlinkMsg convertTenantEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) { - TenantId tenantId = new TenantId(edgeEvent.getEntityId()); + TenantId tenantId = TenantId.fromUUID(edgeEvent.getEntityId()); var msgConstructor = ((TenantMsgConstructor) tenantMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)); if (EdgeEventActionType.UPDATED.equals(edgeEvent.getAction())) { Tenant tenant = edgeCtx.getTenantService().findTenantById(tenantId); diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 8c0ba3a17a..e93b4717a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -202,7 +202,7 @@ public class InstallScripts { ruleChain = ruleChainService.saveRuleChain(ruleChain, false); ruleChainMetaData.setRuleChainId(ruleChain.getId()); - ruleChainService.saveRuleChainMetaData(TenantId.SYS_TENANT_ID, ruleChainMetaData, Function.identity(), false); + ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData, Function.identity(), false); return ruleChain; } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index 2a64dd3388..3aaa36b068 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -503,7 +503,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService { if (cacheKeyProto.hasResourceKey()) { return ImageCacheKey.forImage(tenantId, cacheKeyProto.getResourceKey()); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index c9a6dee588..73810949b3 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -192,7 +192,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< private void updateQueues(List queueUpdateMsgs) { for (QueueUpdateMsg queueUpdateMsg : queueUpdateMsgs) { log.info("Received queue update msg: [{}]", queueUpdateMsg); - TenantId tenantId = new TenantId(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(queueUpdateMsg.getTenantIdMSB(), queueUpdateMsg.getTenantIdLSB())); if (partitionService.isManagedByCurrentService(tenantId)) { QueueId queueId = new QueueId(new UUID(queueUpdateMsg.getQueueIdMSB(), queueUpdateMsg.getQueueIdLSB())); String queueName = queueUpdateMsg.getQueueName(); @@ -212,7 +212,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< private void deleteQueues(List queueDeleteMsgs) { for (QueueDeleteMsg queueDeleteMsg : queueDeleteMsgs) { log.info("Received queue delete msg: [{}]", queueDeleteMsg); - TenantId tenantId = new TenantId(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); + TenantId tenantId = TenantId.fromUUID(new UUID(queueDeleteMsg.getTenantIdMSB(), queueDeleteMsg.getTenantIdLSB())); QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queueDeleteMsg.getQueueName(), tenantId); removeConsumer(queueKey).ifPresent(consumer -> consumer.delete(true)); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java index 4324a3514a..95f5e932d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.RequiredArgsConstructor; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -24,9 +25,12 @@ import java.util.Objects; import java.util.function.BiConsumer; @Data -@AllArgsConstructor +@RequiredArgsConstructor public abstract class TbSubscription { + /** Cache the hash code */ + private transient int hash; // Default to 0. The hash code calculated for this object likely never be zero + private final String serviceId; private final String sessionId; private final int subscriptionId; @@ -49,7 +53,10 @@ public abstract class TbSubscription { @Override public int hashCode() { - return Objects.hash(sessionId, subscriptionId, tenantId, entityId, type); + if (hash == 0) { + hash = Objects.hash(sessionId, subscriptionId, tenantId, entityId, type); + } + return hash; } } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index a5e3532258..f642d7fea5 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -465,7 +465,7 @@ actors: debug_mode_rate_limits_per_tenant: # Enable/Disable the rate limit of persisted debug events for all rule nodes per tenant enabled: "${ACTORS_RULE_CHAIN_DEBUG_MODE_RATE_LIMITS_PER_TENANT_ENABLED:true}" - # The value of DEBUG mode rate limit. By default, no more then 50 thousand events per hour + # The value of DEBUG mode rate limit. By default, no more than 50 thousand events per hour configuration: "${ACTORS_RULE_CHAIN_DEBUG_MODE_RATE_LIMITS_PER_TENANT_CONFIGURATION:50000:3600}" node: # Errors for particular actor are persisted once per specified amount of milliseconds @@ -505,6 +505,12 @@ actors: # Actors statistic persistence frequency in milliseconds persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" +debug: + settings: + # Default duration (in minutes) for debug mode. Min value is 1 minute. Tenant profile settings override this one. + # If value from this setting is invalid, the default value (15 minutes) will be used. + default_duration: "${DEBUG_SETTINGS_DEFAULT_DURATION_MINUTES:15}" + # Cache settings parameters cache: # caffeine or redis(7.2 - latest compatible version) diff --git a/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java new file mode 100644 index 0000000000..21a4a45c6f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/actors/rule/DefaultTbContextTest.java @@ -0,0 +1,949 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.actors.rule; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.actors.TbActorRef; +import org.thingsboard.server.actors.ruleChain.DefaultTbContext; +import org.thingsboard.server.actors.ruleChain.RuleChainOutputMsg; +import org.thingsboard.server.actors.ruleChain.RuleNodeCtx; +import org.thingsboard.server.actors.ruleChain.RuleNodeToRuleChainTellNextMsg; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.debug.DebugSettings; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.TbMsgProcessingStackItem; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TbMsgCallback; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; +import org.thingsboard.server.queue.common.SimpleTbQueueCallback; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.notNull; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; + +@SuppressWarnings("ResultOfMethodCallIgnored") +@ExtendWith(MockitoExtension.class) +class DefaultTbContextTest { + + private final String EXCEPTION_MSG = "Some runtime exception!"; + private final RuntimeException EXCEPTION = new RuntimeException(EXCEPTION_MSG); + + private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("c7bf4c85-923c-4688-a4b5-0f8a0feb7cd5")); + private final RuleNodeId RULE_NODE_ID = new RuleNodeId(UUID.fromString("1ca5e2ef-1309-41d9-bafa-709e9df0e2a6")); + private final RuleChainId RULE_CHAIN_ID = new RuleChainId(UUID.fromString("b87c4123-f9f2-41a6-9a09-e3a5b6580b11")); + + @Mock + private ActorSystemContext mainCtxMock; + @Mock + private RuleNodeCtx nodeCtxMock; + @Mock + private TbActorRef chainActorMock; + + private DefaultTbContext defaultTbContext; + + @BeforeEach + public void setUp() { + defaultTbContext = new DefaultTbContext(mainCtxMock, "Test rule chain name", nodeCtxMock); + } + + @Test + public void givenDebugFailuresEvents_whenTellSuccess_thenVerifyDebugOutputNotPersisted() { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellSuccess(msg); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(mainCtxMock).shouldHaveNoInteractions(); + checkTellNextCommonLogic(callbackMock, TbNodeConnectionType.SUCCESS, msg); + } + + @Test + public void givenDebugFailuresEventsAndSuccessConnection_whenTellNext_thenVerifyDebugOutputNotPersisted() { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellNext(msg, TbNodeConnectionType.SUCCESS); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(mainCtxMock).shouldHaveNoInteractions(); + checkTellNextCommonLogic(callbackMock, TbNodeConnectionType.SUCCESS, msg); + } + + @MethodSource + @ParameterizedTest + void givenDebugFailuresEventsAndConnections_whenTellNext_thenVerifyDebugOutputPersisted(Set connections) { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellNext(msg, connections); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, TbNodeConnectionType.FAILURE, null, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + checkTellNextCommonLogic(callbackMock, connections, msg); + } + + private static Stream> givenDebugFailuresEventsAndConnections_whenTellNext_thenVerifyDebugOutputPersisted() { + return Stream.of( + Collections.singleton(TbNodeConnectionType.FAILURE), + Set.of(TbNodeConnectionType.FAILURE, TbNodeConnectionType.SUCCESS) + ); + } + + @MethodSource + @ParameterizedTest + void givenDebugDisabledAndConnections_whenTellNext_thenVerifyDebugOutputNotPersisted(Set connections) { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.off()); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellNext(msg, connections); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(mainCtxMock).shouldHaveNoInteractions(); + checkTellNextCommonLogic(callbackMock, connections, msg); + } + + private static Stream> givenDebugDisabledAndConnections_whenTellNext_thenVerifyDebugOutputNotPersisted() { + return Stream.of( + Collections.singleton(TbNodeConnectionType.FAILURE), + Collections.singleton(TbNodeConnectionType.SUCCESS), + Set.of(TbNodeConnectionType.FAILURE, TbNodeConnectionType.SUCCESS) + ); + } + + @MethodSource + @ParameterizedTest + void givenDebugAllEventsAndConnection_whenTellNext_thenVerifyDebugOutputPersisted(String connection) { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellNext(msg, connection); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, connection, null, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + checkTellNextCommonLogic(callbackMock, connection, msg); + } + + private static Stream givenDebugAllEventsAndConnection_whenTellNext_thenVerifyDebugOutputPersisted() { + return failureAndSuccessConnection(); + } + + @Test + public void givenDebugAllEventsAndFailureAndSuccessConnection_whenTellNext_thenVerifyDebugOutputPersistedForAllEvents() { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + Set connections = failureAndSuccessConnection().collect(Collectors.toSet()); + defaultTbContext.tellNext(msg, connections); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + var nodeConnectionsCaptor = ArgumentCaptor.forClass(String.class); + int wantedNumberOfInvocations = connections.size(); + then(mainCtxMock).should(times(wantedNumberOfInvocations)).persistDebugOutput(eq(TENANT_ID), eq(RULE_NODE_ID), eq(msg), nodeConnectionsCaptor.capture(), nullable(Throwable.class), nullable(String.class)); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + assertThat(nodeConnectionsCaptor.getAllValues()).hasSize(wantedNumberOfInvocations); + assertThat(nodeConnectionsCaptor.getAllValues()).containsExactlyInAnyOrderElementsOf(connections); + checkTellNextCommonLogic(callbackMock, connections, msg); + } + + @MethodSource + @ParameterizedTest + void givenDebugAllThenOnlyFailureEventsAndConnection_whenTellNext_thenVerifyDebugOutputPersisted(String connection) { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellNext(msg, connection); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, connection, null, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + checkTellNextCommonLogic(callbackMock, connection, msg); + } + + private static Stream givenDebugAllThenOnlyFailureEventsAndConnection_whenTellNext_thenVerifyDebugOutputPersisted() { + return failureAndSuccessConnection(); + } + + @Test + public void givenDebugAllThenOnlyEventsAndFailureAndSuccessConnection_whenTellNext_thenVerifyDebugOutputPersistedForAllEvents() { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failuresOrUntil(getUntilTime())); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + Set connections = failureAndSuccessConnection().collect(Collectors.toSet()); + defaultTbContext.tellNext(msg, connections); + + // THEN + then(nodeCtxMock).should().getChainActor(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + var nodeConnectionsCaptor = ArgumentCaptor.forClass(String.class); + int wantedNumberOfInvocations = connections.size(); + then(mainCtxMock).should(times(wantedNumberOfInvocations)).persistDebugOutput(eq(TENANT_ID), eq(RULE_NODE_ID), eq(msg), nodeConnectionsCaptor.capture(), nullable(Throwable.class), nullable(String.class)); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + assertThat(nodeConnectionsCaptor.getAllValues()).hasSize(wantedNumberOfInvocations); + assertThat(nodeConnectionsCaptor.getAllValues()).containsExactlyInAnyOrderElementsOf(connections); + checkTellNextCommonLogic(callbackMock, connections, msg); + } + + private static Stream failureAndSuccessConnection() { + return Stream.of(TbNodeConnectionType.FAILURE, TbNodeConnectionType.SUCCESS); + } + + @Test + public void givenDebugFailuresEventsAndFailureConnection_whenOutput_thenVerifyDebugOutputPersisted() { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + given(msgMock.popFormStack()).willReturn(new TbMsgProcessingStackItem(RULE_CHAIN_ID, RULE_NODE_ID)); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.output(msgMock, TbNodeConnectionType.FAILURE); + + // THEN + checkOutputCommonLogic(msgMock, TbNodeConnectionType.FAILURE); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msgMock, TbNodeConnectionType.FAILURE, null, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenDebugFailuresEventsAndSuccessConnection_whenOutput_thenVerifyDebugOutputNotPersisted() { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + given(msgMock.popFormStack()).willReturn(new TbMsgProcessingStackItem(RULE_CHAIN_ID, RULE_NODE_ID)); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.output(msgMock, TbNodeConnectionType.SUCCESS); + + // THEN + checkOutputCommonLogic(msgMock, TbNodeConnectionType.SUCCESS); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @ParameterizedTest + @ValueSource(strings = {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE}) + void givenDebugDisabled_whenOutput_thenVerifyDebugOutputNotPersisted(String nodeConnection) { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + given(msgMock.popFormStack()).willReturn(new TbMsgProcessingStackItem(RULE_CHAIN_ID, RULE_NODE_ID)); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.output(msgMock, nodeConnection); + + // THEN + checkOutputCommonLogic(msgMock, nodeConnection); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @ParameterizedTest + @ValueSource(strings = {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE}) + void givenDebugAllEvents_whenOutput_thenVerifyDebugOutputPersisted(String nodeConnection) { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(msgMock.popFormStack()).willReturn(new TbMsgProcessingStackItem(RULE_CHAIN_ID, RULE_NODE_ID)); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.output(msgMock, nodeConnection); + + // THEN + checkOutputCommonLogic(msgMock, nodeConnection); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msgMock, nodeConnection, null, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @ParameterizedTest + @ValueSource(strings = {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE}) + void givenDebugAllThenOnlyFailureEvents_whenOutput_thenVerifyDebugOutputPersisted(String nodeConnection) { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(msgMock.popFormStack()).willReturn(new TbMsgProcessingStackItem(RULE_CHAIN_ID, RULE_NODE_ID)); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.output(msgMock, nodeConnection); + + // THEN + checkOutputCommonLogic(msgMock, nodeConnection); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msgMock, nodeConnection, null, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenEmptyStack_whenOutput_thenVerifyMsgAck() { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + given(msgMock.popFormStack()).willReturn(null); + TbMsgCallback callbackMock = mock(TbMsgCallback.class); + given(msgMock.getCallback()).willReturn(callbackMock); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + + // WHEN + defaultTbContext.output(msgMock, TbNodeConnectionType.SUCCESS); + + // THEN + then(msgMock).should().popFormStack(); + then(callbackMock).should().onProcessingEnd(RULE_NODE_ID); + then(callbackMock).should().onSuccess(); + then(nodeCtxMock).should(never()).getChainActor(); + } + + @Test + public void givenEmptyStackAndDebugAllEvents_whenOutput_thenVerifyMsgAckAndDebugOutputPersisted() { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(msgMock.popFormStack()).willReturn(null); + TbMsgCallback callbackMock = mock(TbMsgCallback.class); + given(msgMock.getCallback()).willReturn(callbackMock); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + + // WHEN + defaultTbContext.output(msgMock, TbNodeConnectionType.SUCCESS); + + // THEN + then(msgMock).should().popFormStack(); + then(callbackMock).should().onProcessingEnd(RULE_NODE_ID); + then(callbackMock).should().onSuccess(); + then(nodeCtxMock).should(never()).getChainActor(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msgMock, TbNodeConnectionType.ACK, null, null); + } + + @Test + public void givenEmptyStackAndDebugAllThenOnlyFailureEvents_whenOutput_thenVerifyMsgAckAndDebugOutputPersisted() { + // GIVEN + var msgMock = mock(TbMsg.class); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failuresOrUntil(getUntilTime())); + given(msgMock.popFormStack()).willReturn(null); + TbMsgCallback callbackMock = mock(TbMsgCallback.class); + given(msgMock.getCallback()).willReturn(callbackMock); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + + // WHEN + defaultTbContext.output(msgMock, TbNodeConnectionType.SUCCESS); + + // THEN + then(msgMock).should().popFormStack(); + then(callbackMock).should().onProcessingEnd(RULE_NODE_ID); + then(callbackMock).should().onSuccess(); + then(nodeCtxMock).should(never()).getChainActor(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msgMock, TbNodeConnectionType.ACK, null, null); + } + + @Test + public void givenDebugFailuresEvents_whenEnqueueForTellFailure_thenVerifyDebugOutputPersisted() { + // GIVEN + var msg = getTbMsgWithQueueName(); + var tpi = new TopicPartitionInfo(DataConstants.MAIN_QUEUE_TOPIC, TENANT_ID, 0, true); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + var tbClusterServiceMock = mock(TbClusterService.class); + + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(mainCtxMock.resolve(any(ServiceType.class), anyString(), any(TenantId.class), any(EntityId.class))).willReturn(tpi); + given(mainCtxMock.getClusterService()).willReturn(tbClusterServiceMock); + + // WHEN + defaultTbContext.enqueueForTellFailure(msg, EXCEPTION); + + // THEN + then(mainCtxMock).should().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TENANT_ID, TENANT_ID); + TbMsg expectedTbMsg = TbMsg.newMsg(msg, msg.getQueueName(), RULE_CHAIN_ID, RULE_NODE_ID); + checkEnqueueForTellFailurePushMsgToRuleEngine(tbClusterServiceMock, tpi, expectedTbMsg); + ArgumentCaptor tbMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + then(mainCtxMock).should().persistDebugOutput(eq(TENANT_ID), eq(RULE_NODE_ID), tbMsgCaptor.capture(), eq(TbNodeConnectionType.FAILURE), isNull(), eq(EXCEPTION_MSG)); + TbMsg actualTbMsg = tbMsgCaptor.getValue(); + assertThat(actualTbMsg).usingRecursiveComparison() + .ignoringFields("id", "ctx") + .isEqualTo(expectedTbMsg); + then(mainCtxMock).should().getClusterService(); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(tbClusterServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenDebugDisabled_whenEnqueueForTellFailure_thenVerifyDebugOutputNotPersisted() { + // GIVEN + var msg = getTbMsgWithQueueName(); + var tpi = new TopicPartitionInfo(DataConstants.MAIN_QUEUE_TOPIC, TENANT_ID, 0, true); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + var tbClusterServiceMock = mock(TbClusterService.class); + + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(mainCtxMock.resolve(any(ServiceType.class), anyString(), any(TenantId.class), any(EntityId.class))).willReturn(tpi); + given(mainCtxMock.getClusterService()).willReturn(tbClusterServiceMock); + + // WHEN + defaultTbContext.enqueueForTellFailure(msg, EXCEPTION); + + // THEN + then(mainCtxMock).should().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TENANT_ID, TENANT_ID); + TbMsg expectedTbMsg = TbMsg.newMsg(msg, msg.getQueueName(), RULE_CHAIN_ID, RULE_NODE_ID); + checkEnqueueForTellFailurePushMsgToRuleEngine(tbClusterServiceMock, tpi, expectedTbMsg); + then(mainCtxMock).should().getClusterService(); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(tbClusterServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenDebugAllEvents_whenEnqueueForTellFailure_thenVerifyDebugOutputPersisted() { + // GIVEN + var msg = getTbMsgWithQueueName(); + var tpi = new TopicPartitionInfo(DataConstants.MAIN_QUEUE_TOPIC, TENANT_ID, 0, true); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + var tbClusterServiceMock = mock(TbClusterService.class); + + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(mainCtxMock.resolve(any(ServiceType.class), anyString(), any(TenantId.class), any(EntityId.class))).willReturn(tpi); + given(mainCtxMock.getClusterService()).willReturn(tbClusterServiceMock); + + // WHEN + defaultTbContext.enqueueForTellFailure(msg, EXCEPTION); + + // THEN + then(mainCtxMock).should().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TENANT_ID, TENANT_ID); + TbMsg expectedTbMsg = TbMsg.newMsg(msg, msg.getQueueName(), RULE_CHAIN_ID, RULE_NODE_ID); + checkEnqueueForTellFailurePushMsgToRuleEngine(tbClusterServiceMock, tpi, expectedTbMsg); + ArgumentCaptor tbMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + then(mainCtxMock).should().persistDebugOutput(eq(TENANT_ID), eq(RULE_NODE_ID), tbMsgCaptor.capture(), eq(TbNodeConnectionType.FAILURE), isNull(), eq(EXCEPTION_MSG)); + TbMsg actualTbMsg = tbMsgCaptor.getValue(); + assertThat(actualTbMsg).usingRecursiveComparison() + .ignoringFields("id", "ctx") + .isEqualTo(expectedTbMsg); + then(mainCtxMock).should().getClusterService(); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(tbClusterServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenInvalidMsg_whenEnqueueForTellFailure_thenDoNothing() { + // GIVEN + var msgMock = mock(TbMsg.class); + var tpi = new TopicPartitionInfo(DataConstants.MAIN_QUEUE_TOPIC, TENANT_ID, 0, true); + + given(msgMock.getOriginator()).willReturn(TENANT_ID); + given(msgMock.getQueueName()).willReturn(DataConstants.MAIN_QUEUE_NAME); + given(msgMock.isValid()).willReturn(false); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(mainCtxMock.resolve(any(ServiceType.class), anyString(), any(TenantId.class), any(EntityId.class))).willReturn(tpi); + + // WHEN + defaultTbContext.enqueueForTellFailure(msgMock, EXCEPTION); + + // THEN + then(msgMock).should(times(2)).getQueueName(); + then(msgMock).should().getOriginator(); + then(msgMock).should().isValid(); + then(msgMock).shouldHaveNoMoreInteractions(); + + then(mainCtxMock).should().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TENANT_ID, TENANT_ID); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + + then(nodeCtxMock).should(times(2)).getTenantId(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + then(chainActorMock).shouldHaveNoInteractions(); + } + + @MethodSource + @ParameterizedTest + void givenDebugOptions_whenEnqueueForTellNext_thenVerifyDebugOutputPersistedOnlyForDebugAll(boolean debugFailures, long debugAllUntil, String connectionType) { + // GIVEN + var msg = getTbMsgWithQueueName(); + var tpi = new TopicPartitionInfo(DataConstants.MAIN_QUEUE_TOPIC, TENANT_ID, 0, true); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(new DebugSettings(debugFailures, debugAllUntil)); + var tbClusterServiceMock = mock(TbClusterService.class); + + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(mainCtxMock.resolve(any(ServiceType.class), anyString(), any(TenantId.class), any(EntityId.class))).willReturn(tpi); + given(mainCtxMock.getClusterService()).willReturn(tbClusterServiceMock); + + // WHEN + defaultTbContext.enqueueForTellNext(msg, connectionType); + + // THEN + then(mainCtxMock).should().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TENANT_ID, TENANT_ID); + TbMsg expectedTbMsg = TbMsg.newMsg(msg, msg.getQueueName(), RULE_CHAIN_ID, RULE_NODE_ID); + + ArgumentCaptor toRuleEngineMsgCaptor = ArgumentCaptor.forClass(ToRuleEngineMsg.class); + ArgumentCaptor simpleTbQueueCallbackCaptor = ArgumentCaptor.forClass(SimpleTbQueueCallback.class); + then(tbClusterServiceMock).should().pushMsgToRuleEngine(eq(tpi), notNull(UUID.class), toRuleEngineMsgCaptor.capture(), simpleTbQueueCallbackCaptor.capture()); + + ToRuleEngineMsg actualToRuleEngineMsg = toRuleEngineMsgCaptor.getValue(); + assertThat(actualToRuleEngineMsg).usingRecursiveComparison() + .ignoringFields("tbMsg_") + .isEqualTo(ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(TENANT_ID.getId().getMostSignificantBits()) + .setTenantIdLSB(TENANT_ID.getId().getLeastSignificantBits()) + .setTbMsg(TbMsg.toByteString(expectedTbMsg)) + .addAllRelationTypes(List.of(connectionType)).build()); + + var simpleTbQueueCallback = simpleTbQueueCallbackCaptor.getValue(); + assertThat(simpleTbQueueCallback).isNotNull(); + simpleTbQueueCallback.onSuccess(null); + + if (debugAllUntil > 0) { + ArgumentCaptor tbMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + then(mainCtxMock).should().persistDebugOutput(eq(TENANT_ID), eq(RULE_NODE_ID), tbMsgCaptor.capture(), eq(connectionType), isNull(), isNull()); + TbMsg actualTbMsg = tbMsgCaptor.getValue(); + assertThat(actualTbMsg).usingRecursiveComparison() + .ignoringFields("id", "ctx") + .isEqualTo(expectedTbMsg); + } + then(mainCtxMock).should().getClusterService(); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(tbClusterServiceMock).shouldHaveNoMoreInteractions(); + } + + @MethodSource + @ParameterizedTest + void givenDebugOptions_whenEnqueue_thenVerifyDebugOutputPersistedOnlyForDebugAll(boolean debugFailures, long debugAllUntil) { + // GIVEN + var msg = getTbMsgWithQueueName(); + var tpi = new TopicPartitionInfo(DataConstants.MAIN_QUEUE_TOPIC, TENANT_ID, 0, true); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setQueueName(DataConstants.MAIN_QUEUE_NAME); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(new DebugSettings(debugFailures, debugAllUntil)); + var tbClusterServiceMock = mock(TbClusterService.class); + + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(mainCtxMock.resolve(any(ServiceType.class), anyString(), any(TenantId.class), any(EntityId.class))).willReturn(tpi); + given(mainCtxMock.getClusterService()).willReturn(tbClusterServiceMock); + + Consumer onFailure = mock(Consumer.class); + Runnable onSuccess = mock(Runnable.class); + + // WHEN + defaultTbContext.enqueue(msg, onSuccess, onFailure); + + // THEN + then(mainCtxMock).should().resolve(ServiceType.TB_RULE_ENGINE, DataConstants.MAIN_QUEUE_NAME, TENANT_ID, TENANT_ID); + TbMsg expectedTbMsg = TbMsg.newMsg(msg, msg.getQueueName(), RULE_CHAIN_ID, RULE_NODE_ID); + + ArgumentCaptor toRuleEngineMsgCaptor = ArgumentCaptor.forClass(ToRuleEngineMsg.class); + ArgumentCaptor simpleTbQueueCallbackCaptor = ArgumentCaptor.forClass(SimpleTbQueueCallback.class); + then(tbClusterServiceMock).should().pushMsgToRuleEngine(eq(tpi), notNull(UUID.class), toRuleEngineMsgCaptor.capture(), simpleTbQueueCallbackCaptor.capture()); + + ToRuleEngineMsg actualToRuleEngineMsg = toRuleEngineMsgCaptor.getValue(); + assertThat(actualToRuleEngineMsg).usingRecursiveComparison() + .ignoringFields("tbMsg_") + .isEqualTo(ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(TENANT_ID.getId().getMostSignificantBits()) + .setTenantIdLSB(TENANT_ID.getId().getLeastSignificantBits()) + .setTbMsg(TbMsg.toByteString(expectedTbMsg)) + .build()); + + var simpleTbQueueCallback = simpleTbQueueCallbackCaptor.getValue(); + assertThat(simpleTbQueueCallback).isNotNull(); + simpleTbQueueCallback.onSuccess(null); + + if (debugAllUntil > 0) { + then(mainCtxMock).should().persistDebugOutput(eq(TENANT_ID), eq(RULE_NODE_ID), eq(msg), eq(TbNodeConnectionType.TO_ROOT_RULE_CHAIN), nullable(Throwable.class), nullable(String.class)); + } + then(mainCtxMock).should().getClusterService(); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(tbClusterServiceMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenDebugFailuress_whenTellFailure_thenVerifyDebugOutputPersisted() { + // GIVEN + var msg = getTbMsg(); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.failures()); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellFailure(msg, EXCEPTION); + + // THEN + var expectedRuleNodeToRuleChainTellNextMsg = new RuleNodeToRuleChainTellNextMsg( + RULE_CHAIN_ID, + RULE_NODE_ID, + Collections.singleton(TbNodeConnectionType.FAILURE), + msg, + EXCEPTION_MSG + ); + then(chainActorMock).should().tell(expectedRuleNodeToRuleChainTellNextMsg); + then(chainActorMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).should().getChainActor(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, TbNodeConnectionType.FAILURE, EXCEPTION, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenDebugDisabled_whenTellFailure_thenVerifyDebugOutputNotPersisted() { + // GIVEN + var msg = getTbMsg(); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellFailure(msg, EXCEPTION); + + // THEN + var expectedRuleNodeToRuleChainTellNextMsg = new RuleNodeToRuleChainTellNextMsg( + RULE_CHAIN_ID, + RULE_NODE_ID, + Collections.singleton(TbNodeConnectionType.FAILURE), + msg, + EXCEPTION_MSG + ); + then(chainActorMock).should().tell(expectedRuleNodeToRuleChainTellNextMsg); + then(chainActorMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).should().getChainActor(); + then(mainCtxMock).shouldHaveNoInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @Test + public void givenDebugAllEvents_whenTellFailure_thenVerifyDebugOutputPersisted() { + // GIVEN + var msg = getTbMsg(); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(DebugSettings.until(getUntilTime())); + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellFailure(msg, EXCEPTION); + + // THEN + var expectedRuleNodeToRuleChainTellNextMsg = new RuleNodeToRuleChainTellNextMsg( + RULE_CHAIN_ID, + RULE_NODE_ID, + Collections.singleton(TbNodeConnectionType.FAILURE), + msg, + EXCEPTION_MSG + ); + then(chainActorMock).should().tell(expectedRuleNodeToRuleChainTellNextMsg); + then(chainActorMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).should().getChainActor(); + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, TbNodeConnectionType.FAILURE, EXCEPTION, null); + then(mainCtxMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).shouldHaveNoMoreInteractions(); + } + + @MethodSource + @ParameterizedTest + void givenDebugFailuresAndDebugAllAndConnectionAndPersistedResultOptions_whenTellNext_thenVerifyDebugOutputPersistence(boolean debugFailures, + long debugAllUntil, + String connection, + boolean shouldPersist, + boolean shouldPersistAfterDurationTime) { + // GIVEN + var callbackMock = mock(TbMsgCallback.class); + var msg = getTbMsgWithCallback(callbackMock); + var ruleNode = new RuleNode(RULE_NODE_ID); + ruleNode.setRuleChainId(RULE_CHAIN_ID); + ruleNode.setDebugSettings(new DebugSettings(debugFailures, debugAllUntil)); + if (shouldPersist) { + given(nodeCtxMock.getTenantId()).willReturn(TENANT_ID); + } + given(nodeCtxMock.getSelf()).willReturn(ruleNode); + given(nodeCtxMock.getChainActor()).willReturn(chainActorMock); + + // WHEN + defaultTbContext.tellNext(msg, connection); + + // THEN + if (shouldPersist) { + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, connection, null, null); + } + + // GIVEN + Mockito.clearInvocations(mainCtxMock); + ruleNode.setDebugSettings(new DebugSettings(ruleNode.getDebugSettings().isFailuresEnabled(), 0)); + + // WHEN + defaultTbContext.tellNext(msg, connection); + + // THEN + if (shouldPersistAfterDurationTime) { + then(mainCtxMock).should().persistDebugOutput(TENANT_ID, RULE_NODE_ID, msg, connection, null, null); + } + } + + private void checkTellNextCommonLogic(TbMsgCallback callbackMock, String nodeConnection, TbMsg msg) { + checkTellNextCommonLogic(callbackMock, Collections.singleton(nodeConnection), msg); + } + + private void checkTellNextCommonLogic(TbMsgCallback callbackMock, Set nodeConnections, TbMsg msg) { + then(callbackMock).should().onProcessingEnd(RULE_NODE_ID); + then(callbackMock).shouldHaveNoMoreInteractions(); + var expectedRuleNodeToRuleChainTellNextMsg = new RuleNodeToRuleChainTellNextMsg( + RULE_CHAIN_ID, + RULE_NODE_ID, + nodeConnections, + msg, + null); + then(chainActorMock).should().tell(expectedRuleNodeToRuleChainTellNextMsg); + then(chainActorMock).shouldHaveNoMoreInteractions(); + } + + private void checkOutputCommonLogic(TbMsg msg, String nodeConnection) { + then(msg).should().popFormStack(); + var expectedRuleChainOutputMsg = new RuleChainOutputMsg( + RULE_CHAIN_ID, + RULE_NODE_ID, + nodeConnection, + msg); + then(chainActorMock).should().tell(expectedRuleChainOutputMsg); + then(chainActorMock).shouldHaveNoMoreInteractions(); + then(nodeCtxMock).should().getChainActor(); + } + + private void checkEnqueueForTellFailurePushMsgToRuleEngine(TbClusterService tbClusterService, TopicPartitionInfo tpi, TbMsg expectedTbMsg) { + ArgumentCaptor toRuleEngineMsgCaptor = ArgumentCaptor.forClass(ToRuleEngineMsg.class); + ArgumentCaptor simpleTbQueueCallbackCaptor = ArgumentCaptor.forClass(SimpleTbQueueCallback.class); + then(tbClusterService).should().pushMsgToRuleEngine(eq(tpi), notNull(UUID.class), toRuleEngineMsgCaptor.capture(), simpleTbQueueCallbackCaptor.capture()); + + ToRuleEngineMsg actualToRuleEngineMsg = toRuleEngineMsgCaptor.getValue(); + assertThat(actualToRuleEngineMsg).usingRecursiveComparison() + .ignoringFields("tbMsg_") + .isEqualTo(ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(TENANT_ID.getId().getMostSignificantBits()) + .setTenantIdLSB(TENANT_ID.getId().getLeastSignificantBits()) + .setTbMsg(TbMsg.toByteString(expectedTbMsg)) + .setFailureMessage(EXCEPTION_MSG) + .addAllRelationTypes(List.of(TbNodeConnectionType.FAILURE)).build()); + + var simpleTbQueueCallback = simpleTbQueueCallbackCaptor.getValue(); + assertThat(simpleTbQueueCallback).isNotNull(); + simpleTbQueueCallback.onSuccess(null); + } + + private static Stream givenDebugOptions_whenEnqueueForTellNext_thenVerifyDebugOutputPersistedOnlyForDebugAll() { + return Stream.of( + Arguments.of(false, getUntilTime(), TbNodeConnectionType.OTHER), + Arguments.of(true, getUntilTime(), TbNodeConnectionType.OTHER), + Arguments.of(true, 0, TbNodeConnectionType.TRUE), + Arguments.of(false, 0, TbNodeConnectionType.FALSE) + ); + } + + private static Stream givenDebugOptions_whenEnqueue_thenVerifyDebugOutputPersistedOnlyForDebugAll() { + return Stream.of( + Arguments.of(false, getUntilTime()), + Arguments.of(true, getUntilTime()), + Arguments.of(true, 0), + Arguments.of(false, 0) + ); + } + + private static Stream givenDebugFailuresAndDebugAllAndConnectionAndPersistedResultOptions_whenTellNext_thenVerifyDebugOutputPersistence() { + return Stream.of( + Arguments.of(false, getUntilTime(), TbNodeConnectionType.SUCCESS, true, false), + Arguments.of(false, getUntilTime(), TbNodeConnectionType.FAILURE, true, false), + Arguments.of(true, getUntilTime(), TbNodeConnectionType.SUCCESS, true, false), + Arguments.of(true, getUntilTime(), TbNodeConnectionType.FAILURE, true, true), + Arguments.of(true, 0, TbNodeConnectionType.SUCCESS, false, false), + Arguments.of(true, 0, TbNodeConnectionType.FAILURE, true, true), + Arguments.of(false, 0, TbNodeConnectionType.SUCCESS, false, false), + Arguments.of(false, 0, TbNodeConnectionType.FAILURE, false, false) + ); + } + + private TbMsg getTbMsgWithCallback(TbMsgCallback callback) { + return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING, callback); + } + + private TbMsg getTbMsgWithQueueName() { + return TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + } + + private TbMsg getTbMsg() { + return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, TENANT_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); + } + + private static long getUntilTime() { + return getUntilTime(15); + } + + private static long getUntilTime(int maxRuleNodeDebugModeDurationMinutes) { + return System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(maxRuleNodeDebugModeDurationMinutes); + } +} diff --git a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java index 46a5d81d7f..10db03a6c2 100644 --- a/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.device.profile.AlarmCondition; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; @@ -103,7 +104,6 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.Random; import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -125,8 +125,6 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { protected EdgeImitator edgeImitator; protected Edge edge; - private final Random random = new Random(); - @Autowired protected EdgeEventService edgeEventService; @@ -166,7 +164,8 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { } private RuleChainId getEdgeRootRuleChainId() throws Exception { - return doGetTypedWithPageLink("/api/ruleChains?type={type}&", new TypeReference>() {}, + return doGetTypedWithPageLink("/api/ruleChains?type={type}&", new TypeReference>() { + }, new PageLink(100, 0, "Edge Root Rule Chain"), "EDGE") .getData().get(0).getId(); @@ -210,7 +209,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest { protected void updateRootRuleChainMetadata() throws Exception { RuleChainId rootRuleChainId = getEdgeRootRuleChainId(); RuleChainMetaData rootRuleChainMetadata = doGet("/api/ruleChain/" + rootRuleChainId.getId().toString() + "/metadata", RuleChainMetaData.class); - rootRuleChainMetadata.getNodes().forEach(n -> n.setDebugMode(random.nextBoolean())); + rootRuleChainMetadata.getNodes().forEach(n -> n.setDebugSettings(DebugSettings.all())); doPost("/api/ruleChain/metadata", rootRuleChainMetadata, RuleChainMetaData.class); } diff --git a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java index eaf4468156..23a58664ed 100644 --- a/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java @@ -21,6 +21,7 @@ import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; import org.thingsboard.rule.engine.util.TbMsgSource; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.rule.RuleChain; @@ -38,6 +39,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.concurrent.TimeUnit; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -228,7 +230,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest { // update metadata for root rule chain edgeImitator.expectMessageAmount(1); - metaData.getNodes().forEach(n -> n.setDebugMode(true)); + metaData.getNodes().forEach(n -> n.setDebugSettings(DebugSettings.all())); doPost("/api/ruleChain/metadata", metaData, RuleChainMetaData.class); Assert.assertTrue(edgeImitator.waitForMessages()); ruleChainUpdateMsgOpt = edgeImitator.findMessageByType(RuleChainUpdateMsg.class); diff --git a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java index 143c6c72a1..fba5f2187f 100644 --- a/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.event.Event; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -144,7 +145,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode1.setDebugMode(true); + ruleNode1.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); configuration1.setFetchTo(TbMsgSource.METADATA); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); @@ -154,7 +155,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); + ruleNode2.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setFetchTo(TbMsgSource.METADATA); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); @@ -250,7 +251,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode1.setDebugMode(true); + ruleNode1.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); configuration1.setFetchTo(TbMsgSource.METADATA); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); @@ -259,7 +260,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule RuleNode ruleNode12 = new RuleNode(); ruleNode12.setName("Simple Rule Node 1"); ruleNode12.setType(org.thingsboard.rule.engine.flow.TbRuleChainInputNode.class.getName()); - ruleNode12.setDebugMode(true); + ruleNode12.setDebugSettings(DebugSettings.all()); TbRuleChainInputNodeConfiguration configuration12 = new TbRuleChainInputNodeConfiguration(); configuration12.setRuleChainId(secondaryRuleChain.getId().getId().toString()); ruleNode12.setConfiguration(JacksonUtil.valueToTree(configuration12)); @@ -284,7 +285,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); + ruleNode2.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setFetchTo(TbMsgSource.METADATA); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index 4c4e887644..d8bd02ec7f 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EventInfo; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -97,7 +98,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac ruleNode.setName("Simple Rule Node"); ruleNode.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode.setDebugMode(true); + ruleNode.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration = new TbGetAttributesNodeConfiguration(); configuration.setFetchTo(TbMsgSource.METADATA); configuration.setServerAttributeNames(Collections.singletonList("serverAttributeKey")); diff --git a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java index c7f2e37211..e4a972cacc 100644 --- a/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.event.EventType; import org.thingsboard.server.common.data.event.LifecycleEvent; import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; @@ -496,7 +497,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode1.setDebugMode(true); + ruleNode1.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration1)); @@ -505,7 +506,7 @@ public class HousekeeperServiceTest extends AbstractControllerTest { ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); + ruleNode2.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); diff --git a/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java b/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java index 55f15516f4..0beaca9af9 100644 --- a/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sync/ie/ExportImportServiceSqlTest.java @@ -471,7 +471,6 @@ public class ExportImportServiceSqlTest extends AbstractControllerTest { RuleNode ruleNode1 = new RuleNode(); ruleNode1.setName("Generator 1"); ruleNode1.setType(TbMsgGeneratorNode.class.getName()); - ruleNode1.setDebugMode(true); TbMsgGeneratorNodeConfiguration configuration1 = new TbMsgGeneratorNodeConfiguration(); configuration1.setOriginatorType(originatorId.getEntityType()); configuration1.setOriginatorId(originatorId.getId().toString()); @@ -481,7 +480,6 @@ public class ExportImportServiceSqlTest extends AbstractControllerTest { ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); @@ -510,7 +508,6 @@ public class ExportImportServiceSqlTest extends AbstractControllerTest { ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode1.setDebugMode(true); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration1)); @@ -519,7 +516,6 @@ public class ExportImportServiceSqlTest extends AbstractControllerTest { ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); diff --git a/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java b/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java index 2e01d700a9..782266ce9c 100644 --- a/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; @@ -870,7 +871,7 @@ public class VersionControlTest extends AbstractControllerTest { RuleNode ruleNode1 = new RuleNode(); ruleNode1.setName("Generator 1"); ruleNode1.setType(TbMsgGeneratorNode.class.getName()); - ruleNode1.setDebugMode(true); + ruleNode1.setDebugSettings(DebugSettings.all()); TbMsgGeneratorNodeConfiguration configuration1 = new TbMsgGeneratorNodeConfiguration(); configuration1.setOriginatorType(originatorId.getEntityType()); configuration1.setOriginatorId(originatorId.getId().toString()); @@ -880,7 +881,7 @@ public class VersionControlTest extends AbstractControllerTest { ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); + ruleNode2.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); @@ -908,7 +909,7 @@ public class VersionControlTest extends AbstractControllerTest { ruleNode1.setName("Simple Rule Node 1"); ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode1.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode1.setDebugMode(true); + ruleNode1.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); ruleNode1.setConfiguration(JacksonUtil.valueToTree(configuration1)); @@ -917,7 +918,7 @@ public class VersionControlTest extends AbstractControllerTest { ruleNode2.setName("Simple Rule Node 2"); ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); ruleNode2.setConfigurationVersion(TbGetAttributesNode.class.getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class).version()); - ruleNode2.setDebugMode(true); + ruleNode2.setDebugSettings(DebugSettings.all()); TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); ruleNode2.setConfiguration(JacksonUtil.valueToTree(configuration2)); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java index 5fafb885fe..4c5e2a3112 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/tenant/TbTenantProfileCache.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.tenant; +import org.thingsboard.server.common.data.SystemParams; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceIdInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceIdInfo.java index 44f52e6d74..1655033042 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceIdInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceIdInfo.java @@ -35,7 +35,7 @@ public class DeviceIdInfo implements Serializable, HasTenantId { private final DeviceId deviceId; public DeviceIdInfo(UUID tenantId, UUID customerId, UUID deviceId) { - this.tenantId = new TenantId(tenantId); + this.tenantId = TenantId.fromUUID(tenantId); this.customerId = customerId != null ? new CustomerId(customerId) : null; this.deviceId = new DeviceId(deviceId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java index 3bc879ff76..ceb01da52b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java @@ -63,7 +63,7 @@ public class DeviceProfileInfo extends EntityInfo { public DeviceProfileInfo(UUID uuid, UUID tenantId, String name, String image, UUID defaultDashboardId, DeviceProfileType type, DeviceTransportType transportType) { super(EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE_PROFILE, uuid), name); - this.tenantId = new TenantId(tenantId); + this.tenantId = TenantId.fromUUID(tenantId); this.image = image; this.defaultDashboardId = defaultDashboardId != null ? new DashboardId(defaultDashboardId) : null; this.type = type; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasDebugSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasDebugSettings.java new file mode 100644 index 0000000000..075db90166 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasDebugSettings.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data; + +import org.thingsboard.server.common.data.debug.DebugSettings; + +public interface HasDebugSettings { + + @Deprecated + boolean isDebugMode(); + + @Deprecated + void setDebugMode(boolean debugMode); + + DebugSettings getDebugSettings(); + + void setDebugSettings(DebugSettings debugSettings); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java index 5b4312f7b0..abe1932327 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SystemParams.java @@ -32,4 +32,6 @@ public class SystemParams { long maxDatapointsLimit; long maxResourceSize; boolean mobileQrEnabled; + int maxDebugModeDurationMinutes; + String ruleChainDebugPerTenantLimitsConfiguration; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java index 49b3052425..0b5c5d2b7f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfileInfo.java @@ -57,7 +57,7 @@ public class AssetProfileInfo extends EntityInfo { public AssetProfileInfo(UUID uuid, UUID tenantId, String name, String image, UUID defaultDashboardId) { super(EntityIdFactory.getByTypeAndUuid(EntityType.ASSET_PROFILE, uuid), name); - this.tenantId = new TenantId(tenantId); + this.tenantId = TenantId.fromUUID(tenantId); this.image = image; this.defaultDashboardId = defaultDashboardId != null ? new DashboardId(defaultDashboardId) : null; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/debug/DebugSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/debug/DebugSettings.java new file mode 100644 index 0000000000..a3bb39202a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/debug/DebugSettings.java @@ -0,0 +1,59 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.data.debug; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class DebugSettings { + + private static DebugSettings DEBUG_OFF = new DebugSettings(false, 0); + private static DebugSettings DEBUG_FAILURES = new DebugSettings(true, 0); + + public DebugSettings(boolean failuresEnabled, long allEnabledUntil) { + this.failuresEnabled = failuresEnabled; + this.allEnabled = false; + this.allEnabledUntil = allEnabledUntil; + } + + @Schema(description = "Debug failures. ", example = "false") + private boolean failuresEnabled; + @Schema(description = "Debug All. Used as a trigger for updating debugAllUntil.", example = "false") + private boolean allEnabled; + @Schema(description = "Timestamp of the end time for the processing debug events.") + private long allEnabledUntil; + + public static DebugSettings off() {return DebugSettings.DEBUG_OFF;} + + public static DebugSettings failures() {return DebugSettings.DEBUG_FAILURES;} + + public static DebugSettings until(long ts) {return new DebugSettings(false, ts);} + + public static DebugSettings failuresOrUntil(long ts) {return new DebugSettings(true, ts);} + + public static DebugSettings all() { + var ds = new DebugSettings(); + ds.setAllEnabled(true); + return ds; + } + + public DebugSettings copy(long maxDebugAllUntil) { + return new DebugSettings(failuresEnabled, allEnabled ? maxDebugAllUntil : Math.min(allEnabledUntil, maxDebugAllUntil)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 8e2688248e..542336b408 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -120,7 +120,7 @@ public class EntityIdFactory { public static EntityId getByEdgeEventTypeAndUuid(EdgeEventType edgeEventType, UUID uuid) { switch (edgeEventType) { case TENANT: - return new TenantId(uuid); + return TenantId.fromUUID(uuid); case CUSTOMER: return new CustomerId(uuid); case USER: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java index e160eb59d0..ff8739c04d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/TenantId.java @@ -40,7 +40,9 @@ public final class TenantId extends UUIDBased implements EntityId { return tenants.computeIfAbsent(id, TenantId::new); } - //default constructor is still available due to possible usage in extensions + // Please, use TenantId.fromUUID instead + // Default constructor is still available due to possible usage in extensions + @Deprecated public TenantId(UUID id) { super(id); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java index e0a1abf9ff..6cf310f7b3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/UUIDBased.java @@ -25,6 +25,9 @@ public abstract class UUIDBased implements HasUUID, Serializable { private static final long serialVersionUID = 1L; + /** Cache the hash code */ + private transient int hash; // Default to 0. The hash code calculated for this object likely never be zero + private final UUID id; public UUIDBased() { @@ -43,10 +46,12 @@ public abstract class UUIDBased implements HasUUID, Serializable { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); - return result; + if (hash == 0) { + final int prime = 31; + int result = 1; + hash = prime * result + ((id == null) ? 0 : id.hashCode()); + } + return hash; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java index 2f466c76b0..a6d56ec221 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbNodeConnectionType.java @@ -23,9 +23,13 @@ public final class TbNodeConnectionType { public static final String SUCCESS = "Success"; public static final String FAILURE = "Failure"; + public static final String ACK = "ACK"; + public static final String TRUE = "True"; public static final String FALSE = "False"; public static final String OTHER = "Other"; + public static final String TO_ROOT_RULE_CHAIN = "To Root Rule Chain"; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java index 542debcfdd..c18fa572e9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java @@ -16,13 +16,17 @@ package org.thingsboard.server.common.data.rule; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; +import org.thingsboard.server.common.data.HasDebugSettings; import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.validation.Length; @@ -32,7 +36,8 @@ import org.thingsboard.server.common.data.validation.NoXss; @Data @EqualsAndHashCode(callSuper = true) @Slf4j -public class RuleNode extends BaseDataWithAdditionalInfo implements HasName { +@JsonIgnoreProperties(ignoreUnknown = true) +public class RuleNode extends BaseDataWithAdditionalInfo implements HasName, HasDebugSettings { private static final long serialVersionUID = -5656679015121235465L; @@ -45,15 +50,18 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements @Length(fieldName = "name") @Schema(description = "User defined name of the rule node. Used on UI and for logging. ", example = "Process sensor reading") private String name; - @Schema(description = "Enable/disable debug. ", example = "false") + @Deprecated + @Schema(description = "Enable/disable debug. ", example = "false", deprecated = true) private boolean debugMode; + @Schema(description = "Debug settings object.") + private DebugSettings debugSettings; @Schema(description = "Enable/disable singleton mode. ", example = "false") private boolean singletonMode; @Schema(description = "Queue name. ", example = "Main") private String queueName; @Schema(description = "Version of rule node configuration. ", example = "0") private int configurationVersion; - @Schema(description = "JSON with the rule node configuration. Structure depends on the rule node implementation.", implementation = com.fasterxml.jackson.databind.JsonNode.class) + @Schema(description = "JSON with the rule node configuration. Structure depends on the rule node implementation.", implementation = JsonNode.class) private transient JsonNode configuration; @JsonIgnore private byte[] configurationBytes; @@ -73,7 +81,7 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements this.ruleChainId = ruleNode.getRuleChainId(); this.type = ruleNode.getType(); this.name = ruleNode.getName(); - this.debugMode = ruleNode.isDebugMode(); + this.debugSettings = ruleNode.getDebugSettings(); this.singletonMode = ruleNode.isSingletonMode(); this.setConfiguration(ruleNode.getConfiguration()); this.externalId = ruleNode.getExternalId(); @@ -93,9 +101,9 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements } @Schema(description = "JSON object with the Rule Node Id. " + - "Specify this field to update the Rule Node. " + - "Referencing non-existing Rule Node Id will cause error. " + - "Omit this field to create new rule node.") + "Specify this field to update the Rule Node. " + + "Referencing non-existing Rule Node Id will cause error. " + + "Omit this field to create new rule node.") @Override public RuleNodeId getId() { return super.getId(); @@ -107,10 +115,21 @@ public class RuleNode extends BaseDataWithAdditionalInfo implements return super.getCreatedTime(); } - @Schema(description = "Additional parameters of the rule node. Contains 'layoutX' and 'layoutY' properties for visualization.", implementation = com.fasterxml.jackson.databind.JsonNode.class) + @Schema(description = "Additional parameters of the rule node. Contains 'layoutX' and 'layoutY' properties for visualization.", implementation = JsonNode.class) @Override public JsonNode getAdditionalInfo() { return super.getAdditionalInfo(); } + // Getter is ignored for serialization + @JsonIgnore + public boolean isDebugMode() { + return debugMode; + } + + // Setter is annotated for deserialization + @JsonSetter + public void setDebugMode(boolean debugMode) { + this.debugMode = debugMode; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 1e4ea75f3f..6aa9075a79 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -95,6 +95,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxDPStorageDays; @Schema(example = "50") private int maxRuleNodeExecutionsPerMessage; + @Schema(example = "15") + private int maxDebugModeDurationMinutes; @Schema(example = "0") private long maxEmails; @Schema(example = "true") diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index 11d320ffa7..4dc762e14c 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/common/edge-api/src/main/proto/edge.proto @@ -169,11 +169,12 @@ message RuleNodeProto { int64 idLSB = 2; string type = 3; string name = 4; - bool debugMode = 5; + bool debugMode = 5 [deprecated = true]; string configuration = 6; string additionalInfo = 7; bool singletonMode = 8; int32 configurationVersion = 9; + string debugSettings = 10; } message NodeConnectionInfoProto { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java index 8cafc2bfd0..6eb54e5447 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java @@ -297,7 +297,7 @@ public class LwM2MClientSerDes { if (tenantId != null) { Field tenantIdField = lwM2mClientClass.getDeclaredField("tenantId"); tenantIdField.setAccessible(true); - tenantIdField.set(lwM2mClient, new TenantId(UUID.fromString(tenantId.getAsString()))); + tenantIdField.set(lwM2mClient, TenantId.fromUUID(UUID.fromString(tenantId.getAsString()))); } JsonElement deviceId = o.get("deviceId"); diff --git a/common/util/src/main/java/org/thingsboard/common/util/DebugModeUtil.java b/common/util/src/main/java/org/thingsboard/common/util/DebugModeUtil.java new file mode 100644 index 0000000000..9ba9038594 --- /dev/null +++ b/common/util/src/main/java/org/thingsboard/common/util/DebugModeUtil.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.common.util; + +import org.thingsboard.server.common.data.HasDebugSettings; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; + +import java.util.Set; + +public final class DebugModeUtil { + + private static final int DEBUG_MODE_DEFAULT_DURATION_MINUTES = 15; + + private DebugModeUtil() { + } + + public static int getMaxDebugAllDuration(int tenantProfileDuration, int systemDefaultDuration) { + if (tenantProfileDuration > 0) { + return tenantProfileDuration; + } else { + return systemDefaultDuration > 0 ? systemDefaultDuration : DEBUG_MODE_DEFAULT_DURATION_MINUTES; + } + } + + public static boolean isDebugAllAvailable(HasDebugSettings debugSettingsAware) { + var debugSettings = debugSettingsAware.getDebugSettings(); + return debugSettings != null && debugSettings.getAllEnabledUntil() > System.currentTimeMillis(); + } + + public static boolean isDebugAvailable(HasDebugSettings debugSettingsAware, String nodeConnection) { + if (isDebugAllAvailable(debugSettingsAware)) { + return true; + } else { + var debugSettings = debugSettingsAware.getDebugSettings(); + return debugSettings != null && debugSettings.isFailuresEnabled() && TbNodeConnectionType.FAILURE.equals(nodeConnection); + } + } + + public static boolean isDebugFailuresAvailable(HasDebugSettings debugSettingsAware, Set nodeConnections) { + if (isDebugAllAvailable(debugSettingsAware)) { + return true; + } else { + var debugSettings = debugSettingsAware.getDebugSettings(); + return debugSettings != null && nodeConnections != null && debugSettings.isFailuresEnabled() && nodeConnections.contains(TbNodeConnectionType.FAILURE); + } + } +} diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/VersionControlRequestCtx.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/VersionControlRequestCtx.java index 74c0257c41..affa902bb0 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/VersionControlRequestCtx.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/VersionControlRequestCtx.java @@ -34,7 +34,7 @@ public class VersionControlRequestCtx { public VersionControlRequestCtx(ToVersionControlServiceMsg msg, RepositorySettings settings) { this.nodeId = msg.getNodeId(); this.requestId = new UUID(msg.getRequestIdMSB(), msg.getRequestIdLSB()); - this.tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + this.tenantId = TenantId.fromUUID(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); this.settings = settings; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index 6b88f19699..571f7211ad 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java @@ -18,10 +18,14 @@ package org.thingsboard.server.dao.entity; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; +import org.thingsboard.common.util.DebugModeUtil; import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.HasDebugSettings; import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -33,11 +37,13 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.housekeeper.CleanUpService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.TimeUnit; @Slf4j public abstract class AbstractEntityService { @@ -68,6 +74,13 @@ public abstract class AbstractEntityService { @Lazy protected CleanUpService cleanUpService; + @Autowired + @Lazy + private TbTenantProfileCache tbTenantProfileCache; + + @Value("${debug.settings.default_duration:15}") + private int defaultDebugDurationMinutes; + protected void createRelation(TenantId tenantId, EntityRelation relation) { log.debug("Creating relation: {}", relation); relationService.saveRelation(tenantId, relation); @@ -124,4 +137,17 @@ public abstract class AbstractEntityService { } } } + + protected void updateDebugSettings(TenantId tenantId, HasDebugSettings entity, long now) { + if (entity.getDebugSettings() != null) { + entity.setDebugSettings(entity.getDebugSettings().copy(getMaxDebugAllUntil(tenantId, now))); + } else if (entity.isDebugMode()) { + entity.setDebugSettings(DebugSettings.failuresOrUntil(getMaxDebugAllUntil(tenantId, now))); + entity.setDebugMode(false); + } + } + + private long getMaxDebugAllUntil(TenantId tenantId, long now) { + return now + TimeUnit.MINUTES.toMillis(DebugModeUtil.getMaxDebugAllDuration(tbTenantProfileCache.get(tenantId).getDefaultProfileConfiguration().getMaxDebugModeDurationMinutes(), defaultDebugDurationMinutes)); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 28b6e6ebc2..15e688b7d3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -398,6 +398,7 @@ public class ModelConstants { public static final String EVENT_MESSAGE_COLUMN_NAME = "e_message"; public static final String DEBUG_MODE = "debug_mode"; + public static final String DEBUG_SETTINGS = "debug_settings"; public static final String SINGLETON_MODE = "singleton_mode"; public static final String QUEUE_NAME = "queue_name"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientEntity.java index 8428d346ce..df76374bc1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientEntity.java @@ -168,7 +168,7 @@ public class OAuth2ClientEntity extends BaseSqlEntity { OAuth2Client registration = new OAuth2Client(); registration.setId(new OAuth2ClientId(id)); registration.setCreatedTime(createdTime); - registration.setTenantId(new TenantId(tenantId)); + registration.setTenantId(TenantId.fromUUID(tenantId)); registration.setTitle(title); registration.setAdditionalInfo(additionalInfo); registration.setMapperConfig( diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueEntity.java index 72716bec12..87849f4a7e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueEntity.java @@ -97,7 +97,7 @@ public class QueueEntity extends BaseSqlEntity { public Queue toData() { Queue queue = new Queue(new QueueId(getUuid())); queue.setCreatedTime(createdTime); - queue.setTenantId(new TenantId(tenantId)); + queue.setTenantId(TenantId.fromUUID(tenantId)); queue.setName(name); queue.setTopic(topic); queue.setPollInterval(pollInterval); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java index 96800a4d38..7f24119831 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java @@ -61,7 +61,7 @@ public class QueueStatsEntity extends BaseSqlEntity { public QueueStats toData() { QueueStats queueStats = new QueueStats(new QueueStatsId(getUuid())); queueStats.setCreatedTime(createdTime); - queueStats.setTenantId(new TenantId(tenantId)); + queueStats.setTenantId(TenantId.fromUUID(tenantId)); queueStats.setQueueName(queueName); queueStats.setServiceId(serviceId); return queueStats; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java index bdba4549bb..fae3d4e269 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java @@ -22,6 +22,9 @@ import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.debug.DebugSettings; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.rule.RuleNode; @@ -58,8 +61,8 @@ public class RuleNodeEntity extends BaseSqlEntity { @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; - @Column(name = ModelConstants.DEBUG_MODE) - private boolean debugMode; + @Column(name = ModelConstants.DEBUG_SETTINGS) + private String debugSettings; @Column(name = ModelConstants.SINGLETON_MODE) private boolean singletonMode; @@ -83,7 +86,7 @@ public class RuleNodeEntity extends BaseSqlEntity { } this.type = ruleNode.getType(); this.name = ruleNode.getName(); - this.debugMode = ruleNode.isDebugMode(); + this.debugSettings = JacksonUtil.toString(ruleNode.getDebugSettings()); this.singletonMode = ruleNode.isSingletonMode(); this.queueName = ruleNode.getQueueName(); this.configurationVersion = ruleNode.getConfigurationVersion(); @@ -103,7 +106,7 @@ public class RuleNodeEntity extends BaseSqlEntity { } ruleNode.setType(type); ruleNode.setName(name); - ruleNode.setDebugMode(debugMode); + ruleNode.setDebugSettings(JacksonUtil.fromString(debugSettings, DebugSettings.class)); ruleNode.setSingletonMode(singletonMode); ruleNode.setQueueName(queueName); ruleNode.setConfigurationVersion(configurationVersion); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 6f34437e0d..0e51cf256a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -214,9 +214,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } RuleChainId ruleChainId = ruleChain.getId(); if (nodes != null) { + long now = System.currentTimeMillis(); for (RuleNode node : toAddOrUpdate) { node.setRuleChainId(ruleChainId); node = ruleNodeUpdater.apply(node); + + updateDebugSettings(tenantId, node, now); + RuleChainDataValidator.validateRuleNode(node); RuleNode savedNode = ruleNodeDao.save(tenantId, node); relations.add(new EntityRelation(ruleChainMetaData.getRuleChainId(), savedNode.getId(), @@ -261,7 +265,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC layout.remove("description"); layout.remove("ruleChainNodeId"); targetNode.setAdditionalInfo(layout); - targetNode.setDebugMode(false); targetNode = ruleNodeDao.save(tenantId, targetNode); EntityRelation sourceRuleChainToRuleNode = new EntityRelation(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 2f3893d405..c7e9e94a39 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -235,7 +235,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService> findEntity(TenantId tenantId, EntityId entityId) { - return Optional.ofNullable(findTenantById(new TenantId(entityId.getId()))); + return Optional.ofNullable(findTenantById(TenantId.fromUUID(entityId.getId()))); } @Override diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index a0f4beab08..f43ea4a5f6 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -182,7 +182,7 @@ CREATE TABLE IF NOT EXISTS rule_node ( configuration varchar(10000000), type varchar(255), name varchar(255), - debug_mode boolean, + debug_settings varchar(1024), singleton_mode boolean, queue_name varchar(255), external_id uuid diff --git a/monitoring/src/main/resources/root_rule_chain.json b/monitoring/src/main/resources/root_rule_chain.json index ed8a93fb63..ff44ebfe79 100644 --- a/monitoring/src/main/resources/root_rule_chain.json +++ b/monitoring/src/main/resources/root_rule_chain.json @@ -20,7 +20,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", - "debugMode": true, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -35,7 +34,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", "name": "Save Attributes", - "debugMode": false, "singletonMode": false, "configurationVersion": 1, "configuration": { @@ -53,7 +51,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "Message Type Switch", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -68,7 +65,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log RPC from Device", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -85,7 +81,6 @@ }, "type": "org.thingsboard.rule.engine.action.TbLogNode", "name": "Log Other", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -102,7 +97,6 @@ }, "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", "name": "RPC Call Request", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -117,7 +111,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbOriginatorTypeFilterNode", "name": "Is Entity Group", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -134,7 +127,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode", "name": "Post attributes or RPC request", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -152,7 +144,6 @@ }, "type": "org.thingsboard.rule.engine.transform.TbDuplicateMsgToGroupNode", "name": "Duplicate To Group Entities", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -169,7 +160,6 @@ }, "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", "name": "Device Profile Node", - "debugMode": true, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -186,7 +176,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", "name": "Test JS script", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -204,7 +193,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbJsFilterNode", "name": "Test TBEL script", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -222,7 +210,6 @@ }, "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", "name": "Add arrival timestamp", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -240,7 +227,6 @@ }, "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", "name": "Calculate additional latencies", - "debugMode": true, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -258,7 +244,6 @@ }, "type": "org.thingsboard.rule.engine.transform.TbChangeOriginatorNode", "name": "To latencies asset", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -287,7 +272,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries", - "debugMode": true, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -303,7 +287,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbCheckMessageNode", "name": "Has testData", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -323,7 +306,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "Save Timeseries with TTL", - "debugMode": true, "singletonMode": false, "configurationVersion": 0, "configuration": { diff --git a/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json b/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json index 7a5015add3..c2bb52514e 100644 --- a/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json +++ b/msa/black-box-tests/src/test/resources/MqttRuleNodeTestMetadata.json @@ -9,7 +9,6 @@ }, "type": "org.thingsboard.rule.engine.mqtt.TbMqttNode", "name": "test mqtt", - "debugMode": true, "singletonMode": true, "queueName": "HighPriority", "configurationVersion": 0, @@ -36,7 +35,6 @@ }, "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", "name": "save timeseries", - "debugMode": true, "singletonMode": false, "configurationVersion": 0, "configuration": { @@ -54,7 +52,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "switch", - "debugMode": false, "singletonMode": false, "configurationVersion": 0, "configuration": { diff --git a/msa/black-box-tests/src/test/resources/RpcResponseRuleChainMetadata.json b/msa/black-box-tests/src/test/resources/RpcResponseRuleChainMetadata.json index 09178ef781..ef700ae37d 100644 --- a/msa/black-box-tests/src/test/resources/RpcResponseRuleChainMetadata.json +++ b/msa/black-box-tests/src/test/resources/RpcResponseRuleChainMetadata.json @@ -8,7 +8,6 @@ }, "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", "name": "msgTypeSwitch", - "debugMode": true, "configuration": { "version": 0 } @@ -20,7 +19,6 @@ }, "type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode", "name": "formResponse", - "debugMode": true, "configuration": { "jsScript": "if (msg.method == \"getResponse\") {\n return {msg: {\"response\": \"requestReceived\"}, metadata: metadata, msgType: msgType};\n}\n\nreturn {msg: msg, metadata: metadata, msgType: msgType};" } @@ -32,7 +30,6 @@ }, "type": "org.thingsboard.rule.engine.rpc.TbSendRPCReplyNode", "name": "rpcReply", - "debugMode": true, "configuration": { "requestIdMetaDataAttribute": "requestId" } diff --git a/packaging/java/scripts/control/template.service b/packaging/java/scripts/control/template.service index 3fee5c88df..235c3a1700 100644 --- a/packaging/java/scripts/control/template.service +++ b/packaging/java/scripts/control/template.service @@ -7,5 +7,9 @@ User=${pkg.user} ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar SuccessExitStatus=143 +# Service shall be restarted in 30 seconds when the service process exits, is killed, or a timeout is reached. +Restart=always # default 'no' +RestartSec=30 + [Install] WantedBy=multi-user.target diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 3a77575b0d..a1155f736b 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -1358,13 +1358,17 @@ public class RestClient implements Closeable { }, params).getBody(); } - public PageData getTenantDeviceInfos(String type, DeviceProfileId deviceProfileId, PageLink pageLink) { + public PageData getTenantDeviceInfos(String type, Boolean active, DeviceProfileId deviceProfileId, PageLink pageLink) { Map params = new HashMap<>(); params.put("type", type); params.put("deviceProfileId", deviceProfileId != null ? deviceProfileId.toString() : null); + if (active != null) { + params.put("active", active.toString()); + } addPageLinkToParam(params, pageLink); return restTemplate.exchange( - baseURL + "/api/tenant/deviceInfos?type={type}&deviceProfileId={deviceProfileId}&" + getUrlParams(pageLink), + baseURL + "/api/tenant/deviceInfos?type={type}&deviceProfileId={deviceProfileId}&" + + (active != null ? "active={active}&" : "") + getUrlParams(pageLink), HttpMethod.GET, HttpEntity.EMPTY, new ParameterizedTypeReference>() { }, params).getBody(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java index b1662dd5c2..0b3d148f4e 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/TenantIdLoader.java @@ -62,7 +62,7 @@ public class TenantIdLoader { HasTenantId tenantEntity; switch (entityType) { case TENANT: - return new TenantId(id); + return TenantId.fromUUID(id); case CUSTOMER: tenantEntity = ctx.getCustomerService().findCustomerById(ctxTenantId, new CustomerId(id)); break; diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index b73847ef83..840984b34e 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -27,6 +27,8 @@ export interface SysParamsState { mobileQrEnabled: boolean; userSettings: UserSettings; maxResourceSize: number; + maxDebugModeDurationMinutes: number; + ruleChainDebugPerTenantLimitsConfiguration?: string; } export interface SysParams extends SysParamsState { diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 73e0d04eb1..20674adf8d 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -31,6 +31,7 @@ const emptyUserAuthState: AuthPayload = { persistDeviceStateToTelemetry: false, mobileQrEnabled: false, maxResourceSize: 0, + maxDebugModeDurationMinutes: 0, userSettings: initialUserSettings }; diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html new file mode 100644 index 0000000000..b779ae1e4c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.html @@ -0,0 +1,32 @@ + + diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts new file mode 100644 index 0000000000..dafda75c0b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-button.component.ts @@ -0,0 +1,155 @@ +/// +/// Copyright © 2016-2024 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 { ChangeDetectionStrategy, Component, forwardRef, Input, Renderer2, ViewContainerRef } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { MatButton } from '@angular/material/button'; +import { DebugSettingsPanelComponent } from './debug-settings-panel.component'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { of, shareReplay, timer } from 'rxjs'; +import { SECOND } from '@shared/models/time/time.models'; +import { DebugSettings } from '@shared/models/entity.models'; +import { map, startWith, switchMap, takeWhile } from 'rxjs/operators'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { AppState } from '@core/core.state'; +import { Store } from '@ngrx/store'; +import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'tb-debug-settings-button', + templateUrl: './debug-settings-button.component.html', + standalone: true, + imports: [ + CommonModule, + SharedModule, + DurationLeftPipe, + ], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DebugSettingsButtonComponent), + multi: true + }, + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DebugSettingsButtonComponent implements ControlValueAccessor { + + @Input() debugLimitsConfiguration: string; + + debugSettingsFormGroup = this.fb.group({ + failuresEnabled: [false], + allEnabled: [false], + allEnabledUntil: [] + }); + + disabled = false; + + isDebugAllActive$ = this.debugSettingsFormGroup.get('allEnabled').valueChanges.pipe( + startWith(null), + switchMap(() => { + if (this.allEnabled) { + return of(true); + } else { + return timer(0, SECOND).pipe( + map(() => this.allEnabledUntil > new Date().getTime()), + takeWhile(value => value, true) + ); + } + }), + takeUntilDestroyed(), + shareReplay(1) + ); + + readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; + + private propagateChange: (settings: DebugSettings) => void = () => {}; + + constructor(private popoverService: TbPopoverService, + private renderer: Renderer2, + private store: Store, + private viewContainerRef: ViewContainerRef, + private fb: FormBuilder, + ) { + this.debugSettingsFormGroup.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => { + this.propagateChange(value); + }) + } + + get failuresEnabled(): boolean { + return this.debugSettingsFormGroup.get('failuresEnabled').value; + } + + get allEnabled(): boolean { + return this.debugSettingsFormGroup.get('allEnabled').value; + } + + get allEnabledUntil(): number { + return this.debugSettingsFormGroup.get('allEnabledUntil').value; + } + + openDebugStrategyPanel($event: Event, matButton: MatButton): void { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + const debugSettings = this.debugSettingsFormGroup.value; + + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const debugStrategyPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, DebugSettingsPanelComponent, 'bottom', true, null, + { + ...debugSettings, + maxDebugModeDurationMinutes: this.maxDebugModeDurationMinutes, + debugLimitsConfiguration: this.debugLimitsConfiguration + }, + {}, + {}, {}, true); + debugStrategyPopover.tbComponentRef.instance.popover = debugStrategyPopover; + debugStrategyPopover.tbComponentRef.instance.onSettingsApplied.subscribe((settings: DebugSettings) => { + this.debugSettingsFormGroup.patchValue(settings); + debugStrategyPopover.hide(); + }); + } + } + + registerOnChange(fn: (settings: DebugSettings) => void): void { + this.propagateChange = fn; + } + + registerOnTouched(_: () => void): void {} + + writeValue(settings: DebugSettings): void { + this.debugSettingsFormGroup.patchValue(settings, {emitEvent: false}); + this.debugSettingsFormGroup.get('allEnabled').updateValueAndValidity({onlySelf: true}); + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.debugSettingsFormGroup.disable({emitEvent: false}); + } else { + this.debugSettingsFormGroup.enable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html new file mode 100644 index 0000000000..5fe0aa2546 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.html @@ -0,0 +1,66 @@ + +
+
debug-config.label
+
+
+ @if (debugLimitsConfiguration) { + {{ 'debug-config.hint.main-limited' | translate: { msg: maxMessagesCount, sec: maxTimeFrameSec } }} + } @else { + {{ 'debug-config.hint.main' | translate }} + } +
+
+
+ +
+ {{ 'debug-config.on-failure' | translate }} +
+
+
+ +
+ {{ 'debug-config.all-messages' | translate: { time: (isDebugAllActive$ | async) && !allEnabled ? (allEnabledUntil | durationLeft) : ('debug-config.min' | translate: { number: maxDebugModeDurationMinutes }) } }} +
+
+ +
+
+
+ + +
+
+ diff --git a/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts new file mode 100644 index 0000000000..a9e237245c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/debug-settings/debug-settings-panel.component.ts @@ -0,0 +1,134 @@ +/// +/// Copyright © 2016-2024 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 { + booleanAttribute, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnInit +} from '@angular/core'; +import { PageComponent } from '@shared/components/page.component'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { FormBuilder } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { SECOND } from '@shared/models/time/time.models'; +import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; +import { of, shareReplay, timer } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { DebugSettings } from '@shared/models/entity.models'; +import { distinctUntilChanged, map, startWith, switchMap, takeWhile } from 'rxjs/operators'; + +@Component({ + selector: 'tb-debug-settings-panel', + templateUrl: './debug-settings-panel.component.html', + standalone: true, + imports: [ + SharedModule, + CommonModule, + DurationLeftPipe + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DebugSettingsPanelComponent extends PageComponent implements OnInit { + + @Input() popover: TbPopoverComponent; + @Input({ transform: booleanAttribute }) failuresEnabled = false; + @Input({ transform: booleanAttribute }) allEnabled = false; + @Input() allEnabledUntil = 0; + @Input() maxDebugModeDurationMinutes: number; + @Input() debugLimitsConfiguration: string; + + onFailuresControl = this.fb.control(false); + debugAllControl = this.fb.control(false); + + maxMessagesCount: string; + maxTimeFrameSec: string; + initialAllEnabled: boolean; + + isDebugAllActive$ = this.debugAllControl.valueChanges.pipe( + startWith(this.debugAllControl.value), + switchMap(value => { + if (value) { + return of(true); + } else { + return timer(0, SECOND).pipe( + map(() => this.allEnabledUntil > new Date().getTime()), + takeWhile(value => value, true) + ); + } + }), + takeUntilDestroyed(), + shareReplay(1), + ); + + onSettingsApplied = new EventEmitter(); + + constructor(private fb: FormBuilder, + private cd: ChangeDetectorRef) { + super(); + + this.debugAllControl.valueChanges.pipe( + takeUntilDestroyed() + ).subscribe(value => { + this.allEnabled = value; + this.cd.markForCheck(); + }); + + this.isDebugAllActive$.pipe( + distinctUntilChanged(), + takeUntilDestroyed() + ).subscribe(isDebugOn => this.debugAllControl.patchValue(isDebugOn, {emitEvent: false})) + } + + ngOnInit(): void { + this.maxMessagesCount = this.debugLimitsConfiguration?.split(':')[0]; + this.maxTimeFrameSec = this.debugLimitsConfiguration?.split(':')[1]; + this.onFailuresControl.patchValue(this.failuresEnabled); + this.debugAllControl.patchValue(this.allEnabled); + this.initialAllEnabled = this.allEnabled || this.allEnabledUntil > new Date().getTime(); + } + + onCancel(): void { + this.popover?.hide(); + } + + onApply(): void { + const isDebugAllChanged = this.initialAllEnabled !== this.debugAllControl.value || this.initialAllEnabled !== this.allEnabledUntil > new Date().getTime(); + if (isDebugAllChanged) { + this.onSettingsApplied.emit({ + allEnabled: this.allEnabled, + failuresEnabled: this.onFailuresControl.value, + allEnabledUntil: 0, + }); + } else { + this.onSettingsApplied.emit({ + allEnabled: false, + failuresEnabled: this.onFailuresControl.value, + allEnabledUntil: this.allEnabledUntil, + }); + } + } + + onReset(): void { + this.debugAllControl.patchValue(true); + this.allEnabledUntil = 0; + this.cd.markForCheck(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts index c8947ed55d..953d1d17c1 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table-config.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table-config.ts @@ -40,8 +40,8 @@ import { EventContentDialogData } from '@home/components/event/event-content-dialog.component'; import { isEqual, sortObjectKeys } from '@core/utils'; -import { historyInterval, MINUTE } from '@shared/models/time/time.models'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { DAY, historyInterval, MINUTE } from '@shared/models/time/time.models'; +import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { ChangeDetectorRef, EventEmitter, Injector, StaticProvider, ViewContainerRef } from '@angular/core'; import { ComponentPortal } from '@angular/cdk/portal'; import { @@ -51,6 +51,9 @@ import { FilterEntityColumn } from '@home/components/event/event-filter-panel.component'; import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models'; +import { getCurrentAuthState } from '@core/auth/auth.selectors'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; export class EventTableConfig extends EntityTableConfig { @@ -59,6 +62,7 @@ export class EventTableConfig extends EntityTableConfig { private filterParams: FilterEventBody = {}; private filterColumns: FilterEntityColumn[] = []; + private readonly maxDebugModeDurationMinutes = getCurrentAuthState(this.store).maxDebugModeDurationMinutes; set eventType(eventType: EventType | DebugEventType) { if (this.eventTypeValue !== eventType) { @@ -88,13 +92,15 @@ export class EventTableConfig extends EntityTableConfig { private overlay: Overlay, private viewContainerRef: ViewContainerRef, private cd: ChangeDetectorRef, + private store: Store, public testButtonLabel?: string, private debugEventSelected?: EventEmitter) { super(); this.loadDataOnInit = false; this.tableTitle = ''; this.useTimePageLink = true; - this.defaultTimewindowInterval = historyInterval(MINUTE * 15); + const defaultInterval = this.maxDebugModeDurationMinutes ? Math.min(this.maxDebugModeDurationMinutes * MINUTE, DAY) : DAY; + this.defaultTimewindowInterval = historyInterval(defaultInterval); this.detailsPanelEnabled = false; this.selectionEnabled = false; this.searchEnabled = false; diff --git a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts index fbcce00581..b4c3bfc58f 100644 --- a/ui-ngx/src/app/modules/home/components/event/event-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/event/event-table.component.ts @@ -36,6 +36,8 @@ import { DebugEventType, EventBody, EventType } from '@shared/models/event.model import { Overlay } from '@angular/cdk/overlay'; import { Subscription } from 'rxjs'; import { isNotEmptyStr } from '@core/utils'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; @Component({ selector: 'tb-event-table', @@ -123,6 +125,7 @@ export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { private dialog: MatDialog, private overlay: Overlay, private viewContainerRef: ViewContainerRef, + private store: Store, private cd: ChangeDetectorRef) { } @@ -142,6 +145,7 @@ export class EventTableComponent implements OnInit, AfterViewInit, OnDestroy { this.overlay, this.viewContainerRef, this.cd, + this.store, this.functionTestButtonLabel, this.debugEventSelected ); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 12a2bcaf7c..0f436b7dda 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -370,6 +370,25 @@ +
+ + {{ 'tenant-profile.debug' | translate }} + +
+ + tenant-profile.maximum-debug-duration-min + + + {{ 'tenant-profile.maximum-debug-duration-min-range' | translate }} + + + +
+
+
+
{{ 'tenant-profile.ota-files-in-bytes' | translate }} tenant-profile.unlimited diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index 4d72205108..490bdc902b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -86,6 +86,7 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA tenantNotificationRequestsRateLimit: [null, []], tenantNotificationRequestsPerRuleRateLimit: [null, []], maxTransportMessages: [null, [Validators.required, Validators.min(0)]], + maxDebugModeDurationMinutes: [null, [Validators.min(0)]], maxTransportDataPoints: [null, [Validators.required, Validators.min(0)]], maxREExecutions: [null, [Validators.required, Validators.min(0)]], maxJSExecutions: [null, [Validators.required, Validators.min(0)]], diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html index 014a73973c..13cc24a7b7 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rule-node-details.component.html @@ -22,7 +22,7 @@
-
+
rulenode.name @@ -34,13 +34,23 @@ {{ 'rulenode.name-max-length' | translate }} -
- - {{ 'rulenode.debug-mode' | translate }} - - - {{ 'rulenode.singleton-mode' | translate }} - +
+ +
(); constructor(protected store: Store, @@ -92,7 +95,7 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O if (this.ruleNode) { this.ruleNodeFormGroup = this.fb.group({ name: [this.ruleNode.name, [Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*'), Validators.maxLength(255)]], - debugMode: [this.ruleNode.debugMode, []], + debugSettings: [this.ruleNode.debugSettings], singletonMode: [this.ruleNode.singletonMode, []], configuration: [this.ruleNode.configuration, [Validators.required]], additionalInfo: this.fb.group( @@ -167,6 +170,15 @@ export class RuleNodeDetailsComponent extends PageComponent implements OnInit, O this.ruleNodeConfigComponent.validate(); } + onSingleModeChange($event: Event): void { + if ($event) { + $event.stopPropagation(); + } + const singleModeControl = this.ruleNodeFormGroup.get('singletonMode'); + singleModeControl.patchValue(!singleModeControl.value); + singleModeControl.markAsDirty(); + } + openRuleChain($event: Event) { if ($event) { $event.stopPropagation(); diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts index 4425b9c039..35b10a27b3 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts @@ -575,7 +575,7 @@ export class RuleChainPageComponent extends PageComponent additionalInfo: ruleNode.additionalInfo, configuration: ruleNode.configuration, configurationVersion: isDefinedAndNotNull(ruleNode.configurationVersion) ? ruleNode.configurationVersion : 0, - debugMode: ruleNode.debugMode, + debugSettings: ruleNode.debugSettings, singletonMode: ruleNode.singletonMode, queueName: ruleNode.queueName, x: Math.round(ruleNode.additionalInfo.layoutX), @@ -936,7 +936,7 @@ export class RuleChainPageComponent extends PageComponent name: node.name, configuration: deepClone(node.configuration), additionalInfo: node.additionalInfo ? deepClone(node.additionalInfo) : {}, - debugMode: node.debugMode, + debugSettings: node.debugSettings, singletonMode: node.singletonMode, queueName: node.queueName }; @@ -1009,7 +1009,6 @@ export class RuleChainPageComponent extends PageComponent name: outputEdge.label, configuration: {}, additionalInfo: {}, - debugMode: false, singletonMode: false }; outputNode.additionalInfo.layoutX = Math.round(destNode.x); @@ -1055,7 +1054,6 @@ export class RuleChainPageComponent extends PageComponent configuration: { ruleChainId: ruleChain.id.id }, - debugMode: false, singletonMode: false, x: Math.round(ruleChainNodeX), y: Math.round(ruleChainNodeY), @@ -1475,7 +1473,7 @@ export class RuleChainPageComponent extends PageComponent : node.component.configurationVersion, configuration: node.configuration, additionalInfo: node.additionalInfo ? node.additionalInfo : {}, - debugMode: node.debugMode, + debugSettings: node.debugSettings, singletonMode: node.singletonMode, queueName: node.queueName }; diff --git a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts index a2a3d3995b..1fdf11c780 100644 --- a/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts +++ b/ui-ngx/src/app/modules/home/pages/rulechain/rulechain.module.ts @@ -32,6 +32,8 @@ import { RuleNodeDetailsComponent } from './rule-node-details.component'; import { RuleNodeLinkComponent } from './rule-node-link.component'; import { LinkLabelsComponent } from '@home/pages/rulechain/link-labels.component'; import { RuleNodeConfigComponent } from './rule-node-config.component'; +import { DurationLeftPipe } from '@shared/pipe/duration-left.pipe'; +import { DebugSettingsButtonComponent } from '@home/components/debug-settings/debug-settings-button.component'; @NgModule({ declarations: [ @@ -59,7 +61,9 @@ import { RuleNodeConfigComponent } from './rule-node-config.component'; CommonModule, SharedModule, HomeComponentsModule, - RuleChainRoutingModule + RuleChainRoutingModule, + DurationLeftPipe, + DebugSettingsButtonComponent ] }) export class RuleChainModule { } diff --git a/ui-ngx/src/app/shared/import-export/import-export.service.ts b/ui-ngx/src/app/shared/import-export/import-export.service.ts index 5ad59fc10f..5b707d6399 100644 --- a/ui-ngx/src/app/shared/import-export/import-export.service.ts +++ b/ui-ngx/src/app/shared/import-export/import-export.service.ts @@ -649,16 +649,20 @@ export class ImportExportService { ); } - private processOldRuleChainConnections(ruleChainImport: RuleChainImport): Observable { - ruleChainImport.ruleChain = this.prepareImport(ruleChainImport.ruleChain); - const metadata = ruleChainImport.metadata; + private processOldRuleChainConnections({ruleChain, metadata}: RuleChainImport): Observable { + ruleChain = this.prepareImport(ruleChain); + metadata = { + ...metadata, + nodes: metadata.nodes.map(({ debugMode, ...node }: RuleNode & { debugMode: boolean }) => { + return debugMode ? { ...node, debugSettings: { failuresEnabled: true, allEnabled: true} } : node + }) + }; if ((metadata as any).ruleChainConnections) { const ruleChainNameResolveObservables: Observable[] = []; for (const ruleChainConnection of (metadata as any).ruleChainConnections) { if (ruleChainConnection.targetRuleChainId && ruleChainConnection.targetRuleChainId.id) { const ruleChainNode: RuleNode = { name: '', - debugMode: false, singletonMode: false, type: 'org.thingsboard.rule.engine.flow.TbRuleChainInputNode', configuration: { @@ -686,13 +690,13 @@ export class ImportExportService { } if (ruleChainNameResolveObservables.length) { return forkJoin(ruleChainNameResolveObservables).pipe( - map(() => ruleChainImport) + map(() => ({ruleChain, metadata})) ); } else { - return of(ruleChainImport); + return of({ruleChain, metadata}); } } else { - return of(ruleChainImport); + return of({ruleChain, metadata}); } } diff --git a/ui-ngx/src/app/shared/models/entity.models.ts b/ui-ngx/src/app/shared/models/entity.models.ts index 51fbb8d261..19b0718336 100644 --- a/ui-ngx/src/app/shared/models/entity.models.ts +++ b/ui-ngx/src/app/shared/models/entity.models.ts @@ -193,4 +193,14 @@ export interface HasVersion { version?: number; } +export interface HasDebugSettings { + debugSettings?: DebugSettings; +} + +export interface DebugSettings { + failuresEnabled?: boolean; + allEnabled?: boolean; + allEnabledUntil?: number; +} + export type VersionedEntity = EntityInfoData & HasVersion | RuleChainMetaData; diff --git a/ui-ngx/src/app/shared/models/rule-node.models.ts b/ui-ngx/src/app/shared/models/rule-node.models.ts index 712b225743..ddfad98f11 100644 --- a/ui-ngx/src/app/shared/models/rule-node.models.ts +++ b/ui-ngx/src/app/shared/models/rule-node.models.ts @@ -27,17 +27,16 @@ import { AppState } from '@core/core.state'; import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { RuleChainType } from '@shared/models/rule-chain.models'; import { DebugRuleNodeEventBody } from '@shared/models/event.models'; -import { TranslateService } from '@ngx-translate/core'; +import { HasDebugSettings } from '@shared/models/entity.models'; export interface RuleNodeConfiguration { [key: string]: any; } -export interface RuleNode extends BaseData { +export interface RuleNode extends BaseData, HasDebugSettings { ruleChainId?: RuleChainId; type: string; name: string; - debugMode: boolean; singletonMode: boolean; queueName?: string; configurationVersion?: number; @@ -332,7 +331,7 @@ export interface RuleNodeComponentDescriptor extends ComponentDescriptor { configurationDescriptor?: RuleNodeConfigurationDescriptor; } -export interface FcRuleNodeType extends FcNode { +export interface FcRuleNodeType extends FcNode, HasDebugSettings { component?: RuleNodeComponentDescriptor; singletonMode?: boolean; queueName?: string; @@ -345,7 +344,6 @@ export interface FcRuleNode extends FcRuleNodeType { ruleNodeId?: RuleNodeId; additionalInfo?: any; configuration?: RuleNodeConfiguration; - debugMode?: boolean; error?: string; highlighted?: boolean; componentClazz?: string; diff --git a/ui-ngx/src/app/shared/pipe/duration-left.pipe.ts b/ui-ngx/src/app/shared/pipe/duration-left.pipe.ts new file mode 100644 index 0000000000..4aeb3842a2 --- /dev/null +++ b/ui-ngx/src/app/shared/pipe/duration-left.pipe.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2024 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 { Pipe, PipeTransform } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { MillisecondsToTimeStringPipe } from './milliseconds-to-time-string.pipe'; + +@Pipe({ + name: 'durationLeft', + pure: false, + standalone: true, +}) +export class DurationLeftPipe implements PipeTransform { + + constructor(private translate: TranslateService, private millisecondsToTimeString: MillisecondsToTimeStringPipe) { + } + + transform(untilTimestamp: number, shortFormat = true, onlyFirstDigit = true): string { + const time = this.millisecondsToTimeString.transform((untilTimestamp - new Date().getTime()), shortFormat, onlyFirstDigit) ?? 0; + return this.translate.instant('common.time-left', { time }); + } +} diff --git a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts index c767be3dc5..853890ee09 100644 --- a/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/milliseconds-to-time-string.pipe.ts @@ -16,6 +16,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; +import { DAY, HOUR, MINUTE, SECOND } from '@shared/models/time/time.models'; @Pipe({ name: 'milliSecondsToTimeString' @@ -25,52 +26,44 @@ export class MillisecondsToTimeStringPipe implements PipeTransform { constructor(private translate: TranslateService) { } - transform(millseconds: number, shortFormat = false): string { - let seconds = Math.floor(millseconds / 1000); - const days = Math.floor(seconds / 86400); - let hours = Math.floor((seconds % 86400) / 3600); - let minutes = Math.floor(((seconds % 86400) % 3600) / 60); - seconds = seconds % 60; + transform(milliSeconds: number, shortFormat = false, onlyFirstDigit = false): string { + const { days, hours, minutes, seconds } = this.extractTimeUnits(milliSeconds); + return this.formatTimeString(days, hours, minutes, seconds, shortFormat, onlyFirstDigit); + } + + private extractTimeUnits(milliseconds: number): { days: number; hours: number; minutes: number; seconds: number } { + const days = Math.floor(milliseconds / DAY); + const hours = Math.floor((milliseconds % DAY) / HOUR); + const minutes = Math.floor((milliseconds % HOUR) / MINUTE); + const seconds = Math.floor((milliseconds % MINUTE) / SECOND); + return { days, hours, minutes, seconds }; + } + + private formatTimeString( + days: number, + hours: number, + minutes: number, + seconds: number, + shortFormat: boolean, + onlyFirstDigit: boolean + ): string { + const timeUnits = [ + { value: days, key: 'days', shortKey: 'short.days' }, + { value: hours, key: 'hours', shortKey: 'short.hours' }, + { value: minutes, key: 'minutes', shortKey: 'short.minutes' }, + { value: seconds, key: 'seconds', shortKey: 'short.seconds' } + ]; + let timeString = ''; - if (shortFormat) { - if (days > 0) { - timeString += this.translate.instant('timewindow.short.days', {days}); - } - if (hours > 0) { - timeString += this.translate.instant('timewindow.short.hours', {hours}); - } - if (minutes > 0) { - timeString += this.translate.instant('timewindow.short.minutes', {minutes}); - } - if (seconds > 0) { - timeString += this.translate.instant('timewindow.short.seconds', {seconds}); - } - if (!timeString.length) { - timeString += this.translate.instant('timewindow.short.seconds', {seconds: 0}); - } - } else { - if (days > 0) { - timeString += this.translate.instant('timewindow.days', {days}); - } - if (hours > 0) { - if (timeString.length === 0 && hours === 1) { - hours = 0; - } - timeString += this.translate.instant('timewindow.hours', {hours}); - } - if (minutes > 0) { - if (timeString.length === 0 && minutes === 1) { - minutes = 0; - } - timeString += this.translate.instant('timewindow.minutes', {minutes}); - } - if (seconds > 0) { - if (timeString.length === 0 && seconds === 1) { - seconds = 0; + for (const { value, key, shortKey } of timeUnits) { + if (value > 0) { + timeString += this.translate.instant(shortFormat ? `timewindow.${shortKey}` : `timewindow.${key}`, { [key]: value }); + if (onlyFirstDigit) { + return timeString; } - timeString += this.translate.instant('timewindow.seconds', {seconds}); } } - return timeString; + + return timeString.length > 0 ? timeString : this.translate.instant('timewindow.short.seconds', { seconds: 0 }); } } diff --git a/ui-ngx/src/app/shared/pipe/public-api.ts b/ui-ngx/src/app/shared/pipe/public-api.ts index 7c477f44c6..767ff1deee 100644 --- a/ui-ngx/src/app/shared/pipe/public-api.ts +++ b/ui-ngx/src/app/shared/pipe/public-api.ts @@ -25,3 +25,4 @@ export * from './file-size.pipe'; export * from './selectable-columns.pipe'; export * from './image.pipe'; export * from './key-value-not-empty.pipe'; +export * from './duration-left.pipe'; diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index 1cf43d72bc..c7bb0ec41e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -4556,7 +4556,6 @@ "deselect-all": "إلغاء تحديد الكل", "rulenode-details": "تفاصيل عقدة القاعدة", "debug-mode": "وضع التصحيح", - "singleton-mode": "وضع المفرد", "configuration": "التكوين", "link": "رابط", "link-details": "تفاصيل رابط عقدة القاعدة", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index aac5b2aada..14b43ed79d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -988,6 +988,20 @@ "type-timeseries-deleted": "Telemetry deleted", "type-sms-sent": "SMS sent" }, + "debug-config": { + "min": "{{number}} min", + "label": "Debug configuration", + "on-failure": "Failures only (24/7)", + "all-messages": "All messages ({{time}})", + "failures": "Failures", + "all": "All", + "hint": { + "main": "All node debug messages rate limited with:", + "main-limited": "All node debug messages will be rate-limited, with a maximum of {{msg}} messages allowed per {{sec}} seconds.", + "on-failure": "Save all failure debug events without time limit.", + "all-messages": "Save all debug events during time limit." + } + }, "confirm-on-exit": { "message": "You have unsaved changes. Are you sure you want to leave this page?", "html-message": "You have unsaved changes.
Are you sure you want to leave this page?", @@ -1018,11 +1032,13 @@ "enter-password": "Enter password", "enter-search": "Enter search", "created-time": "Created time", + "disabled": "Disabled", "loading": "Loading...", "proceed": "Proceed", "open-details-page": "Open details page", "not-found": "Not found", - "documentation": "Documentation" + "documentation": "Documentation", + "time-left": "{{time}} left" }, "content-type": { "json": "Json", @@ -3162,7 +3178,7 @@ "critical": "Critical", "critical-click": "Critical click", "critical-state-hint": "Indicates whether component is in critical state.", - "critical-state-animation": "Critical state animation", + "critical-state-animation": "Alarm state animation", "critical-state-animation-hint": "Whether to enable blinking animation when component is in critical state.", "animation": "Animation", "broken": "Broken", @@ -3187,6 +3203,7 @@ "top-right-connector": "Top right connector", "running-color": "Running color", "stopped-color": "Stopped color", + "stopped": "Stopped", "warning-color": "Warning color", "critical-color": "Critical color", "opened": "Opened", @@ -3286,7 +3303,9 @@ "low-critical-state": "Low Critical state", "show-low-critical-scale": "Show Low Critical state", "low-critical-scale": "Low Critical state", - "low-critical-state-hint": "Double value indicates a low critical range up to the min value scale." + "low-critical-state-hint": "Double value indicates a low critical range up to the min value scale.", + "colors": "Colors", + "alarm-colors": "Alarm colors" } }, "item": { @@ -4224,7 +4243,7 @@ "deselect-all": "Deselect all", "rulenode-details": "Rule node details", "debug-mode": "Debug mode", - "singleton-mode": "Singleton mode", + "singleton": "Singleton", "configuration": "Configuration", "link": "Link", "link-details": "Rule node link details", @@ -4435,6 +4454,7 @@ "tenant-profiles": "Tenant profiles", "add": "Add tenant profile", "add-profile": "Add profile", + "debug": "Debug", "edit": "Edit tenant profile", "tenant-profile-details": "Tenant profile details", "no-tenant-profiles-text": "No tenant profiles found", @@ -4505,6 +4525,8 @@ "maximum-ota-packages-sum-data-size": "Maximum total size of OTA package files (bytes)", "maximum-ota-package-sum-data-size-required": "Maximum total size of OTA package files is required.", "maximum-ota-package-sum-data-size-range": "Maximum total size of OTA package files can't be negative", + "maximum-debug-duration-min": "Maximum debug duration (min)", + "maximum-debug-duration-min-range": "Maximum debug duration can't be negative", "rest-requests-for-tenant": "REST requests for tenant", "transport-tenant-telemetry-msg-rate-limit": "Transport tenant telemetry messages", "transport-tenant-telemetry-data-points-rate-limit": "Transport tenant telemetry data points", @@ -7410,6 +7432,7 @@ "get-dashboard-state": "Get dashboard state", "add-time-series": "Add time series", "execute-rpc-text": "Execute RPC method '{{methodName}}'", + "get-time-series-text": "Use time series '{{key}}'", "get-attribute-text": "Use attribute '{{key}}'", "get-alarm-status-text": "Use alarm status", "get-dashboard-state-text": "Use dashboard state", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 0979697471..f675072cef 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -3485,7 +3485,6 @@ "deselect-all": "Deshacer selección de todos", "rulenode-details": "Detalles del nodo de reglas", "debug-mode": "Modo Debug", - "singleton-mode": "Módo único", "configuration": "Configuración", "link": "Enlace", "link-details": "Detalles del enlace del nodo de reglas", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index 9fd04689c2..d4c5d7e9a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -4458,7 +4458,6 @@ "deselect-all": "Deselect all", "rulenode-details": "Rule node details", "debug-mode": "Debug mode", - "singleton-mode": "Singleton mode", "configuration": "Configuration", "link": "Link", "link-details": "Rule node link details", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index 9e7ac3e14b..eddb79c155 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -3793,7 +3793,7 @@ "downlink-topic-name": "Naam van downlink-onderwerp", "downlink-topic-name-required": "De naam van het downlink-onderwerp is vereist" }, - + "item": { "selected": "Geselecteerd" }, @@ -4419,7 +4419,7 @@ "system": "Systeem", "title": "Titel", "title-required": "Titel is vereist.", - "title-max-length": "Titel moet kleiner zijn dan 256 tekens", + "title-max-length": "Titel moet kleiner zijn dan 256 tekens" }, "rpc": { "error": { @@ -4515,7 +4515,6 @@ "deselect-all": "Alles deselecteren", "rulenode-details": "Details van rule nodes", "debug-mode": "Foutopsporingsmodus", - "singleton-mode": "Singleton-modus", "configuration": "Configuratie", "link": "Verbinden", "link-details": "Details van rule nodes links", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index a09907560d..d74dccaa7c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -4474,7 +4474,6 @@ "deselect-all": "Odznacz wszystkie", "rulenode-details": "Szczegóły węzła reguły", "debug-mode": "Tryb debugowania", - "singleton-mode": "Tryb singletonowy", "configuration": "Konfiguracja", "link": "Połączenie", "link-details": "Szczegóły połączenia węzła reguły", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index b1ec1e0a75..bbaaa0bd8f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -3968,7 +3968,6 @@ "deselect-all": "取消选择", "rulenode-details": "规则节点详情", "debug-mode": "调试模式", - "singleton-mode": "单例模式", "configuration": "配置", "link": "链接", "link-details": "规则节点链接详情", diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index 346941d4d1..c394a29826 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -1270,6 +1270,24 @@ pre.tb-highlight { .no-wrap { white-space: nowrap; } + + .tb-rounded-btn { + border-radius: 20px; + padding: 0 16px; + + &:not(.active, :disabled) { + color: rgba(0, 0, 0, 0.76); + background-color: rgba(0, 0, 0, 0.06); + } + + &.active:not(:disabled) { + --mdc-outlined-button-outline-color: $primary; + } + + &:disabled { + background: rgba(0, 0, 0, 0.06); + } + } } /***************