Browse Source

Merge remote-tracking branch 'upstream/master' into kafka-configurable-topics

pull/12588/head
Andrii Landiak 2 years ago
parent
commit
1980eb454c
  1. 2
      .github/release.yml
  2. 2
      .github/workflows/check-configuration-files.yml
  3. 2
      .github/workflows/license-header-format.yml
  4. 6
      application/pom.xml
  5. 2
      application/src/main/conf/logback.xml
  6. 2
      application/src/main/conf/thingsboard.conf
  7. 17
      application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_db.md
  8. 9
      application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json
  9. 108
      application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg
  10. 2
      application/src/main/data/json/system/scada_symbols/control-panel-hp.svg
  11. 2
      application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg
  12. 360
      application/src/main/data/json/system/scada_symbols/electrical-engine-hp.svg
  13. 361
      application/src/main/data/json/system/scada_symbols/gas-preventer-hp.svg
  14. 363
      application/src/main/data/json/system/scada_symbols/gas-wellhead-hp.svg
  15. 363
      application/src/main/data/json/system/scada_symbols/heat-exchanger-hp.svg
  16. 2
      application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg
  17. 108
      application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg
  18. 342
      application/src/main/data/json/system/scada_symbols/large-horizontal-separator-connector-hp.svg
  19. 350
      application/src/main/data/json/system/scada_symbols/large-horizontal-separator-hp.svg
  20. 350
      application/src/main/data/json/system/scada_symbols/large-vertical-separator-connector-hp.svg
  21. 350
      application/src/main/data/json/system/scada_symbols/large-vertical-separator-hp.svg
  22. 2
      application/src/main/data/json/system/scada_symbols/leak-sensor.svg
  23. 2
      application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg
  24. 2
      application/src/main/data/json/system/scada_symbols/left-drain-pipe.svg
  25. 2
      application/src/main/data/json/system/scada_symbols/left-elbow-drain-pipe.svg
  26. 104
      application/src/main/data/json/system/scada_symbols/left-flow-meter.svg
  27. 2
      application/src/main/data/json/system/scada_symbols/long-bottom-filter.svg
  28. 2
      application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg
  29. 2
      application/src/main/data/json/system/scada_symbols/long-top-filter.svg
  30. 2
      application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg
  31. 357
      application/src/main/data/json/system/scada_symbols/oil-pump-hp.svg
  32. 2
      application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg
  33. 2
      application/src/main/data/json/system/scada_symbols/right-drain-pipe.svg
  34. 2
      application/src/main/data/json/system/scada_symbols/right-elbow-drain-pipe.svg
  35. 104
      application/src/main/data/json/system/scada_symbols/right-flow-meter.svg
  36. 2
      application/src/main/data/json/system/scada_symbols/short-bottom-filter.svg
  37. 2
      application/src/main/data/json/system/scada_symbols/short-left-drain-pipe.svg
  38. 2
      application/src/main/data/json/system/scada_symbols/short-right-drain-pipe.svg
  39. 2
      application/src/main/data/json/system/scada_symbols/short-top-filter.svg
  40. 2
      application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg
  41. 342
      application/src/main/data/json/system/scada_symbols/small-horizontal-separator-connector-hp.svg
  42. 342
      application/src/main/data/json/system/scada_symbols/small-horizontal-separator-hp.svg
  43. 342
      application/src/main/data/json/system/scada_symbols/small-vertical-separator-connector-hp.svg
  44. 342
      application/src/main/data/json/system/scada_symbols/small-vertical-separator-hp.svg
  45. 108
      application/src/main/data/json/system/scada_symbols/top-flow-meter.svg
  46. 377
      application/src/main/data/json/system/scada_symbols/turbine-hp.svg
  47. 2
      application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg
  48. 108
      application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg
  49. 1
      application/src/main/data/json/system/widget_bundles/buttons.json
  50. 1
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  51. 4
      application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json
  52. 2
      application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json
  53. 16
      application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json
  54. 51
      application/src/main/data/json/system/widget_types/value_stepper.json
  55. 9
      application/src/main/data/json/tenant/device_profile/rule_chain_template.json
  56. 7
      application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
  57. 231
      application/src/main/data/upgrade/basic/schema_update.sql
  58. 2
      application/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java
  59. 2
      application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
  60. 2
      application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java
  61. 121
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  62. 2
      application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java
  63. 29
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  64. 2
      application/src/main/java/org/thingsboard/server/actors/app/AppInitMsg.java
  65. 70
      application/src/main/java/org/thingsboard/server/actors/calculatedField/AbstractCalculatedFieldActor.java
  66. 81
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActor.java
  67. 50
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActorCreator.java
  68. 44
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityDeleteMsg.java
  69. 439
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java
  70. 40
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldException.java
  71. 40
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldLinkedTelemetryMsg.java
  72. 90
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActor.java
  73. 46
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActorCreator.java
  74. 471
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java
  75. 40
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldStateRestoreMsg.java
  76. 39
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldTelemetryMsg.java
  77. 42
      application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityCalculatedFieldLinkedTelemetryMsg.java
  78. 56
      application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityCalculatedFieldTelemetryMsg.java
  79. 41
      application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityInitCalculatedFieldMsg.java
  80. 56
      application/src/main/java/org/thingsboard/server/actors/calculatedField/MultipleTbCallback.java
  81. 2
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
  82. 2
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java
  83. 8
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  84. 2
      application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java
  85. 2
      application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java
  86. 2
      application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java
  87. 2
      application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java
  88. 2
      application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java
  89. 2
      application/src/main/java/org/thingsboard/server/actors/device/TransportSessionCloseReason.java
  90. 15
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  91. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
  92. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  93. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainInputMsg.java
  94. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
  95. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainOutputMsg.java
  96. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java
  97. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java
  98. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java
  99. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java
  100. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java

2
.github/release.yml

@ -1,5 +1,5 @@
#
# Copyright © 2016-2024 The Thingsboard Authors
# Copyright © 2016-2025 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.

2
.github/workflows/check-configuration-files.yml

@ -1,5 +1,5 @@
#
# Copyright © 2016-2024 The Thingsboard Authors
# Copyright © 2016-2025 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.

2
.github/workflows/license-header-format.yml

@ -1,5 +1,5 @@
#
# Copyright © 2016-2024 The Thingsboard Authors
# Copyright © 2016-2025 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.

6
application/pom.xml

@ -1,6 +1,6 @@
<!--
Copyright © 2016-2024 The Thingsboard Authors
Copyright © 2016-2025 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.
@ -369,6 +369,10 @@
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
</dependency>
<dependency>
<groupId>org.rocksdb</groupId>
<artifactId>rocksdbjni</artifactId>
</dependency>
</dependencies>
<build>

2
application/src/main/conf/logback.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright © 2016-2024 The Thingsboard Authors
Copyright © 2016-2025 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.

2
application/src/main/conf/thingsboard.conf

@ -1,5 +1,5 @@
#
# Copyright © 2016-2024 The Thingsboard Authors
# Copyright © 2016-2025 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.

17
application/src/main/data/json/edge/instructions/upgrade/docker/upgrade_db.md

@ -43,19 +43,6 @@ volumes:
Execute the following command to start upgrade process:
```bash
docker compose -f docker-compose-upgrade.yml up
docker compose -f docker-compose-upgrade.yml up --abort-on-container-exit
{:copy-code}
```
Once upgrade process successfully completed, exit from the docker-compose shell by this combination:
```text
Ctrl + C
```
Execute the following command to stop TB Edge upgrade container:
```bash
docker compose -f docker-compose-upgrade.yml stop
{:copy-code}
```
```

9
application/src/main/data/json/edge/rule_chains/edge_root_rule_chain.json

@ -33,8 +33,13 @@
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"configurationVersion": 1,
"configuration": {
"defaultTTL": 0
"defaultTTL": 0,
"useServerTs": false,
"processingSettings": {
"type": "ON_EVERY_MESSAGE"
}
},
"externalId": null
},
@ -185,4 +190,4 @@
],
"ruleChainConnections": null
}
}
}

108
application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg

@ -57,7 +57,7 @@
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
"stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'displayClick');"
@ -463,16 +463,20 @@
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "m³/hr",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "medium-width",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.decimals}",
"type": "number",
"default": 0,
"fieldClass": "medium-width",
"min": 0,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "defaultBorderColor",
@ -511,16 +515,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "activeBorderColor",
@ -559,16 +555,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "warningBorderColor",
@ -607,16 +595,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "criticalBorderColor",
@ -655,16 +635,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "backgroundColor",
@ -703,48 +675,24 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#1EC1F480",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 73 KiB

2
application/src/main/data/json/system/scada_symbols/control-panel-hp.svg

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Control panel",
"description": "Control panel",
"description": "Sends the command to the device or updates attribute/time series when the user pushes the button. Widget settings will enable you to configure behavior, how to fetch the initial state, and what to trigger when power on/off states.",
"searchTags": [
"control"
],

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

2
application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg

@ -776,7 +776,7 @@
}
]
}]]></tb:metadata>
<text x="206.16602" y="43.234375" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="195.21875" y="784.75" fill="#000000" font-family="Roboto" font-size="40px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g stroke="#000" tb:tag="scale">
<text x="206.16602" y="43.234375" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="195.21875" y="770" fill="#000000" font-family="Roboto" font-size="40px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g stroke="#000" tb:tag="scale">
<rect x="176.5" y="83" width="41" height="652" fill="#c8dff7" tb:tag="scaleBackground"/>
<rect transform="scale(1,-1)" x="176.5" y="-735" width="41" height="164" fill="#ebebeb" tb:tag="lowWarningScale"/>
<rect x="176.5" y="83.5" width="41" height="164" fill="#ebebeb" tb:tag="highWarningScale"/>

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

360
application/src/main/data/json/system/scada_symbols/electrical-engine-hp.svg

@ -0,0 +1,360 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="400" fill="none" version="1.1" viewBox="0 0 600 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Electrical engine",
"description": "Electrical engine with various states.",
"searchTags": [
"extraction",
"power"
],
"widgetSizeX": 3,
"widgetSizeY": 2,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<g fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background">
<rect x="161" y="21" width="178" height="30" rx="1"/>
<rect x="201" y="1" width="98" height="20" rx="1"/>
<rect x="51" y="377" width="398" height="22" rx="3"/>
<path d="m76.152 377 32.051-38h283.59l32.051 38h-347.7z"/>
<path d="m1 100c0-27.062 21.938-49 49-49h446c1.657 0 3 1.3431 3 3v282c0 1.657-1.343 3-3 3h-446c-27.062 0-49-21.938-49-49v-190z"/>
<path d="m499 116h16v158h-16v-158z"/>
<path d="m515 29c0-1.6569 1.343-3 3-3h26c1.657 0 3 1.3431 3 3v332c0 1.657-1.343 3-3 3h-26c-1.657 0-3-1.343-3-3v-332z"/>
<path d="m545 183c0-1.657 1.343-3 3-3h48c1.657 0 3 1.343 3 3v24c0 1.657-1.343 3-3 3h-48c-1.657 0-3-1.343-3-3v-24z"/>
</g><path d="m56 50v290" stroke="#1A1A1A" stroke-width="2"/><path d="m56 83h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 67h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 99h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 115h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 131h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 147h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 163h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 179h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 211h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 195h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 227h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 259h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 243h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 275h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 291h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 307h389" stroke="#1A1A1A" stroke-width="2"/><path d="m56 323h389" stroke="#1A1A1A" stroke-width="2"/><path d="m445 50v290" stroke="#1A1A1A" stroke-width="2"/><path d="m201.79 0s-201.79 0-201.79 67v328.36c0 2.6512 5.3727 4.6404 12 4.6404h576c6.627 0 12-1.9892 12-4.6404v-328.36c0-67-198.21-67-198.21-67h-101.79zm201.21 81.2c-3.8661 0-6.9999 1.2536-6.9999 2.8v300.4c0 1.5464 3.1341 2.8 6.9999 2.8h43.998c3.8661 0 6.9999-1.2536 6.9999-2.8v-300.4c0-1.5464-3.1341-2.8-6.9999-2.8z" fill="#000" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,316)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 316)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

361
application/src/main/data/json/system/scada_symbols/gas-preventer-hp.svg

@ -0,0 +1,361 @@
<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 Gas preventer",
"description": "Gas preventer with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<g fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background">
<rect x="58" y="177" width="84" height="22" rx="2"/>
<rect x="41" y="147" width="118" height="30" rx="2"/>
<rect x="1" y="1" width="198" height="146" rx="2"/>
<rect x="1" y="33" width="198" height="38"/>
<circle cx="20" cy="52" r="7"/>
<circle cx="52" cy="52" r="7"/>
<circle cx="84" cy="52" r="7"/>
<circle cx="116" cy="52" r="7"/>
<circle cx="148" cy="52" r="7"/>
<circle cx="180" cy="52" r="7"/>
</g><path d="m67.263 0s-67.263 0-67.263 33.5v164.18c0 1.3256 1.7909 2.3202 4 2.3202h192c2.209 0 4-0.9946 4-2.3202v-164.18c0-33.5-66.07-33.5-66.07-33.5h-33.93zm67.07 40.6c-1.2887 0-2.3333 0.6268-2.3333 1.4v150.2c0 0.7732 1.0447 1.4 2.3333 1.4h14.666c1.2887 0 2.3333-0.6268 2.3333-1.4v-150.2c0-0.7732-1.0447-1.4-2.3333-1.4z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,116)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 116)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

363
application/src/main/data/json/system/scada_symbols/gas-wellhead-hp.svg

@ -0,0 +1,363 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="1000" fill="none" version="1.1" viewBox="0 0 800 1000"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Gas wellhead",
"description": "Gas wellhead with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 4,
"widgetSizeY": 5,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<path d="m368 869h1v-45c0-0.552 0.448-1 1-1h60c0.552 0 1 0.448 1 1v45h1 38c0.552 0 1 0.448 1 1v60c0 0.552-0.448 1-1 1h-39v45c0 0.552-0.448 1-1 1h-60c-0.552 0-1-0.448-1-1v-45h-39c-0.552 0-1-0.448-1-1v-60c0-0.552 0.448-1 1-1h38z" stroke="#1A1A1A" stroke-width="2"/><rect x="385" y="116" width="30" height="143" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="106" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="259" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="385" y="588" width="30" height="225" stroke="#1A1A1A" stroke-width="2"/><rect x="385" y="341" width="30" height="225" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="578" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="331" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="813" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="566" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="377" y="94" width="46" height="10" rx="1" stroke="#1A1A1A" stroke-width="2"/><circle cx="400" cy="30" r="29" stroke="#1A1A1A" stroke-width="2"/><circle cx="400" cy="30" r="20" stroke="#1A1A1A" stroke-width="2"/><rect x="390" y="57" width="2" height="37" fill="#1A1A1A"/><rect x="176" y="285" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="408" y="57" width="2" height="37" fill="#1A1A1A"/><rect x="176" y="885" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="646" y="285" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="646" y="885" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="11" y="285" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="11" y="885" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="481" y="285" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="481" y="885" width="143" height="30" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="319" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="319" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="789" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="789" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="329" y="269" width="142" height="62" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="166" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="166" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="636" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="636" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="154" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="154" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="624" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="624" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="1" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="1" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="471" y="277" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="471" y="877" width="10" height="46" rx="1" stroke="#1A1A1A" stroke-width="2"/><g fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background">
<rect x="281" y="977" width="238" height="22" rx="1"/>
<path d="m436.76 159.34-0.692-0.919-0.813 0.814-27.767 27.766 27.767 27.766 0.813 0.814 0.692-0.919c5.803-7.698 9.243-17.279 9.243-27.661s-3.44-19.963-9.243-27.661zm-10.147-8.434 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381-10.382 0-19.963 3.44-27.661 9.243l-0.919 0.692 0.814 0.813 26.766 26.767 27.61-27.61zm-62.705 9.485-0.84-0.84-0.684 0.971c-5.28 7.488-8.381 16.623-8.381 26.479s3.101 18.991 8.381 26.479l0.684 0.971 27.45-27.45-26.61-26.61zm8.329 61.862-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243 9.856 0 18.991-3.101 26.479-8.381l0.971-0.684-28.45-28.45-26.766 26.767zm27.766-98.252c34.794 0 63 28.206 63 63s-28.206 63-63 63-63-28.206-63-63 28.206-63 63-63z"/>
<path d="m436.76 430.34-0.692-0.919-0.813 0.814-27.767 27.766 27.767 27.766 0.813 0.814 0.692-0.919c5.803-7.698 9.243-17.279 9.243-27.661s-3.44-19.963-9.243-27.661zm-10.147-8.434 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381-10.382 0-19.963 3.44-27.661 9.243l-0.919 0.692 0.814 0.813 26.766 26.767 27.61-27.61zm-62.705 9.485-0.84-0.84-0.684 0.971c-5.28 7.488-8.381 16.623-8.381 26.479s3.101 18.991 8.381 26.479l0.684 0.971 27.45-27.45-26.61-26.61zm8.329 61.862-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243 9.856 0 18.991-3.101 26.479-8.381l0.971-0.684-28.45-28.45-26.766 26.767zm27.766-98.252c34.794 0 63 28.206 63 63s-28.206 63-63 63-63-28.206-63-63 28.206-63 63-63z"/>
<path d="m436.76 677.34-0.692-0.919-0.813 0.814-27.767 27.766 27.767 27.766 0.813 0.814 0.692-0.919c5.803-7.698 9.243-17.279 9.243-27.661s-3.44-19.963-9.243-27.661zm-10.147-8.434 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381-10.382 0-19.963 3.44-27.661 9.243l-0.919 0.692 0.814 0.813 26.766 26.767 27.61-27.61zm-62.705 9.485-0.84-0.84-0.684 0.971c-5.28 7.488-8.381 16.623-8.381 26.479s3.101 18.991 8.381 26.479l0.684 0.971 27.45-27.45-26.61-26.61zm8.329 61.862-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243 9.856 0 18.991-3.101 26.479-8.381l0.971-0.684-28.45-28.45-26.766 26.767zm27.766-98.252c34.794 0 63 28.206 63 63s-28.206 63-63 63-63-28.206-63-63 28.206-63 63-63z"/>
<path d="m275.66 336.76 0.919-0.692-0.814-0.813-27.766-27.767-27.766 27.767-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.767 26.766 27.61 27.61zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.971 0.684 27.45 27.45 26.61-26.61zm-61.862 8.329-0.813-0.814-0.692 0.919c-5.803 7.698-9.243 17.279-9.243 27.661 0 9.856 3.101 18.991 8.381 26.479l0.684 0.971 28.45-28.45-26.767-26.766zm98.252 27.766c0 34.794-28.206 63-63 63s-63-28.206-63-63 28.206-63 63-63 63 28.206 63 63z"/>
<path d="m275.66 936.76 0.919-0.692-0.814-0.813-27.766-27.767-27.766 27.767-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.767 26.766 27.61 27.61zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.971 0.684 27.45 27.45 26.61-26.61zm-61.862 8.329-0.813-0.814-0.692 0.919c-5.803 7.698-9.243 17.279-9.243 27.661 0 9.856 3.101 18.991 8.381 26.479l0.684 0.971 28.45-28.45-26.767-26.766zm98.252 27.766c0 34.794-28.206 63-63 63s-63-28.206-63-63 28.206-63 63-63 63 28.206 63 63z"/>
<path d="m745.66 336.76 0.919-0.692-0.814-0.813-27.766-27.767-27.766 27.767-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.767 26.766 27.61 27.61zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.971 0.684 27.45 27.45 26.61-26.61zm-61.862 8.329-0.813-0.814-0.692 0.919c-5.803 7.698-9.243 17.279-9.243 27.661 0 9.856 3.101 18.991 8.381 26.479l0.684 0.971 28.45-28.45-26.767-26.766zm98.252 27.766c0 34.794-28.206 63-63 63s-63-28.206-63-63 28.206-63 63-63 63 28.206 63 63z"/>
<path d="m745.66 936.76 0.919-0.692-0.814-0.813-27.766-27.767-27.766 27.767-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.767 26.766 27.61 27.61zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.971 0.684 27.45 27.45 26.61-26.61zm-61.862 8.329-0.813-0.814-0.692 0.919c-5.803 7.698-9.243 17.279-9.243 27.661 0 9.856 3.101 18.991 8.381 26.479l0.684 0.971 28.45-28.45-26.767-26.766zm98.252 27.766c0 34.794-28.206 63-63 63s-63-28.206-63-63 28.206-63 63-63 63 28.206 63 63z"/>
<path d="m110.66 336.76 0.919-0.692-0.814-0.813-27.059-27.06-0.7071-0.707-0.7071 0.707-27.059 27.06-0.8134 0.813 0.9186 0.692c7.6985 5.803 17.28 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.06 26.059-0.7071 0.707 0.7071 0.707 26.903 26.903zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.9707 0.684 0.8399 0.84 25.903 25.903 0.7071 0.707 0.7071-0.707 25.903-25.903zm-61.862 8.329-0.8134-0.814-0.6923 0.919c-5.8024 7.698-9.2427 17.279-9.2427 27.661 0 9.856 3.1011 18.991 8.3807 26.479l0.6844 0.971 0.8399-0.84 26.903-26.903 0.7071-0.707-0.7071-0.707-26.059-26.059zm98.252 27.766c0 34.794-28.206 63-63 63-34.794 0-63-28.206-63-63s28.206-63 63-63c34.794 0 63 28.206 63 63z"/>
<path d="m110.66 936.76 0.919-0.692-0.814-0.813-27.059-27.06-0.7071-0.707-0.7071 0.707-27.059 27.06-0.8134 0.813 0.9186 0.692c7.6985 5.803 17.28 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.06 26.059-0.7071 0.707 0.7071 0.707 26.903 26.903zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.9707 0.684 0.8399 0.84 25.903 25.903 0.7071 0.707 0.7071-0.707 25.903-25.903zm-61.862 8.329-0.8134-0.814-0.6923 0.919c-5.8024 7.698-9.2427 17.279-9.2427 27.661 0 9.856 3.1011 18.991 8.3807 26.479l0.6844 0.971 0.8399-0.84 26.903-26.903 0.7071-0.707-0.7071-0.707-26.059-26.059zm98.252 27.766c0 34.794-28.206 63-63 63-34.794 0-63-28.206-63-63s28.206-63 63-63c34.794 0 63 28.206 63 63z"/>
<path d="m580.66 336.76 0.919-0.692-0.814-0.813-27.766-27.767-27.766 27.767-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.767 26.766 27.61 27.61zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.971 0.684 27.45 27.45 26.61-26.61zm-61.862 8.329-0.813-0.814-0.692 0.919c-5.803 7.698-9.243 17.279-9.243 27.661 0 9.856 3.101 18.991 8.381 26.479l0.684 0.971 28.45-28.45-26.767-26.766zm98.252 27.766c0 34.794-28.206 63-63 63s-63-28.206-63-63 28.206-63 63-63 63 28.206 63 63z"/>
<path d="m580.66 936.76 0.919-0.692-0.814-0.813-27.766-27.767-27.766 27.767-0.814 0.813 0.919 0.692c7.698 5.803 17.279 9.243 27.661 9.243s19.963-3.44 27.661-9.243zm8.434-10.147 0.84 0.84 0.684-0.971c5.28-7.488 8.381-16.623 8.381-26.479 0-10.382-3.44-19.963-9.243-27.661l-0.692-0.919-0.813 0.814-26.767 26.766 27.61 27.61zm-9.485-62.705 0.84-0.84-0.971-0.684c-7.488-5.28-16.623-8.381-26.479-8.381s-18.991 3.101-26.479 8.381l-0.971 0.684 27.45 27.45 26.61-26.61zm-61.862 8.329-0.813-0.814-0.692 0.919c-5.803 7.698-9.243 17.279-9.243 27.661 0 9.856 3.101 18.991 8.381 26.479l0.684 0.971 28.45-28.45-26.767-26.766zm98.252 27.766c0 34.794-28.206 63-63 63s-63-28.206-63-63 28.206-63 63-63 63 28.206 63 63z"/>
</g><path d="m269.05 0s-269.05 0-269.05 167.5v820.9c0 6.628 7.1636 11.601 16 11.601h768c8.836 0 16-4.973 16-11.601v-820.9c0-167.5-264.28-167.5-264.28-167.5h-135.72zm268.28 203c-5.1548 0-9.3332 3.134-9.3332 7v751c0 3.866 4.1788 7 9.3332 7h58.664c5.1548 0 9.3332-3.134 9.3332-7v-751c0-3.866-4.1788-7-9.3332-7z" fill="#000" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,916)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 916)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

363
application/src/main/data/json/system/scada_symbols/heat-exchanger-hp.svg

@ -0,0 +1,363 @@
<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 Heat exchanger",
"description": "Heat exchanger with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 3,
"widgetSizeY": 3,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<g fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background">
<path d="m1 5c0-2.2091 1.7909-4 4-4h590c2.209 0 4 1.7909 4 4v134h-598v-134z"/>
<path d="m121 317h358v54c0 2.209-1.791 4-4 4h-350c-2.209 0-4-1.791-4-4v-54z"/>
<rect x="271" y="375" width="58" height="80"/>
<rect x="248" y="456" width="104" height="14" rx="2"/>
<path d="m221 474c0-2.209 1.791-4 4-4h150c2.209 0 4 1.791 4 4v125h-158v-125z"/>
<path d="m120.65 317-79.111-178h516.92l-79.111 178h-358.7z"/>
<path d="m263 318v56"/>
<path d="m188 318v56"/>
<path d="m413 318v56"/>
<path d="m200 0v138"/>
<path d="m400 0v138"/>
<path d="m338 318v56"/>
</g><path d="m201.79 0s-201.79 0-201.79 100.5v492.54c0 3.9768 5.3727 6.9606 12 6.9606h576c6.627 0 12-2.9838 12-6.9606v-492.54c0-100.5-198.21-100.5-198.21-100.5h-101.79zm201.21 121.8c-3.8661 0-6.9999 1.8804-6.9999 4.2v450.6c0 2.3196 3.1341 4.2 6.9999 4.2h43.998c3.8661 0 6.9999-1.8804 6.9999-4.2v-450.6c0-2.3196-3.1341-4.2-6.9999-4.2z" fill-opacity="0" tb:tag="clickArea"/><g fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

2
application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg

@ -1,6 +1,6 @@
<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 connector",
"description": "Horizontal connector",
"description": "Horizontal connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

108
application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg

@ -57,7 +57,7 @@
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
"stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'displayClick');"
@ -463,16 +463,20 @@
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "m³/hr",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "medium-width",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.decimals}",
"type": "number",
"default": 0,
"fieldClass": "medium-width",
"min": 0,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "defaultBorderColor",
@ -511,16 +515,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "activeBorderColor",
@ -559,16 +555,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "warningBorderColor",
@ -607,16 +595,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "criticalBorderColor",
@ -655,16 +635,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "backgroundColor",
@ -703,48 +675,24 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#1EC1F480",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 76 KiB

342
application/src/main/data/json/system/scada_symbols/large-horizontal-separator-connector-hp.svg

@ -0,0 +1,342 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1000" height="600" fill="none" version="1.1" viewBox="0 0 1000 600"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Large horizontal separator with connector",
"description": "Large horizontal separator with connector with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 5,
"widgetSizeY": 3,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<rect x="1" y="81" width="898" height="518" rx="159" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m1e3 300h-8c-16.569 0-30-13.431-30-30v-237c0-16.568-13.431-30-30-30h-240c-16.569 0-30 13.432-30 30v47" stroke="#1A1A1A" stroke-width="6"/><path d="m336.71-0.31324s-336.32 0-336.32 100.5v492.54c0 3.9768 8.9545 6.9605 20 6.9605h960c11.045 0 20-2.9838 20-6.9605v-492.54c0-100.5-330.35-100.5-330.35-100.5h-169.65zm335.35 121.8c-6.4435 0-11.666 1.8804-11.666 4.2v450.6c0 2.3196 5.2235 4.2 11.666 4.2h73.33c6.4435 0 11.666-1.8804 11.666-4.2v-450.6c0-2.3196-5.2235-4.2-11.666-4.2z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,516)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,516)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

350
application/src/main/data/json/system/scada_symbols/large-horizontal-separator-hp.svg

@ -0,0 +1,350 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1000" height="600" fill="none" version="1.1" viewBox="0 0 1000 600"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Large horizontal separator",
"description": "Large horizontal separator with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 5,
"widgetSizeY": 3,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<rect x="1" y="1" width="998" height="598" rx="159" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m336.32 0s-336.32 0-336.32 100.5v492.54c0 3.9768 8.9545 6.9605 20 6.9605h960c11.045 0 20-2.9838 20-6.9605v-492.54c0-100.5-330.35-100.5-330.35-100.5h-169.65zm335.35 121.8c-6.4435 0-11.666 1.8804-11.666 4.2v450.6c0 2.3196 5.2235 4.2 11.666 4.2h73.33c6.4435 0 11.666-1.8804 11.666-4.2v-450.6c0-2.3196-5.2235-4.2-11.666-4.2z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,516)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 516)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

350
application/src/main/data/json/system/scada_symbols/large-vertical-separator-connector-hp.svg

@ -0,0 +1,350 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="1000" fill="none" version="1.1" viewBox="0 0 600 1000"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Large vertical separator with connector",
"description": "Large vertical separator with connector with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 3,
"widgetSizeY": 5,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<path d="m1 744c0 44.821 7.8e-4 78.376 2.1766 105.01 2.1748 26.619 6.5177 46.23 15.153 63.178 15.244 29.918 39.568 54.242 69.486 69.486 16.948 8.636 36.56 12.979 63.178 15.153 26.63 2.176 60.185 2.177 105.01 2.177h8c44.821 0 78.376-1e-3 105.01-2.177 26.619-2.174 46.23-6.517 63.178-15.153 29.918-15.244 54.242-39.568 69.486-69.486 8.636-16.948 12.979-36.559 15.153-63.178 2.176-26.63 2.177-60.185 2.177-105.01v-388c0-44.821-1e-3 -78.376-2.177-105.01-2.174-26.619-6.517-46.23-15.153-63.179-15.244-29.917-39.568-54.241-69.486-69.485-16.948-8.636-36.559-12.979-63.178-15.153-26.63-2.176-60.185-2.177-105.01-2.177h-8c-44.821 0-78.376 1e-3 -105.01 2.177-26.619 2.174-46.23 6.517-63.178 15.153-29.918 15.244-54.242 39.568-69.486 69.485-8.6357 16.949-12.979 36.56-15.153 63.179-2.1758 26.63-2.1766 60.185-2.1766 105.01v388z" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m600 700h-10c-16.569 0-30-13.431-30-30v-637c0-16.569-13.431-30-30-30h-240c-16.569 0-30 13.432-30 30v67" stroke="#1A1A1A" stroke-width="6"/><path d="m201.92 0s-201.79 0-201.79 167.5v820.9c0 6.628 5.3727 11.601 12 11.601h576c6.627 0 12-4.973 12-11.601v-820.9c0-167.5-198.21-167.5-198.21-167.5h-101.79zm201.21 203c-3.8661 0-6.9996 3.134-6.9996 7v751c0 3.866 3.1341 7 6.9996 7h43.998c3.8661 0 6.9996-3.134 6.9996-7v-751c0-3.866-3.1341-7-6.9996-7z" fill="#000" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,916)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 916)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

350
application/src/main/data/json/system/scada_symbols/large-vertical-separator-hp.svg

@ -0,0 +1,350 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="1000" fill="none" version="1.1" viewBox="0 0 600 1000"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Large vertical separator",
"description": "Large vertical separator with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 3,
"widgetSizeY": 5,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<rect x="1" y="1" width="598" height="998" rx="159" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m201.79 0s-201.79 0-201.79 167.5v820.9c0 6.628 5.3727 11.601 12 11.601h576c6.627 0 12-4.973 12-11.601v-820.9c0-167.5-198.21-167.5-198.21-167.5h-101.79zm201.21 203c-3.8661 0-6.9996 3.134-6.9996 7v751c0 3.866 3.1341 7 6.9996 7h43.998c3.8661 0 6.9996-3.134 6.9996-7v-751c0-3.866-3.1341-7-6.9996-7z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,916)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 916)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

2
application/src/main/data/json/system/scada_symbols/leak-sensor.svg

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="100" height="100" fill="none" version="1.1" viewBox="0 0 100 100"><tb:metadata xmlns=""><![CDATA[{
"title": "Leak sensor",
"description": "Leak sensor",
"description": "Leak sensor for real-time detection and warning of fluid leakage.",
"searchTags": [
"leak"
],

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

2
application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg

@ -1,6 +1,6 @@
<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": "Left analog water level meter",
"description": "Left analog water level meter",
"description": "Left analog water level meter with real-time display of fluid levels.",
"searchTags": [
"water meter"
],

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

2
application/src/main/data/json/system/scada_symbols/left-drain-pipe.svg

@ -1,7 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200">
<tb:metadata><![CDATA[{
"title": "Left drain pipe",
"description": "Left drain pipe",
"description": "Left drain pipe with configurable fluid and leak visualizations.",
"searchTags": [
"pipe",
"drain"

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

2
application/src/main/data/json/system/scada_symbols/left-elbow-drain-pipe.svg

@ -1,7 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200">
<tb:metadata xmlns=""><![CDATA[{
"title": "Left elbow drain pipe",
"description": "Left elbow drain pipe",
"description": "Left elbow drain pipe with configurable fluid and leak visualizations.",
"searchTags": [
"pipe",
"drain"

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

104
application/src/main/data/json/system/scada_symbols/left-flow-meter.svg

@ -57,7 +57,7 @@
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
"stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'displayClick');"
@ -463,16 +463,16 @@
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "m³/hr",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "medium-width"
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.decimals}",
"type": "number",
"default": 0,
"fieldClass": "medium-width",
"min": null,
"max": null,
"step": null
"min": 0,
"step": 1
},
{
"id": "defaultBorderColor",
@ -510,17 +510,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "activeBorderColor",
@ -558,17 +548,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "warningBorderColor",
@ -606,17 +586,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "criticalBorderColor",
@ -654,17 +624,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "backgroundColor",
@ -702,49 +662,19 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#1EC1F480",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"default": "#1EC1F480"
},
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"default": "#FFFFFF"
}
]
}]]></tb:metadata>

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 72 KiB

2
application/src/main/data/json/system/scada_symbols/long-bottom-filter.svg

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="600" fill="none" version="1.1" viewBox="0 0 200 600">
<tb:metadata xmlns=""><![CDATA[{
"title": "Long bottom filter",
"title": "Long bottom filter with configurable click actions for custom operations, dashboard manipulation, etc.",
"description": "Long bottom filter",
"searchTags": [
"filter"

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

2
application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg

@ -1,7 +1,7 @@
<svg width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200" xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg">
<tb:metadata><![CDATA[{
"title": "HP Long horizontal connector",
"description": "Long horizontal connector",
"description": "Long horizontal connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 2,
"widgetSizeY": 1,
"tags": [

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

2
application/src/main/data/json/system/scada_symbols/long-top-filter.svg

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="600" fill="none" version="1.1" viewBox="0 0 200 600">
<tb:metadata xmlns=""><![CDATA[{
"title": "Long top filter",
"title": "Long top filter with configurable click actions for custom operations, dashboard manipulation, etc.",
"description": "Title\nLong top filter\n",
"searchTags": [
"filter"

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

2
application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg

@ -1,6 +1,6 @@
<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",
"description": "Long vertical connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 1,
"widgetSizeY": 2,
"tags": [

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

357
application/src/main/data/json/system/scada_symbols/oil-pump-hp.svg

@ -0,0 +1,357 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1000" height="1200" fill="none" version="1.1" viewBox="0 0 1000 1200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Oil pump",
"description": "Oil pump with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 5,
"widgetSizeY": 6,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<rect x="1" y="1161" width="998" height="38" rx="3" stroke="#1A1A1A" stroke-width="2"/><path d="m315.2 1161 151.18-840.74 63.841-21.443 154.58 862.18h-369.61z" stroke="#1A1A1A" stroke-width="2"/><path d="m900 541v384" stroke="#000" stroke-width="6"/><path d="m315 1160 313-313" stroke="#1A1A1A" stroke-width="2"/><path d="m685 1160-313-313" stroke="#1A1A1A" stroke-width="2"/><path d="m628 847-217-217" stroke="#1A1A1A" stroke-width="2"/><path d="m589 630-151-151" stroke="#1A1A1A" stroke-width="2"/><path d="m562 479-106-106" stroke="#1A1A1A" stroke-width="2"/><path d="m438 479 106-106" stroke="#1A1A1A" stroke-width="2"/><path d="m411 630 151-151" stroke="#1A1A1A" stroke-width="2"/><path d="m372 847 217-217" stroke="#1A1A1A" stroke-width="2"/><path d="m372 847h256" stroke="#1A1A1A" stroke-width="2"/><path d="m410 630h180" stroke="#1A1A1A" stroke-width="2"/><path d="m438 479h124" stroke="#1A1A1A" stroke-width="2"/><path d="m114 451 154 564" stroke="#1A1A1A" stroke-width="2"/><rect x="861" y="1143" width="78" height="18" rx="1" stroke="#1A1A1A" stroke-width="2"/><rect x="881" y="925" width="38" height="218" stroke="#1A1A1A" stroke-width="2"/><path d="m920 1100h80" stroke="#1A1A1A" stroke-width="6"/><g fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background">
<path d="m91.339 388.56c0.0845-1.178 0.8541-2.197 1.9665-2.601l563.4-204.78c1.558-0.567 3.28 0.236 3.847 1.791l0.939-0.343-0.939 0.343 23.263 63.828c0.566 1.553-0.235 3.272-1.793 3.839l-592 215.18c-2.0459 0.744-4.1755-0.866-4.0202-3.029l5.3305-74.221z"/>
<path d="m682.58 4.8723-69.104 147.33c-0.776 1.654-0.587 3.599 0.493 5.073l279.32 381.28c0.926 1.265 2.393 2.021 3.961 2.041l31.595 0.409c2.586 0.033 4.775-1.947 5.019-4.523 24.171-254.85-96.897-453.81-161.98-523.16-0.794-0.8464-1.821-1.3367-2.973-1.4758l-81.21-9.8032c-2.138-0.25813-4.201 0.88532-5.116 2.8362z"/>
<path d="m341.58 903.6c0.934-0.934 2.338-0.737 2.922 0.302 4.821 8.575 17.591 33.981 20.073 66.303 2.478 32.286-5.299 71.486-41.597 107.78-36.297 36.3-75.496 44.08-107.78 41.6-32.322-2.48-57.728-15.25-66.303-20.07-1.039-0.59-1.236-1.99-0.302-2.93l192.98-192.98z"/>
<path d="m24.767 832.67c0.5636-1.6 2.0759-2.671 3.7729-2.671h152.67c1.791 0 3.364 1.191 3.85 2.915l18.592 66c0.199 0.71 0.199 1.46 0 2.17l-18.592 66c-0.486 1.724-2.059 2.915-3.85 2.915h-152.67c-1.697 0-3.2093-1.071-3.7729-2.671l-23.239-66c-0.30274-0.86-0.30274-1.798 0-2.658l23.239-66z"/>
<rect x="65" y="970" width="76" height="191"/>
<path d="m134.96 889.96c6.62-6.619 17.351-6.619 23.97 0l124.1 124.11c6.619 6.61 6.619 17.35 0 23.97-6.62 6.61-17.351 6.61-23.97 0l-124.1-124.11c-6.619-6.619-6.619-17.35 0-23.97z"/>
</g><path d="m278.34 1033.8c-4.296 4.29-11.261 4.29-15.557 0-4.295-4.3-4.295-11.26 0-15.56 4.296-4.29 11.261-4.29 15.557 0 4.296 4.3 4.296 11.26 0 15.56z" stroke="#1A1A1A" stroke-width="2"/><path d="m155.75 910.75c-4.296 4.296-11.261 4.296-15.556 0-4.296-4.296-4.296-11.261 0-15.556 4.295-4.296 11.26-4.296 15.556 0 4.296 4.295 4.296 11.26 0 15.556z" stroke="#1A1A1A" stroke-width="2"/><path d="m336.32 0s-336.32 0-336.32 201v985.08c0 7.9536 8.9545 13.921 20 13.921h960c11.045 0 20-5.9676 20-13.921v-985.08c0-201-330.35-201-330.35-201h-169.65zm335.35 243.6c-6.4435 0-11.666 3.7608-11.666 8.4v901.2c0 4.6392 5.2235 8.4 11.666 8.4h73.33c6.4435 0 11.666-3.7608 11.666-8.4v-901.2c0-4.6392-5.2235-8.4-11.666-8.4z" fill="#000" fill-opacity="0" style="stroke-width:2.2361" tb:tag="clickArea"/><g transform="translate(0,1116)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 1116)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

2
application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg

@ -1,6 +1,6 @@
<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": "Right analog water level meter",
"description": "Right analog water level meter",
"description": "Right analog water level meter with real-time display of fluid levels.",
"searchTags": [
"water meter"
],

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

2
application/src/main/data/json/system/scada_symbols/right-drain-pipe.svg

@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200"><tb:metadata xmlns=""><![CDATA[{
"title": "Right drain pipe",
"description": "Right drain pipe",
"description": "Right drain pipe with configurable fluid and leak visualizations.",
"searchTags": [
"pipe",
"drain"

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

2
application/src/main/data/json/system/scada_symbols/right-elbow-drain-pipe.svg

@ -1,7 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200">
<tb:metadata xmlns=""><![CDATA[{
"title": "Right elbow drain pipe",
"description": "Right elbow drain pipe",
"description": "Right elbow drain pipe with configurable fluid and leak visualizations.",
"searchTags": [
"pipe",
"drain"

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

104
application/src/main/data/json/system/scada_symbols/right-flow-meter.svg

@ -57,7 +57,7 @@
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
"stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'displayClick');"
@ -463,16 +463,16 @@
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "m³/hr",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "medium-width"
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.decimals}",
"type": "number",
"default": 0,
"fieldClass": "medium-width",
"min": null,
"max": null,
"step": null
"min": 0,
"step": 1
},
{
"id": "defaultBorderColor",
@ -510,17 +510,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "activeBorderColor",
@ -558,17 +548,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "warningBorderColor",
@ -606,17 +586,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "criticalBorderColor",
@ -654,17 +624,7 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "backgroundColor",
@ -702,49 +662,19 @@
"rangeAdvanced": []
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#1EC1F480",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"default": "#1EC1F480"
},
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"default": "#FFFFFF"
}
]
}]]></tb:metadata>

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 72 KiB

2
application/src/main/data/json/system/scada_symbols/short-bottom-filter.svg

@ -1,7 +1,7 @@
<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": "Short bottom filter",
"description": "Short bottom filter",
"description": "Short bottom filter with configurable click actions for custom operations, dashboard manipulation, etc.",
"searchTags": [
"filter"
],

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

2
application/src/main/data/json/system/scada_symbols/short-left-drain-pipe.svg

@ -1,7 +1,7 @@
<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><![CDATA[{
"title": "Short left drain pipe",
"description": "Short left drain pipe",
"description": "Short left drain pipe with configurable fluid and leak visualizations.",
"searchTags": [
"pipe",
"drain"

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

2
application/src/main/data/json/system/scada_symbols/short-right-drain-pipe.svg

@ -1,7 +1,7 @@
<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><![CDATA[{
"title": "Short right drain pipe",
"description": "Short right drain pipe",
"description": "Short right drain pipe with configurable fluid and leak visualizations.",
"searchTags": [
"pipe",
"drain"

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

2
application/src/main/data/json/system/scada_symbols/short-top-filter.svg

@ -1,6 +1,6 @@
<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": "Short top filter",
"title": "Short top filter with configurable click actions for custom operations, dashboard manipulation, etc.",
"description": "Short top filter",
"searchTags": [
"filter"

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

2
application/src/main/data/json/system/scada_symbols/simple-vertical-scale-hp.svg

@ -704,7 +704,7 @@
}
]
}]]></tb:metadata>
<text x="206.16602" y="43.234375" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="195.21875" y="786.75" fill="#000000" font-family="Roboto" font-size="40px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g stroke="#000" tb:tag="scale">
<text x="206.16602" y="43.234375" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="195.21875" y="770" fill="#000000" font-family="Roboto" font-size="40px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g stroke="#000" tb:tag="scale">
<rect x="176.5" y="83" width="41" height="652" fill="#c8dff7" tb:tag="scaleBackground"/>
<rect transform="scale(1,-1)" x="176.5" y="-735" width="41" height="164" fill="#ebebeb" tb:tag="lowWarningScale"/>
<rect x="176.5" y="83.5" width="41" height="164" fill="#ebebeb" tb:tag="highWarningScale"/>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

342
application/src/main/data/json/system/scada_symbols/small-horizontal-separator-connector-hp.svg

@ -0,0 +1,342 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1000" height="400" fill="none" version="1.1" viewBox="0 0 1000 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Small horizontal separator with connector",
"description": "Small horizontal separator with connector with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 5,
"widgetSizeY": 2,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<rect x="1" y="81" width="898" height="318" rx="119" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m1e3 300h-8c-16.569 0-30-13.431-30-30v-237c0-16.568-13.431-30-30-30h-240c-16.569 0-30 13.432-30 30v47" stroke="#1A1A1A" stroke-width="6"/><path d="m336.71 0.31299s-336.32 0-336.32 67v328.36c0 2.6512 8.9545 4.6403 20 4.6403h960c11.045 0 20-1.9892 20-4.6403v-328.36c0-67-330.35-67-330.35-67h-169.65zm335.35 81.2c-6.4435 0-11.666 1.2536-11.666 2.8v300.4c0 1.5464 5.2235 2.8 11.666 2.8h73.33c6.4435 0 11.666-1.2536 11.666-2.8v-300.4c0-1.5464-5.2235-2.8-11.666-2.8z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,316)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 316)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

342
application/src/main/data/json/system/scada_symbols/small-horizontal-separator-hp.svg

@ -0,0 +1,342 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1000" height="400" fill="none" version="1.1" viewBox="0 0 1000 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Small horizontal separator",
"description": "Small horizontal separator with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 5,
"widgetSizeY": 2,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<rect x="1" y="1" width="998" height="398" rx="159" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m336.32 0s-336.32 0-336.32 67v328.36c0 2.6512 8.9545 4.6403 20 4.6403h960c11.045 0 20-1.9892 20-4.6403v-328.36c0-67-330.35-67-330.35-67h-169.65zm335.35 81.2c-6.4435 0-11.666 1.2536-11.666 2.8v300.4c0 1.5464 5.2235 2.8 11.666 2.8h73.33c6.4435 0 11.666-1.2536 11.666-2.8v-300.4c0-1.5464-5.2235-2.8-11.666-2.8z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,316)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 316)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

342
application/src/main/data/json/system/scada_symbols/small-vertical-separator-connector-hp.svg

@ -0,0 +1,342 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="1000" fill="none" version="1.1" viewBox="0 0 400 1000"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Small vertical separator with connector",
"description": "Small vertical separator with connector with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 2,
"widgetSizeY": 5,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m1 260c0-37.263 0.00859-55.699 4.0548-70.799 11.004-41.066 43.08-73.143 84.146-84.146 15.1-4.046 33.536-4.055 70.8-4.055s55.699 9e-3 70.799 4.055c41.066 11.003 73.143 43.08 84.146 84.146 4.046 15.1 4.055 33.536 4.055 70.799v580c0 37.263-9e-3 55.699-4.055 70.799-11.003 41.066-43.08 73.143-84.146 84.146-15.1 4.046-33.536 4.055-70.799 4.055s-55.699-9e-3 -70.8-4.055c-41.066-11.003-73.142-43.08-84.146-84.146-4.0462-15.1-4.0548-33.536-4.0548-70.799v-580z" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m400 700h-8c-16.569 0-30-13.431-30-30v-637c0-16.569-13.431-30-30-30h-147c-16.569 0-30 13.432-30 30v67" stroke="#1A1A1A" stroke-width="6"/><path d="m134.4 0s-134.53 0-134.53 167.5v820.9c0 6.628 3.5818 11.601 8 11.601h384c4.418 0 8-4.973 8-11.601v-820.9c0-167.5-132.14-167.5-132.14-167.5h-67.86zm134.14 203c-2.5774 0-4.6664 3.134-4.6664 7v751c0 3.866 2.0894 7 4.6664 7h29.332c2.5774 0 4.6664-3.134 4.6664-7v-751c0-3.866-2.0894-7-4.6664-7z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,916)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 916)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

342
application/src/main/data/json/system/scada_symbols/small-vertical-separator-hp.svg

@ -0,0 +1,342 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="1000" fill="none" version="1.1" viewBox="0 0 400 1000"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Small vertical separator",
"description": "Small vertical separator with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 2,
"widgetSizeY": 5,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m1 200c0-37.22 0.00484-55.684 3.0551-71.019 12.546-63.074 61.852-112.38 124.93-124.93 15.335-3.0503 33.799-3.0551 71.019-3.0551 37.22 1e-5 55.684 0.00485 71.019 3.0552 63.074 12.546 112.38 61.852 124.93 124.93 3.05 15.335 3.055 33.799 3.055 71.019v600c0 37.22-5e-3 55.684-3.055 71.019-12.546 63.074-61.852 112.38-124.93 124.93-15.335 3.05-33.799 3.055-71.019 3.055s-55.684-5e-3 -71.019-3.055c-63.074-12.546-112.38-61.852-124.93-124.93-3.0503-15.335-3.0551-33.799-3.0551-71.019v-600z" fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background"/><path d="m134.53 0s-134.53 0-134.53 167.5v820.9c0 6.628 3.5818 11.601 8 11.601h384c4.418 0 8-4.973 8-11.601v-820.9c0-167.5-132.14-167.5-132.14-167.5h-67.86zm134.14 203c-2.5774 0-4.6664 3.134-4.6664 7v751c0 3.866 2.0894 7 4.6664 7h29.332c2.5774 0 4.6664-3.134 4.6664-7v-751c0-3.866-2.0894-7-4.6664-7z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,916)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 916)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

108
application/src/main/data/json/system/scada_symbols/top-flow-meter.svg

@ -57,7 +57,7 @@
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
"stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'displayClick');"
@ -463,16 +463,20 @@
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "m³/hr",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "medium-width",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.decimals}",
"type": "number",
"default": 0,
"fieldClass": "medium-width",
"min": 0,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "defaultBorderColor",
@ -511,16 +515,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "activeBorderColor",
@ -559,16 +555,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "warningBorderColor",
@ -607,16 +595,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "criticalBorderColor",
@ -655,16 +635,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "backgroundColor",
@ -703,48 +675,24 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#1EC1F480",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 72 KiB

377
application/src/main/data/json/system/scada_symbols/turbine-hp.svg

@ -0,0 +1,377 @@
<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 Turbine",
"description": "Turbine with various states.",
"searchTags": [
"extraction"
],
"widgetSizeX": 4,
"widgetSizeY": 2,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "secondary-background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = '#999';\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": null,
"typeList": null
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": "{i18n:scada.symbol.warning-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"MAJOR",
"MINOR",
"WARNING",
"INDETERMINATE"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": "{i18n:scada.symbol.critical-state}",
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "GET_ALARM_STATUS",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"getAlarmStatus": {
"severityList": [
"CRITICAL"
],
"typeList": null
},
"dataToValue": {
"type": "NONE",
"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.warning-critical-state-animation}",
"hint": "{i18n:scada.symbol.warning-critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-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": "runningColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "{i18n:scada.symbol.running}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.colors}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "{i18n:scada.symbol.stopped}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "{i18n:scada.symbol.warning}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.alarm-colors}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "{i18n:scada.symbol.critical}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null,
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>
<g fill="#fff" stroke="#1A1A1A" stroke-width="2" tb:tag="background">
<path d="m24 7.458c0-1.4928 1.0864-2.743 2.5495-2.9373 7.9591-1.0573 28.201-3.5206 43.95-3.5206s35.991 2.4633 43.95 3.5206c1.464 0.19436 2.55 1.4446 2.55 2.9373v385.06c0 1.501-1.093 2.755-2.564 2.944-8.156 1.05-29.041 3.535-43.936 3.535s-35.78-2.485-43.936-3.535c-1.4708-0.189-2.5641-1.443-2.5641-2.944v-385.06z"/>
<path d="M1 16H24V384H1V16Z"/>
<path d="m117 36h141.26c0.685 0 1.361 0.1404 1.989 0.4125l38.745 16.796c1.828 0.7925 3.011 2.5949 3.011 4.5875v284.41c0 1.993-1.183 3.795-3.011 4.588l-38.745 16.795c-0.628 0.273-1.304 0.413-1.989 0.413h-141.26v-328z"/>
<rect x="302" y="91" width="321" height="218"/>
<rect x="334" y="91" width="98" height="218"/>
<rect x="623" y="59" width="118" height="282"/>
<rect x="741" y="121" width="41" height="158"/>
<rect x="782" y="111" width="17" height="178"/>
</g><g fill="#999" stroke="#1A1A1A" stroke-width="2" tb:tag="secondary-background">
<rect x="578" y="91" width="12" height="218"/>
<rect x="540" y="91" width="12" height="218"/>
<rect x="502" y="91" width="12" height="218"/>
<rect x="251" y="36" width="8" height="328"/>
<rect x="215" y="36" width="8" height="328"/>
<rect x="179" y="36" width="8" height="328"/>
<rect x="464" y="91" width="12" height="218"/>
<rect x="143" y="36" width="8" height="328"/>
<path d="m196.96 364c-0.419 5.029-4 8.736-9.2 11.449-5.538 2.89-13.138 4.785-21.485 6.04-16.304 2.452-35.811 2.51-49.274 2.511v8.999c15.934-0.021 38.037-0.586 56.285-4.601 9.322-2.05 17.547-4.983 23.419-9.136 5.552-3.927 8.95-8.899 9.271-15.262h-9.016z"/>
<path d="m785.99 289c-0.236 12.22-4.835 20.271-12.839 25.225-7.92 4.903-19.041 6.687-32.151 6.772v8.998c15.452-0.143 28.851-3.617 38.42-10.434 9.501-6.77 15.301-16.886 15.57-30.561h-9z"/>
<path d="m785.99 111c-0.236-12.22-4.835-20.271-12.839-25.225-7.92-4.9031-19.041-6.6864-32.151-6.7715v-8.9986c15.452 0.1433 28.851 3.6171 38.42 10.435 9.501 6.7698 15.301 16.886 15.57 30.561h-9z"/>
<path d="m196.96 36c-0.419-5.0286-4-8.7363-9.2-11.449-5.538-2.8898-13.138-4.7845-21.485-6.0398-16.304-2.4517-35.811-2.5097-49.274-2.5111v-8.9993c15.934 0.02162 38.037 0.58638 56.285 4.6009 9.322 2.0509 17.547 4.9835 23.419 9.1367 5.552 3.9269 8.95 8.8992 9.271 15.262h-9.016z"/>
</g><path d="m269.05 0s-269.05 0-269.05 67v328.36c0 2.6512 7.1636 4.6404 16 4.6404h768c8.836 0 16-1.9892 16-4.6404v-328.36c0-67-264.28-67-264.28-67h-135.72zm268.28 81.2c-5.1548 0-9.3332 1.2536-9.3332 2.8v300.4c0 1.5464 4.1788 2.8 9.3332 2.8h58.664c5.1548 0 9.3332-1.2536 9.3332-2.8v-300.4c0-1.5464-4.1788-2.8-9.3332-2.8z" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,316)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 316)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

2
application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg

@ -1,6 +1,6 @@
<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 connector",
"description": "Vertical connector",
"description": "Vertical connector with an optional directional arrow to visually indicate flow.",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

108
application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg

@ -57,7 +57,7 @@
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
"stateRenderFunction": "var value = ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, '', false);\nctx.api.text(element, value);\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'displayClick');"
@ -463,16 +463,20 @@
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "m³/hr",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "medium-width",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.decimals}",
"type": "number",
"default": 0,
"fieldClass": "medium-width",
"min": 0,
"step": 1,
"disabled": false,
"visible": true
},
{
"id": "defaultBorderColor",
@ -511,16 +515,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "activeBorderColor",
@ -559,16 +555,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "warningBorderColor",
@ -607,16 +595,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "criticalBorderColor",
@ -655,16 +635,8 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "backgroundColor",
@ -703,48 +675,24 @@
},
"colorFunction": "var temperature = value;\nif (typeof temperature !== undefined) {\n var percent = (temperature + 60)/120 * 100;\n return tinycolor.mix('blue', 'red', percent).toHexString();\n}\nreturn 'blue';"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#1EC1F480",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
},
{
"id": "pipeColor",
"name": "{i18n:scada.symbol.pipe-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
"disabled": false,
"visible": true
}
]
}]]></tb:metadata>

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 76 KiB

1
application/src/main/data/json/system/widget_bundles/buttons.json

@ -12,6 +12,7 @@
"command_button",
"toggle_button",
"two_segment_button",
"value_stepper",
"power_button"
]
}

1
application/src/main/data/json/system/widget_bundles/control_widgets.json

@ -12,6 +12,7 @@
"command_button",
"toggle_button",
"two_segment_button",
"value_stepper",
"power_button",
"slider",
"control_widgets.switch_control",

4
application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json

@ -29,9 +29,11 @@
"hp_top_tee_connector",
"hp_drawwork",
"hp_crane",
"hp_hook",
"hp_consumers",
"hp_house",
"hp_apartments",
"hp_manufacture"
"hp_manufacture",
"hp_electrical_engine"
]
}

2
application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json

File diff suppressed because one or more lines are too long

16
application/src/main/data/json/system/widget_bundles/high_performance_scada_oil_gas.json

@ -15,6 +15,20 @@
"hp_preventer",
"hp_drill",
"hp_drilling_line",
"hp_platform"
"hp_platform",
"hp_electrical_engine",
"hp_oil_pump",
"hp_gas_wellhead",
"hp_gas_preventer",
"hp_heat_exchanger",
"hp_turbine",
"hp_large_vertical_separator",
"hp_large_vertical_separator_with_connector",
"hp_large_horizontal_separator",
"hp_large_horizontal_separator_with_connector",
"hp_small_vertical_separator",
"hp_small_vertical_separator_with_connector",
"hp_small_horizontal_separator",
"hp_small_horizontal_separator_with_connector"
]
}

51
application/src/main/data/json/system/widget_types/value_stepper.json

@ -0,0 +1,51 @@
{
"fqn": "value_stepper",
"name": "Value stepper",
"deprecated": false,
"image": "tb-image;/api/images/system/value-stepper-widget.svg",
"description": "Allows users to click the buttons to send commands to devices or update attributes/time series data. Configurable settings let users define how to retrieve the initial state and specify actions for each button.",
"descriptor": {
"type": "rpc",
"sizeX": 3.5,
"sizeY": 2,
"resources": [],
"templateHtml": "<tb-value-stepper-widget\n [ctx]='ctx'\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-value-stepper-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.actionWidget.onInit();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '230px',\n previewHeight: '110px',\n embedTitlePanel: true,\n displayRpcMessageToast: false\n };\n};\n\nself.onDestroy = function() {\n}\n",
"dataKeySettingsForm": [],
"settingsDirective": "tb-value-stepper-widget-settings",
"hasBasicMode": true,
"basicModeDirective": "tb-value-stepper-basic-config",
"defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#ffffff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"initialState\":{\"action\":\"EXECUTE_RPC\",\"defaultValue\":0,\"executeRpc\":{\"method\":\"getState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return integer value */\\nreturn data;\"}},\"disabledState\":{\"action\":\"DO_NOTHING\",\"defaultValue\":false,\"getAttribute\":{\"key\":\"state\",\"scope\":null},\"getTimeSeries\":{\"key\":\"state\"},\"getAlarmStatus\":{\"severityList\":null,\"typeList\":null},\"dataToValue\":{\"type\":\"NONE\",\"compareToValue\":true,\"dataToValueFunction\":\"/* Should return boolean value */\\nreturn data;\"}},\"leftButtonClick\":{\"action\":\"EXECUTE_RPC\",\"executeRpc\":{\"method\":\"setState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"setAttribute\":{\"key\":\"state\",\"scope\":\"SERVER_SCOPE\"},\"putTimeSeries\":{\"key\":\"state\"},\"valueToData\":{\"type\":\"VALUE\",\"constantValue\":0,\"valueToDataFunction\":\"/* Convert input integer value to RPC parameters or attribute/time-series value */\\nreturn value;\"}},\"rightButtonClick\":{\"action\":\"EXECUTE_RPC\",\"executeRpc\":{\"method\":\"setState\",\"requestTimeout\":5000,\"requestPersistent\":false,\"persistentPollingInterval\":1000},\"setAttribute\":{\"key\":\"state\",\"scope\":\"SERVER_SCOPE\"},\"putTimeSeries\":{\"key\":\"state\"},\"valueToData\":{\"type\":\"VALUE\",\"constantValue\":0,\"valueToDataFunction\":\"/* Convert input integer value to RPC parameters or attribute/time-series value */\\nreturn value;\"}},\"appearance\":{\"type\":\"simplified\",\"autoScale\":true,\"minValueRange\":-100,\"maxValueRange\":100,\"valueStep\":0.5,\"showValueBox\":true,\"valueUnits\":\"\",\"valueDecimals\":1,\"valueFont\":{\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"size\":16,\"sizeUnit\":\"px\",\"lineHeight\":\"24px\"},\"valueColor\":\"#000\",\"valueBoxBackground\":\"rgba(0, 0, 0, 0.04)\",\"showBorder\":true,\"borderWidth\":1,\"borderColor\":\"#305680\"},\"buttonAppearance\":{\"leftButton\":{\"showButton\":true,\"icon\":\"arrow_back_ios_new\",\"iconSize\":24,\"iconSizeUnit\":\"px\",\"mainColorOn\":\"#3F52DD\",\"backgroundColorOn\":\"#FFFFFF\",\"mainColorDisabled\":\"rgba(0,0,0,0.12)\",\"backgroundColorDisabled\":\"#FFFFFF\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"pressed\":null,\"activated\":null,\"disabled\":null}},\"rightButton\":{\"showButton\":true,\"icon\":\"arrow_forward_ios\",\"iconSize\":24,\"iconSizeUnit\":\"px\",\"mainColorOn\":\"#3F52DD\",\"backgroundColorOn\":\"#FFFFFF\",\"mainColorDisabled\":\"rgba(0,0,0,0.12)\",\"backgroundColorDisabled\":\"#FFFFFF\",\"customStyle\":{\"enabled\":null,\"hovered\":null,\"pressed\":null,\"activated\":null,\"disabled\":null}}},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"12px\"},\"title\":\"Value stepper\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"actions\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":null,\"weight\":\"500\",\"style\":null,\"lineHeight\":\"24px\"},\"showTitleIcon\":false,\"titleTooltip\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"titleIcon\":\"\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"14px\",\"configMode\":\"basic\",\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"datasources\":null,\"borderRadius\":null}"
},
"resources": [
{
"link": "/api/images/system/value-stepper-widget.svg",
"title": "\"Value stepper\" system widget image",
"type": "IMAGE",
"subType": "IMAGE",
"fileName": "value-stepper-widget.svg",
"publicResourceKey": "s0UKoqbiMCcKVn0pD55XZzPUR89XlXAO",
"mediaType": "image/svg+xml",
"data": "PHN2ZyB3aWR0aD0iMjE0IiBoZWlnaHQ9Ijc2IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGZpbHRlcj0idXJsKCNhKSI+PHJlY3QgeD0iOC41IiB5PSI0LjUiIHdpZHRoPSIxOTciIGhlaWdodD0iNTkiIHJ4PSI0IiBmaWxsPSIjZmZmIiBzaGFwZS1yZW5kZXJpbmc9ImNyaXNwRWRnZXMiLz48cmVjdCB4PSI5IiB5PSI1IiB3aWR0aD0iMTk2IiBoZWlnaHQ9IjU4IiByeD0iMy41IiBzdHJva2U9IiMzMDU2ODAiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyIvPjxyZWN0IHg9IjIwLjUiIHk9IjE4IiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSIxNiIgZmlsbD0iIzMwNTY4MCIvPjxwYXRoIGQ9Im0zOC41IDQwIDEuNDEtMS40MUwzNS4zMyAzNGw0LjU4LTQuNTlMMzguNSAyOGwtNiA2IDYgNloiIGZpbGw9IiNmZmYiLz48cmVjdCB4PSI2NSIgeT0iMTguNSIgd2lkdGg9Ijg0IiBoZWlnaHQ9IjMxIiByeD0iMy41IiBmaWxsPSIjMzA1NjgwIiBmaWxsLW9wYWNpdHk9Ii4wNCIvPjxyZWN0IHg9IjY1IiB5PSIxOC41IiB3aWR0aD0iODQiIGhlaWdodD0iMzEiIHJ4PSIzLjUiIHN0cm9rZT0iIzMwNTY4MCIvPjxwYXRoIGQ9Ik04OC41OSAzNy41VjM5aC03LjYzdi0xLjI5bDMuNy00LjA0Yy40MS0uNDYuNzMtLjg1Ljk3LTEuMTkuMjMtLjMzLjQtLjYzLjQ5LS45YTIuMyAyLjMgMCAwIDAtLjA2LTEuNzNjLS4xMy0uMjctLjMyLS41LS41OC0uNjVhMS43IDEuNyAwIDAgMC0uOTMtLjI0Yy0uNDIgMC0uNzcuMS0xLjA2LjI3LS4yOC4xOS0uNS40NC0uNjUuNzZhMi42IDIuNiAwIDAgMC0uMjIgMS4xaC0xLjg4YzAtLjY3LjE1LTEuMjcuNDYtMS44Mi4zLS41NS43My0uOTkgMS4zLTEuM2E0LjEgNC4xIDAgMCAxIDIuMDgtLjVjLjc2IDAgMS40LjEzIDEuOTQuMzguNTMuMjYuOTMuNjIgMS4yIDEuMDlhMy4zNiAzLjM2IDAgMCAxIC4yNSAyLjcyIDUgNSAwIDAgMS0uNDkgMS4wNCA5IDkgMCAwIDEtLjc0IDEuMDRjLS4yOC4zNS0uNi43LS45NCAxLjA1bC0yLjQ2IDIuNzFoNS4yNVptOS4yNy05Ljg4djEuMDRMOTMuMyAzOWgtMS45OGw0LjU0LTkuODhoLTUuOXYtMS41aDcuODlabTEuOTggMTAuNDRjMC0uMjkuMS0uNTMuMy0uNzMuMi0uMi40Ni0uMy44LS4zcy42LjEuOC4zYy4yLjIuMy40NC4zLjczYTEgMSAwIDAgMS0uMy43NGMtLjIuMi0uNDYuMy0uOC4zcy0uNi0uMS0uOC0uM2ExIDEgMCAwIDEtLjMtLjc0Wm02LjQ5LTQuMzUtMS41LS4zNy42MS01LjcyaDYuMTR2MS42SDEwN2wtLjMyIDIuNzlhMy42NyAzLjY3IDAgMCAxIDEuODEtLjQ2Yy41NCAwIDEuMDIuMDkgMS40NC4yNi40Mi4xNy43OS40MyAxLjA4Ljc2LjMuMzMuNTMuNzMuNjggMS4yYTUgNSAwIDAgMSAwIDMuMDcgMy4xNyAzLjE3IDAgMCAxLTEuODUgMi4wM2MtLjQ2LjE5LTEuMDEuMjktMS42NS4yOWE0LjYgNC42IDAgMCAxLTEuMzYtLjIgMy43MyAzLjczIDAgMCAxLTEuMTctLjYyIDMuMTQgMy4xNCAwIDAgMS0xLjE5LTIuNDJoMS44NWMuMDUuMzcuMTUuNjkuMy45NS4xNi4yNS4zOC40NS42NC41OGEyIDIgMCAwIDAgLjkyLjJjLjMyIDAgLjYtLjA1LjgzLS4xNi4yMy0uMTEuNDItLjI3LjU3LS40OC4xNS0uMjIuMjctLjQ3LjM0LS43NWEzLjYyIDMuNjIgMCAwIDAtLjAyLTEuODcgMS45OCAxLjk4IDAgMCAwLS4zOC0uNzJjLS4xNy0uMi0uMzgtLjM2LS42My0uNDdhMi4xMyAyLjEzIDAgMCAwLS44OC0uMTdjLS40NSAwLS44LjA3LTEuMDQuMi0uMjMuMTMtLjQ1LjI5LS42NS40OFptMTEuNzUtNC4xNmMwLS4zOC4xLS43Mi4yOC0xLjA0LjE5LS4zMi40NC0uNTcuNzUtLjc2YTEuOTYgMS45NiAwIDAgMSAyLjA1IDBjLjMxLjE5LjU2LjQ0Ljc0Ljc2LjE5LjMyLjI4LjY2LjI4IDEuMDRzLS4xLjczLS4yOCAxLjA1Yy0uMTguMzEtLjQzLjU2LS43NC43NGEyLjA0IDIuMDQgMCAwIDEtMi44LS43NCAyLjAzIDIuMDMgMCAwIDEtLjI4LTEuMDVabTEuMDUgMGExIDEgMCAwIDAgMSAxIC45Ny45NyAwIDAgMCAuOTgtMSAxIDEgMCAwIDAtLjI3LS43Mi45My45MyAwIDAgMC0uNy0uM2MtLjI4IDAtLjUxLjEtLjcxLjMtLjIuMi0uMy40My0uMy43MlptMTIuMTkgNS43NWgxLjk1YTQuNSA0LjUgMCAwIDEtLjYyIDEuOTkgMy43MiAzLjcyIDAgMCAxLTEuNSAxLjM3IDUgNSAwIDAgMS0yLjMzLjUgNC4xNSA0LjE1IDAgMCAxLTMuMzQtMS40NWMtLjQtLjQ4LS43MS0xLjA0LS45Mi0xLjctLjIxLS42Ni0uMzItMS40LS4zMi0yLjIydi0uOTVjMC0uODEuMS0xLjU1LjMyLTIuMjIuMjItLjY2LjUzLTEuMjIuOTQtMS42OS40LS40Ny45LS44NCAxLjQ2LTEuMDlhNC43OCA0Ljc4IDAgMCAxIDEuOTMtLjM3Yy45IDAgMS42Ny4xNyAyLjMuNS42Mi4zMyAxLjEuOCAxLjQ1IDEuMzguMzUuNTkuNTYgMS4yNi42NCAyLjAyaC0xLjk1Yy0uMDUtLjQ4LS4xNy0uOS0uMzUtMS4yNWExLjc3IDEuNzcgMCAwIDAtLjc2LS44IDIuNzMgMi43MyAwIDAgMC0xLjMzLS4yOGMtLjQ1IDAtLjg0LjA4LTEuMTcuMjVhMi4yIDIuMiAwIDAgMC0uODQuNzNjLS4yMi4zMy0uMzkuNzItLjUgMS4yLS4xMS40Ny0uMTcgMS0uMTcgMS42di45N2MwIC41Ny4wNSAxLjEuMTUgMS41Ni4xLjQ3LjI2Ljg2LjQ3IDEuMi4yMS4zMy40OC41OS44MS43Ny4zMy4xOC43Mi4yNyAxLjE4LjI3LjU2IDAgMS0uMDggMS4zNS0uMjYuMzUtLjE4LjYxLS40NC44LS43OC4xNy0uMzQuMy0uNzYuMzUtMS4yNVoiIGZpbGw9IiMwMDAiIGZpbGwtb3BhY2l0eT0iLjg3Ii8+PHJlY3QgeD0iMTYxLjUiIHk9IjE4IiB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHJ4PSIxNiIgZmlsbD0iIzMwNTY4MCIvPjxwYXRoIGQ9Im0xNzUuNSAyOC0xLjQxIDEuNDEgNC41OCA0LjU5LTQuNTggNC41OUwxNzUuNSA0MGw2LTYtNi02WiIgZmlsbD0iI2ZmZiIvPjwvZz48ZGVmcz48ZmlsdGVyIGlkPSJhIiB4PSIuNSIgeT0iLjUiIHdpZHRoPSIyMTMiIGhlaWdodD0iNzUiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj48ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz48ZmVPZmZzZXQgZHk9IjQiLz48ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI0Ii8+PGZlQ29tcG9zaXRlIGluMj0iaGFyZEFscGhhIiBvcGVyYXRvcj0ib3V0Ii8+PGZlQ29sb3JNYXRyaXggdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA0IDAiLz48ZmVCbGVuZCBpbjI9IkJhY2tncm91bmRJbWFnZUZpeCIgcmVzdWx0PSJlZmZlY3QxX2Ryb3BTaGFkb3dfNTY2OV8xNjA3MDUiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfNTY2OV8xNjA3MDUiIHJlc3VsdD0ic2hhcGUiLz48L2ZpbHRlcj48L2RlZnM+PC9zdmc+",
"public": true
}
],
"scada": false,
"tags": [
"command",
"downlink",
"device configuration",
"device control",
"invocation",
"remote method",
"remote function",
"interface",
"subroutine call",
"inter-process communication",
"server request",
"update attribute",
"set attribute",
"add time-series"
]
}

9
application/src/main/data/json/tenant/device_profile/rule_chain_template.json

@ -19,8 +19,13 @@
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"configurationVersion": 1,
"configuration": {
"defaultTTL": 0
"defaultTTL": 0,
"useServerTs": false,
"processingSettings": {
"type": "ON_EVERY_MESSAGE"
}
}
},
{
@ -134,4 +139,4 @@
],
"ruleChainConnections": null
}
}
}

7
application/src/main/data/json/tenant/rule_chains/root_rule_chain.json

@ -18,8 +18,13 @@
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"configurationVersion": 1,
"configuration": {
"defaultTTL": 0
"defaultTTL": 0,
"useServerTs": false,
"processingSettings": {
"type": "ON_EVERY_MESSAGE"
}
}
},
{

231
application/src/main/data/upgrade/basic/schema_update.sql

@ -1,5 +1,5 @@
--
-- Copyright © 2016-2024 The Thingsboard Authors
-- Copyright © 2016-2025 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.
@ -14,198 +14,53 @@
-- limitations under the License.
--
ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS last_login_ts BIGINT;
UPDATE user_credentials c SET last_login_ts = (SELECT (additional_info::json ->> 'lastLoginTs')::bigint FROM tb_user u WHERE u.id = c.user_id)
WHERE last_login_ts IS NULL;
-- UPDATE SAVE TIME SERIES NODES START
ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS failed_login_attempts INT;
UPDATE user_credentials c SET failed_login_attempts = (SELECT (additional_info::json ->> 'failedLoginAttempts')::int FROM tb_user u WHERE u.id = c.user_id)
WHERE failed_login_attempts IS NULL;
UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts' - 'userCredentialsEnabled')::text
WHERE additional_info IS NOT NULL AND additional_info != 'null' AND jsonb_typeof(additional_info::jsonb) = 'object';
-- 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
$$
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
CREATE TABLE IF NOT EXISTS mobile_app_bundle (
id uuid NOT NULL CONSTRAINT mobile_app_bundle_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid,
title varchar(255),
description varchar(1024),
android_app_id uuid UNIQUE,
ios_app_id uuid UNIQUE,
layout_config varchar(16384),
oauth2_enabled boolean,
CONSTRAINT fk_android_app_id FOREIGN KEY (android_app_id) REFERENCES mobile_app(id) ON DELETE SET NULL,
CONSTRAINT fk_ios_app_id FOREIGN KEY (ios_app_id) REFERENCES mobile_app(id) ON DELETE SET NULL
);
CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id);
-- Check if the rule_node table exists
IF EXISTS (
SELECT 1
FROM information_schema.tables
WHERE table_name = 'rule_node'
) THEN
UPDATE rule_node
SET configuration = (
(configuration::jsonb - 'skipLatestPersistence')
|| jsonb_build_object(
'processingSettings', jsonb_build_object(
'type', 'ADVANCED',
'timeseries', jsonb_build_object('type', 'ON_EVERY_MESSAGE'),
'latest', jsonb_build_object('type', 'SKIP'),
'webSockets', jsonb_build_object('type', 'ON_EVERY_MESSAGE'),
'calculatedFields', jsonb_build_object('type', 'ON_EVERY_MESSAGE')
)
)
)::text,
configuration_version = 1
WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode'
AND configuration_version = 0
AND configuration::jsonb ->> 'skipLatestPersistence' = 'true';
UPDATE rule_node
SET configuration = (
(configuration::jsonb - 'skipLatestPersistence')
|| jsonb_build_object(
'processingSettings', jsonb_build_object(
'type', 'ON_EVERY_MESSAGE'
)
)
)::text,
configuration_version = 1
WHERE type = 'org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode'
AND configuration_version = 0
AND (configuration::jsonb ->> 'skipLatestPersistence' != 'true' OR configuration::jsonb ->> 'skipLatestPersistence' IS NULL);
ALTER TABLE mobile_app ADD COLUMN IF NOT EXISTS platform_type varchar(32),
ADD COLUMN IF NOT EXISTS status varchar(32),
ADD COLUMN IF NOT EXISTS version_info varchar(100000),
ADD COLUMN IF NOT EXISTS store_info varchar(16384),
DROP CONSTRAINT IF EXISTS mobile_app_pkg_name_key,
DROP CONSTRAINT IF EXISTS mobile_app_unq_key;
-- rename mobile_app_oauth2_client to mobile_app_bundle_oauth2_client
DO
$$
BEGIN
-- in case of running the upgrade script a second time
IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'mobile_app_oauth2_client') THEN
ALTER TABLE mobile_app_oauth2_client RENAME TO mobile_app_bundle_oauth2_client;
ALTER TABLE mobile_app_bundle_oauth2_client DROP CONSTRAINT IF EXISTS fk_domain;
ALTER TABLE mobile_app_bundle_oauth2_client RENAME COLUMN mobile_app_id TO mobile_app_bundle_id;
END IF;
END;
$$;
-- UPDATE SAVE TIME SERIES NODES END
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- duplicate each mobile app and create mobile app bundle for the pair of android and ios app
DO
$$
DECLARE
generatedBundleId uuid;
iosAppId uuid;
mobileAppRecord RECORD;
BEGIN
-- in case of running the upgrade script a second time
IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'mobile_app' and column_name = 'oauth2_enabled') THEN
UPDATE mobile_app SET platform_type = 'ANDROID' WHERE platform_type IS NULL;
UPDATE mobile_app SET status = 'DRAFT' WHERE mobile_app.status IS NULL;
FOR mobileAppRecord IN SELECT * FROM mobile_app
LOOP
-- duplicate app for iOS platform type
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, app_secret, platform_type, status)
VALUES (iosAppId, mobileAppRecord.created_time, mobileAppRecord.tenant_id, mobileAppRecord.pkg_name, mobileAppRecord.app_secret, 'IOS', mobileAppRecord.status)
ON CONFLICT DO NOTHING;
-- create bundle for android and iOS app
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id, ios_app_id, oauth2_enabled)
VALUES (generatedBundleId, mobileAppRecord.created_time, mobileAppRecord.tenant_id,
mobileAppRecord.pkg_name || ' (autogenerated)', mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled)
ON CONFLICT DO NOTHING;
UPDATE mobile_app_bundle_oauth2_client SET mobile_app_bundle_id = generatedBundleId WHERE mobile_app_bundle_id = mobileAppRecord.id;
END LOOP;
END IF;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_mobile_app_bundle_oauth2_client_bundle_id') THEN
ALTER TABLE mobile_app_bundle_oauth2_client ADD CONSTRAINT fk_mobile_app_bundle_oauth2_client_bundle_id
FOREIGN KEY (mobile_app_bundle_id) REFERENCES mobile_app_bundle(id) ON DELETE CASCADE;
END IF;
ALTER TABLE mobile_app DROP COLUMN IF EXISTS oauth2_enabled;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'mobile_app_pkg_name_platform_unq_key') THEN
ALTER TABLE mobile_app ADD CONSTRAINT mobile_app_pkg_name_platform_unq_key UNIQUE (pkg_name, platform_type);
END IF;
END;
$$;
ALTER TABLE IF EXISTS mobile_app_settings RENAME TO qr_code_settings;
ALTER TABLE qr_code_settings ADD COLUMN IF NOT EXISTS mobile_app_bundle_id uuid,
ADD COLUMN IF NOT EXISTS android_enabled boolean,
ADD COLUMN IF NOT EXISTS ios_enabled boolean;
-- migrate mobile apps from qr code settings to mobile_app, create mobile app bundle for the pair of apps
DO
$$
DECLARE
androidPkgName varchar;
iosPkgName varchar;
androidAppId uuid;
iosAppId uuid;
generatedBundleId uuid;
qrCodeRecord RECORD;
BEGIN
-- in case of running the upgrade script a second time
IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'qr_code_settings' AND column_name = 'android_config') THEN
FOR qrCodeRecord IN SELECT * FROM qr_code_settings
LOOP
generatedBundleId := NULL;
-- migrate android config
IF (qrCodeRecord.android_config::jsonb ->> 'appPackage' IS NOT NULL) THEN
androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage';
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID';
IF androidAppId IS NULL THEN
androidAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
androidPkgName, 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled');
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId;
ELSE
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId);
END IF;
END IF;
UPDATE qr_code_settings SET android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
-- migrate ios config
IF (qrCodeRecord.ios_config::jsonb ->> 'appId' IS NOT NULL) THEN
iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1);
SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS';
IF iosAppId IS NULL THEN
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (iosAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
iosPkgName, 'IOS', 'DRAFT', qrCodeRecord.ios_config::jsonb - 'enabled');
IF generatedBundleId IS NULL THEN
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId;
ELSE
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
END IF;
ELSE
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId);
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId;
END IF;
END IF;
UPDATE qr_code_settings SET ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id;
END LOOP;
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key;
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_pkey TO qr_code_settings_pkey;
END IF;
ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config;
END;
$$;
-- update constraint name
DO
$$
BEGIN
ALTER TABLE domain DROP CONSTRAINT IF EXISTS domain_unq_key;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'domain_name_key') THEN
ALTER TABLE domain ADD CONSTRAINT domain_name_key UNIQUE (name);
END IF;
END;
$$;
-- UPDATE RESOURCE JS_MODULE SUB TYPE START
UPDATE resource SET resource_sub_type = 'EXTENSION' WHERE resource_type = 'JS_MODULE' AND resource_sub_type IS NULL;
-- UPDATE RESOURCE JS_MODULE SUB TYPE END
ALTER TABLE api_usage_state ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;

2
application/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/ThingsboardServerApplication.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

121
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.
@ -41,13 +41,18 @@ import org.thingsboard.script.api.js.JsInvokeService;
import org.thingsboard.script.api.tbel.TbelInvokeService;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.cache.limits.RateLimitService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.event.CalculatedFieldDebugEvent;
import org.thingsboard.server.common.data.event.ErrorEvent;
import org.thingsboard.server.common.data.event.LifecycleEvent;
import org.thingsboard.server.common.data.event.RuleChainDebugEvent;
import org.thingsboard.server.common.data.event.RuleNodeDebugEvent;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.limit.LimitedApi;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg;
@ -62,6 +67,7 @@ import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.cf.CalculatedFieldService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.ClaimDevicesService;
@ -94,6 +100,7 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.usagerecord.ApiLimitService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
@ -101,6 +108,10 @@ import org.thingsboard.server.queue.discovery.DiscoveryService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.cf.CalculatedFieldProcessingService;
import org.thingsboard.server.service.cf.CalculatedFieldStateService;
import org.thingsboard.server.service.cf.cache.CalculatedFieldEntityProfileCache;
import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService;
@ -121,13 +132,17 @@ import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.transport.TbCoreToTransportService;
import org.thingsboard.server.utils.DebugModeRateLimitsConfig;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Component
@ -156,6 +171,18 @@ public class ActorSystemContext {
}
};
private static final FutureCallback<Void> CALCULATED_FIELD_DEBUG_EVENT_ERROR_CALLBACK = new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void event) {
}
@Override
public void onFailure(Throwable th) {
log.error("Could not save debug Event for Calculated Field", th);
}
};
private final ConcurrentMap<TenantId, DebugTbRateLimits> debugPerTenantLimits = new ConcurrentHashMap<>();
public ConcurrentMap<TenantId, DebugTbRateLimits> getDebugPerTenantLimits() {
@ -289,6 +316,7 @@ public class ActorSystemContext {
@Getter
private TbEntityViewService tbEntityViewService;
@Lazy
@Autowired
@Getter
private TelemetrySubscriptionService tsSubService;
@ -394,6 +422,10 @@ public class ActorSystemContext {
@Getter
private SlackService slackService;
@Autowired
@Getter
private CalculatedFieldService calculatedFieldService;
@Lazy
@Autowired(required = false)
@Getter
@ -416,6 +448,21 @@ public class ActorSystemContext {
@Getter
private TbCoreToTransportService tbCoreToTransportService;
@Lazy
@Autowired(required = false)
@Getter
private ApiLimitService apiLimitService;
@Lazy
@Autowired(required = false)
@Getter
private RateLimitService rateLimitService;
@Lazy
@Autowired(required = false)
@Getter
private DebugModeRateLimitsConfig debugModeRateLimitsConfig;
/**
* The following Service will be null if we operate in tb-core mode
*/
@ -487,6 +534,21 @@ public class ActorSystemContext {
@Getter
private EntityService entityService;
@Lazy
@Autowired(required = false)
@Getter
private CalculatedFieldProcessingService calculatedFieldProcessingService;
@Lazy
@Autowired(required = false)
@Getter
private CalculatedFieldStateService calculatedFieldStateService;
@Lazy
@Autowired(required = false)
@Getter
private CalculatedFieldEntityProfileCache calculatedFieldEntityProfileCache;
@Value("${actors.session.max_concurrent_sessions_per_device:1}")
@Getter
private long maxConcurrentSessionsPerDevice;
@ -558,14 +620,6 @@ public class ActorSystemContext {
@Getter
private long sessionReportTimeout;
@Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled:true}")
@Getter
private boolean debugPerTenantEnabled;
@Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.configuration:50000:3600}")
@Getter
private String debugPerTenantLimitsConfiguration;
@Value("${actors.rpc.submit_strategy:BURST}")
@Getter
private String rpcSubmitStrategy;
@ -719,9 +773,9 @@ public class ActorSystemContext {
}
private boolean checkLimits(TenantId tenantId, TbMsg tbMsg, Throwable error) {
if (debugPerTenantEnabled) {
if (debugModeRateLimitsConfig.isRuleChainDebugPerTenantLimitsEnabled()) {
DebugTbRateLimits debugTbRateLimits = debugPerTenantLimits.computeIfAbsent(tenantId, id ->
new DebugTbRateLimits(new TbRateLimits(debugPerTenantLimitsConfiguration), false));
new DebugTbRateLimits(new TbRateLimits(debugModeRateLimitsConfig.getRuleChainDebugPerTenantLimitsConfiguration()), false));
if (!debugTbRateLimits.getTbRateLimits().tryConsume()) {
if (!debugTbRateLimits.isRuleChainEventSaved()) {
@ -751,6 +805,51 @@ public class ActorSystemContext {
Futures.addCallback(future, RULE_CHAIN_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
}
public void persistCalculatedFieldDebugEvent(TenantId tenantId, CalculatedFieldId calculatedFieldId, EntityId entityId, Map<String, ArgumentEntry> arguments, UUID tbMsgId, TbMsgType tbMsgType, String result, String errorMessage) {
if (checkLimits(tenantId)) {
try {
CalculatedFieldDebugEvent.CalculatedFieldDebugEventBuilder eventBuilder = CalculatedFieldDebugEvent.builder()
.tenantId(tenantId)
.entityId(calculatedFieldId.getId())
.serviceId(getServiceId())
.calculatedFieldId(calculatedFieldId)
.eventEntity(entityId);
if (tbMsgId != null) {
eventBuilder.msgId(tbMsgId);
}
if (tbMsgType != null) {
eventBuilder.msgType(tbMsgType.name());
}
if (arguments != null) {
eventBuilder.arguments(JacksonUtil.toString(
arguments.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toTbelCfArg()))
));
}
if (result != null) {
eventBuilder.result(result);
}
if (errorMessage != null) {
eventBuilder.error(errorMessage);
}
ListenableFuture<Void> future = eventService.saveAsync(eventBuilder.build());
Futures.addCallback(future, CALCULATED_FIELD_DEBUG_EVENT_ERROR_CALLBACK, MoreExecutors.directExecutor());
} catch (IllegalArgumentException ex) {
log.warn("Failed to persist calculated field debug message", ex);
}
}
}
private boolean checkLimits(TenantId tenantId) {
if (debugModeRateLimitsConfig.isCalculatedFieldDebugPerTenantLimitsEnabled() &&
!rateLimitService.checkRateLimit(LimitedApi.CALCULATED_FIELD_DEBUG_EVENTS, (Object) tenantId, debugModeRateLimitsConfig.getCalculatedFieldDebugPerTenantLimitsConfiguration())) {
log.trace("[{}] Calculated field debug event limits exceeded!", tenantId);
return false;
}
return true;
}
public static Exception toException(Throwable error) {
return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
}

2
application/src/main/java/org/thingsboard/server/actors/TbEntityTypeActorIdPredicate.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

29
application/src/main/java/org/thingsboard/server/actors/app/AppActor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.
@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
@ -87,6 +88,7 @@ public class AppActor extends ContextAwareActor {
case APP_INIT_MSG:
break;
case PARTITION_CHANGE_MSG:
case CF_PARTITIONS_CHANGE_MSG:
ctx.broadcastToChildren(msg, true);
break;
case COMPONENT_LIFE_CYCLE_MSG:
@ -111,6 +113,18 @@ public class AppActor extends ContextAwareActor {
case SESSION_TIMEOUT_MSG:
ctx.broadcastToChildrenByType(msg, EntityType.TENANT);
break;
case CF_INIT_MSG:
case CF_LINK_INIT_MSG:
case CF_STATE_RESTORE_MSG:
case CF_ENTITY_LIFECYCLE_MSG:
//TODO: use priority from the message body. For example, messages about CF lifecycle are important and Device lifecycle are not.
// same for the Linked telemetry.
onToCalculatedFieldSystemActorMsg((ToCalculatedFieldSystemMsg) msg, true);
break;
case CF_TELEMETRY_MSG:
case CF_LINKED_TELEMETRY_MSG:
onToCalculatedFieldSystemActorMsg((ToCalculatedFieldSystemMsg) msg, false);
break;
default:
return false;
}
@ -175,6 +189,19 @@ public class AppActor extends ContextAwareActor {
}
}
private void onToCalculatedFieldSystemActorMsg(ToCalculatedFieldSystemMsg msg, boolean priority) {
getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(tenantActor -> {
if (priority) {
tenantActor.tellWithHighPriority(msg);
} else {
tenantActor.tell(msg);
}
}, () -> {
msg.getCallback().onSuccess();
});
}
private void onToDeviceActorMsg(TenantAwareMsg msg, boolean priority) {
getOrCreateTenantActor(msg.getTenantId()).ifPresentOrElse(tenantActor -> {
if (priority) {

2
application/src/main/java/org/thingsboard/server/actors/app/AppInitMsg.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

70
application/src/main/java/org/thingsboard/server/actors/calculatedField/AbstractCalculatedFieldActor.java

@ -0,0 +1,70 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.DebugModeUtil;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
@Slf4j
public abstract class AbstractCalculatedFieldActor extends ContextAwareActor {
protected final TenantId tenantId;
public AbstractCalculatedFieldActor(ActorSystemContext systemContext, TenantId tenantId) {
super(systemContext);
this.tenantId = tenantId;
}
@Override
protected boolean doProcess(TbActorMsg msg) {
if (msg instanceof ToCalculatedFieldSystemMsg cfm) {
Exception cause;
try {
return doProcessCfMsg(cfm);
} catch (CalculatedFieldException cfe) {
if (DebugModeUtil.isDebugFailuresAvailable(cfe.getCtx().getCalculatedField())) {
String message;
if (cfe.getErrorMessage() != null) {
message = cfe.getErrorMessage();
} else if (cfe.getCause() != null) {
message = cfe.getCause().getMessage();
} else {
message = "N/A";
}
systemContext.persistCalculatedFieldDebugEvent(tenantId, cfe.getCtx().getCfId(), cfe.getEventEntity(), cfe.getArguments(), cfe.getMsgId(), cfe.getMsgType(), null, message);
}
cause = cfe.getCause();
} catch (Exception e) {
logProcessingException(e);
cause = e;
}
cfm.getCallback().onFailure(cause);
return true;
} else {
return false;
}
}
abstract void logProcessingException(Exception e);
abstract boolean doProcessCfMsg(ToCalculatedFieldSystemMsg msg) throws CalculatedFieldException;
}

81
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActor.java

@ -0,0 +1,81 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg;
@Slf4j
public class CalculatedFieldEntityActor extends AbstractCalculatedFieldActor {
private final CalculatedFieldEntityMessageProcessor processor;
CalculatedFieldEntityActor(ActorSystemContext systemContext, TenantId tenantId, EntityId entityId) {
super(systemContext, tenantId);
this.processor = new CalculatedFieldEntityMessageProcessor(systemContext, tenantId, entityId);
}
@Override
public void init(TbActorCtx ctx) throws TbActorException {
super.init(ctx);
log.debug("[{}][{}] Starting CF entity actor.", processor.tenantId, processor.entityId);
try {
processor.init(ctx);
log.debug("[{}][{}] CF entity actor started.", processor.tenantId, processor.entityId);
} catch (Exception e) {
log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.entityId, e);
throw new TbActorException("Failed to initialize CF entity actor", e);
}
}
@Override
protected boolean doProcessCfMsg(ToCalculatedFieldSystemMsg msg) throws CalculatedFieldException {
switch (msg.getMsgType()) {
case CF_PARTITIONS_CHANGE_MSG:
processor.process((CalculatedFieldPartitionChangeMsg) msg);
break;
case CF_STATE_RESTORE_MSG:
processor.process((CalculatedFieldStateRestoreMsg) msg);
break;
case CF_ENTITY_INIT_CF_MSG:
processor.process((EntityInitCalculatedFieldMsg) msg);
break;
case CF_ENTITY_DELETE_MSG:
processor.process((CalculatedFieldEntityDeleteMsg) msg);
break;
case CF_ENTITY_TELEMETRY_MSG:
processor.process((EntityCalculatedFieldTelemetryMsg) msg);
break;
case CF_LINKED_TELEMETRY_MSG:
processor.process((EntityCalculatedFieldLinkedTelemetryMsg) msg);
break;
default:
return false;
}
return true;
}
@Override
void logProcessingException(Exception e) {
log.warn("[{}][{}] Processing failure", tenantId, processor.entityId, e);
}
}

50
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityActorCreator.java

@ -0,0 +1,50 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActor;
import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbCalculatedFieldEntityActorId;
import org.thingsboard.server.actors.TbEntityActorId;
import org.thingsboard.server.actors.device.DeviceActor;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
public class CalculatedFieldEntityActorCreator extends ContextBasedCreator {
private final TenantId tenantId;
private final EntityId entityId;
public CalculatedFieldEntityActorCreator(ActorSystemContext context, TenantId tenantId, EntityId entityId) {
super(context);
this.tenantId = tenantId;
this.entityId = entityId;
}
@Override
public TbActorId createActorId() {
return new TbCalculatedFieldEntityActorId(entityId);
}
@Override
public TbActor createActor() {
return new CalculatedFieldEntityActor(context, tenantId, entityId);
}
}

44
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityDeleteMsg.java

@ -0,0 +1,44 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
@Data
public class CalculatedFieldEntityDeleteMsg implements ToCalculatedFieldSystemMsg {
private final TenantId tenantId;
private final EntityId entityId;
private final TbCallback callback;
public CalculatedFieldEntityDeleteMsg(TenantId tenantId,
EntityId entityId,
TbCallback callback) {
this.tenantId = tenantId;
this.entityId = entityId;
this.callback = callback;
}
@Override
public MsgType getMsgType() {
return MsgType.CF_ENTITY_DELETE_MSG;
}
}

439
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldEntityMessageProcessor.java

@ -0,0 +1,439 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.DebugModeUtil;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeScopeProto;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeValueProto;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.cf.CalculatedFieldProcessingService;
import org.thingsboard.server.service.cf.CalculatedFieldResult;
import org.thingsboard.server.service.cf.CalculatedFieldStateService;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId;
import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState;
import org.thingsboard.server.service.cf.ctx.state.SingleValueArgumentEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @author Andrew Shvayka
*/
@Slf4j
public class CalculatedFieldEntityMessageProcessor extends AbstractContextAwareMsgProcessor {
// (1 for result persistence + 1 for the state persistence )
public static final int CALLBACKS_PER_CF = 2;
final TenantId tenantId;
final EntityId entityId;
final CalculatedFieldProcessingService cfService;
final CalculatedFieldStateService cfStateService;
final int partition;
TbActorCtx ctx;
Map<CalculatedFieldId, CalculatedFieldState> states = new HashMap<>();
CalculatedFieldEntityMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, EntityId entityId) {
super(systemContext);
this.tenantId = tenantId;
this.entityId = entityId;
this.cfService = systemContext.getCalculatedFieldProcessingService();
this.cfStateService = systemContext.getCalculatedFieldStateService();
this.partition = systemContext.getCalculatedFieldEntityProfileCache().getEntityIdPartition(tenantId, entityId);
}
void init(TbActorCtx ctx) {
this.ctx = ctx;
}
public void process(CalculatedFieldPartitionChangeMsg msg) {
if (!msg.getPartitions()[partition]) {
log.info("[{}][{}] Stopping entity actor due to change partition event.", partition, entityId);
ctx.stop(ctx.getSelf());
}
}
public void process(CalculatedFieldStateRestoreMsg msg) {
CalculatedFieldId cfId = msg.getId().cfId();
log.info("[{}] [{}] Processing CF state restore msg.", msg.getId().entityId(), cfId);
if (msg.getState() != null) {
states.put(cfId, msg.getState());
} else {
states.remove(cfId);
}
}
public void process(EntityInitCalculatedFieldMsg msg) throws CalculatedFieldException {
log.info("[{}] Processing entity init CF msg.", msg.getCtx().getCfId());
var ctx = msg.getCtx();
if (msg.isForceReinit()) {
log.info("Force reinitialization of CF: [{}].", ctx.getCfId());
states.remove(ctx.getCfId());
}
try {
var state = getOrInitState(ctx);
if (state.isSizeOk()) {
processStateIfReady(ctx, Collections.singletonList(ctx.getCfId()), state, null, null, msg.getCallback());
} else {
throw new RuntimeException(ctx.getSizeExceedsLimitMessage());
}
} catch (Exception e) {
if (e instanceof CalculatedFieldException cfe) {
throw cfe;
}
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build();
}
}
public void process(CalculatedFieldEntityDeleteMsg msg) {
log.info("[{}] Processing CF entity delete msg.", msg.getEntityId());
if (this.entityId.equals(msg.getEntityId())) {
MultipleTbCallback multipleTbCallback = new MultipleTbCallback(states.size(), msg.getCallback());
states.forEach((cfId, state) -> cfStateService.removeState(new CalculatedFieldEntityCtxId(tenantId, cfId, entityId), multipleTbCallback));
ctx.stop(ctx.getSelf());
} else {
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
var state = states.remove(cfId);
if (state != null) {
cfStateService.removeState(new CalculatedFieldEntityCtxId(tenantId, cfId, entityId), msg.getCallback());
}
}
}
public void process(EntityCalculatedFieldTelemetryMsg msg) throws CalculatedFieldException {
log.info("[{}] Processing CF telemetry msg.", msg.getEntityId());
var proto = msg.getProto();
var numberOfCallbacks = CALLBACKS_PER_CF * (msg.getEntityIdFields().size() + msg.getProfileIdFields().size());
MultipleTbCallback callback = new MultipleTbCallback(numberOfCallbacks, msg.getCallback());
List<CalculatedFieldId> cfIdList = getCalculatedFieldIds(proto);
Set<CalculatedFieldId> cfIdSet = new HashSet<>(cfIdList);
for (var ctx : msg.getEntityIdFields()) {
process(ctx, proto, cfIdSet, cfIdList, callback);
}
for (var ctx : msg.getProfileIdFields()) {
process(ctx, proto, cfIdSet, cfIdList, callback);
}
}
public void process(EntityCalculatedFieldLinkedTelemetryMsg msg) throws CalculatedFieldException {
log.info("[{}] Processing CF link telemetry msg.", msg.getEntityId());
var proto = msg.getProto();
var ctx = msg.getCtx();
var callback = new MultipleTbCallback(CALLBACKS_PER_CF, msg.getCallback());
try {
List<CalculatedFieldId> cfIds = getCalculatedFieldIds(proto);
if (cfIds.contains(ctx.getCfId())) {
callback.onSuccess(CALLBACKS_PER_CF);
} else {
if (proto.getTsDataCount() > 0) {
processArgumentValuesUpdate(ctx, cfIds, callback, mapToArguments(ctx, msg.getEntityId(), proto.getTsDataList()), toTbMsgId(proto), toTbMsgType(proto));
} else if (proto.getAttrDataCount() > 0) {
processArgumentValuesUpdate(ctx, cfIds, callback, mapToArguments(ctx, msg.getEntityId(), proto.getScope(), proto.getAttrDataList()), toTbMsgId(proto), toTbMsgType(proto));
} else if (proto.getRemovedTsKeysCount() > 0) {
processArgumentValuesUpdate(ctx, cfIds, callback, mapToArgumentsWithFetchedValue(ctx, proto.getRemovedTsKeysList()), toTbMsgId(proto), toTbMsgType(proto));
} else if (proto.getRemovedAttrKeysCount() > 0) {
processArgumentValuesUpdate(ctx, cfIds, callback, mapToArgumentsWithDefaultValue(ctx, msg.getEntityId(), proto.getScope(), proto.getRemovedAttrKeysList()), toTbMsgId(proto), toTbMsgType(proto));
} else {
callback.onSuccess(CALLBACKS_PER_CF);
}
}
} catch (Exception e) {
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build();
}
}
private void process(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, Collection<CalculatedFieldId> cfIds, List<CalculatedFieldId> cfIdList, MultipleTbCallback callback) throws CalculatedFieldException {
try {
if (cfIds.contains(ctx.getCfId())) {
callback.onSuccess(CALLBACKS_PER_CF);
} else {
if (proto.getTsDataCount() > 0) {
processTelemetry(ctx, proto, cfIdList, callback);
} else if (proto.getAttrDataCount() > 0) {
processAttributes(ctx, proto, cfIdList, callback);
} else if (proto.getRemovedTsKeysCount() > 0) {
processRemovedTelemetry(ctx, proto, cfIdList, callback);
} else if (proto.getRemovedAttrKeysCount() > 0) {
processRemovedAttributes(ctx, proto, cfIdList, callback);
} else {
callback.onSuccess(CALLBACKS_PER_CF);
}
}
} catch (Exception e) {
if (e instanceof CalculatedFieldException cfe) {
throw cfe;
}
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).cause(e).build();
}
}
private void processTelemetry(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List<CalculatedFieldId> cfIdList, MultipleTbCallback callback) throws CalculatedFieldException {
processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArguments(ctx, proto.getTsDataList()), toTbMsgId(proto), toTbMsgType(proto));
}
private void processAttributes(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List<CalculatedFieldId> cfIdList, MultipleTbCallback callback) throws CalculatedFieldException {
processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArguments(ctx, proto.getScope(), proto.getAttrDataList()), toTbMsgId(proto), toTbMsgType(proto));
}
private void processRemovedTelemetry(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List<CalculatedFieldId> cfIdList, MultipleTbCallback callback) throws CalculatedFieldException {
processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArgumentsWithFetchedValue(ctx, proto.getRemovedTsKeysList()), toTbMsgId(proto), toTbMsgType(proto));
}
private void processRemovedAttributes(CalculatedFieldCtx ctx, CalculatedFieldTelemetryMsgProto proto, List<CalculatedFieldId> cfIdList, MultipleTbCallback callback) throws CalculatedFieldException {
processArgumentValuesUpdate(ctx, cfIdList, callback, mapToArgumentsWithDefaultValue(ctx, proto.getScope(), proto.getRemovedAttrKeysList()), toTbMsgId(proto), toTbMsgType(proto));
}
private void processArgumentValuesUpdate(CalculatedFieldCtx ctx, List<CalculatedFieldId> cfIdList, MultipleTbCallback callback,
Map<String, ArgumentEntry> newArgValues, UUID tbMsgId, TbMsgType tbMsgType) throws CalculatedFieldException {
if (newArgValues.isEmpty()) {
log.info("[{}] No new argument values to process for CF.", ctx.getCfId());
callback.onSuccess(CALLBACKS_PER_CF);
}
CalculatedFieldState state = states.get(ctx.getCfId());
boolean justRestored = false;
if (state == null) {
state = getOrInitState(ctx);
justRestored = true;
}
if (state.isSizeOk()) {
if (state.updateState(newArgValues) || justRestored) {
cfIdList = new ArrayList<>(cfIdList);
cfIdList.add(ctx.getCfId());
processStateIfReady(ctx, cfIdList, state, tbMsgId, tbMsgType, callback);
} else {
callback.onSuccess(CALLBACKS_PER_CF);
}
} else {
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build();
}
}
@SneakyThrows
private CalculatedFieldState getOrInitState(CalculatedFieldCtx ctx) {
CalculatedFieldState state = states.get(ctx.getCfId());
if (state != null) {
return state;
} else {
ListenableFuture<CalculatedFieldState> stateFuture = systemContext.getCalculatedFieldProcessingService().fetchStateFromDb(ctx, entityId);
// Ugly but necessary. We do not expect to often fetch data from DB. Only once per <Entity, CalculatedField> pair lifetime.
// This call happens while processing the CF pack from the queue consumer. So the timeout should be relatively low.
// Alternatively, we can fetch the state outside the actor system and push separate command to create this actor,
// but this will significantly complicate the code.
state = stateFuture.get(1, TimeUnit.MINUTES);
state.checkStateSize(new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId), ctx.getMaxStateSize());
states.put(ctx.getCfId(), state);
}
return state;
}
private void processStateIfReady(CalculatedFieldCtx ctx, List<CalculatedFieldId> cfIdList, CalculatedFieldState state, UUID tbMsgId, TbMsgType tbMsgType, TbCallback callback) throws CalculatedFieldException {
CalculatedFieldEntityCtxId ctxId = new CalculatedFieldEntityCtxId(tenantId, ctx.getCfId(), entityId);
boolean stateSizeOk;
if (ctx.isInitialized() && state.isReady()) {
try {
CalculatedFieldResult calculationResult = state.performCalculation(ctx).get(5, TimeUnit.SECONDS);
state.checkStateSize(ctxId, ctx.getMaxStateSize());
stateSizeOk = state.isSizeOk();
if (stateSizeOk) {
cfService.pushMsgToRuleEngine(tenantId, entityId, calculationResult, cfIdList, callback);
if (DebugModeUtil.isDebugAllAvailable(ctx.getCalculatedField())) {
systemContext.persistCalculatedFieldDebugEvent(tenantId, ctx.getCfId(), entityId, state.getArguments(), tbMsgId, tbMsgType, JacksonUtil.writeValueAsString(calculationResult.getResult()), null);
}
}
} catch (Exception e) {
throw CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).msgId(tbMsgId).msgType(tbMsgType).arguments(state.getArguments()).cause(e).build();
}
} else {
state.checkStateSize(ctxId, ctx.getMaxStateSize());
stateSizeOk = state.isSizeOk();
if (stateSizeOk) {
callback.onSuccess(); // State was updated but no calculation performed;
}
}
if (stateSizeOk) {
cfStateService.persistState(ctxId, state, callback);
} else {
removeStateAndRaiseSizeException(ctxId, CalculatedFieldException.builder().ctx(ctx).eventEntity(entityId).errorMessage(ctx.getSizeExceedsLimitMessage()).build(), callback);
}
}
private void removeStateAndRaiseSizeException(CalculatedFieldEntityCtxId ctxId, CalculatedFieldException ex, TbCallback callback) {
// We remove the state, but remember that it is over-sized in a local map.
cfStateService.removeState(ctxId, new TbCallback() {
@Override
public void onSuccess() {
callback.onFailure(ex);
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(ex);
}
});
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, List<TsKvProto> data) {
return mapToArguments(ctx.getMainEntityArguments(), data);
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, List<TsKvProto> data) {
var argNames = ctx.getLinkedEntityArguments().get(entityId);
if (argNames.isEmpty()) {
return Collections.emptyMap();
}
return mapToArguments(argNames, data);
}
private Map<String, ArgumentEntry> mapToArguments(Map<ReferencedEntityKey, String> argNames, List<TsKvProto> data) {
if (argNames.isEmpty()) {
return Collections.emptyMap();
}
Map<String, ArgumentEntry> arguments = new HashMap<>();
for (TsKvProto item : data) {
ReferencedEntityKey key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_LATEST, null);
String argName = argNames.get(key);
if (argName != null) {
arguments.put(argName, new SingleValueArgumentEntry(item));
}
key = new ReferencedEntityKey(item.getKv().getKey(), ArgumentType.TS_ROLLING, null);
argName = argNames.get(key);
if (argName != null) {
arguments.put(argName, new SingleValueArgumentEntry(item));
}
}
return arguments;
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
return mapToArguments(ctx.getMainEntityArguments(), scope, attrDataList);
}
private Map<String, ArgumentEntry> mapToArguments(CalculatedFieldCtx ctx, EntityId entityId, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
var argNames = ctx.getLinkedEntityArguments().get(entityId);
if (argNames.isEmpty()) {
return Collections.emptyMap();
}
return mapToArguments(argNames, scope, attrDataList);
}
private Map<String, ArgumentEntry> mapToArguments(Map<ReferencedEntityKey, String> argNames, AttributeScopeProto scope, List<AttributeValueProto> attrDataList) {
Map<String, ArgumentEntry> arguments = new HashMap<>();
for (AttributeValueProto item : attrDataList) {
ReferencedEntityKey key = new ReferencedEntityKey(item.getKey(), ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name()));
String argName = argNames.get(key);
if (argName != null) {
arguments.put(argName, new SingleValueArgumentEntry(item));
}
}
return arguments;
}
private Map<String, ArgumentEntry> mapToArgumentsWithDefaultValue(CalculatedFieldCtx ctx, EntityId entityId, AttributeScopeProto scope, List<String> removedAttrKeys) {
var argNames = ctx.getLinkedEntityArguments().get(entityId);
if (argNames.isEmpty()) {
return Collections.emptyMap();
}
return mapToArgumentsWithDefaultValue(argNames, ctx.getArguments(), scope, removedAttrKeys);
}
private Map<String, ArgumentEntry> mapToArgumentsWithDefaultValue(CalculatedFieldCtx ctx, AttributeScopeProto scope, List<String> removedAttrKeys) {
return mapToArgumentsWithDefaultValue(ctx.getMainEntityArguments(), ctx.getArguments(), scope, removedAttrKeys);
}
private Map<String, ArgumentEntry> mapToArgumentsWithDefaultValue(Map<ReferencedEntityKey, String> argNames, Map<String, Argument> configArguments, AttributeScopeProto scope, List<String> removedAttrKeys) {
Map<String, ArgumentEntry> arguments = new HashMap<>();
for (String removedKey : removedAttrKeys) {
ReferencedEntityKey key = new ReferencedEntityKey(removedKey, ArgumentType.ATTRIBUTE, AttributeScope.valueOf(scope.name()));
String argName = argNames.get(key);
if (argName != null) {
Argument argument = configArguments.get(argName);
String defaultValue = (argument != null) ? argument.getDefaultValue() : null;
arguments.put(argName, StringUtils.isNotEmpty(defaultValue)
? new SingleValueArgumentEntry(System.currentTimeMillis(), new StringDataEntry(removedKey, defaultValue), null)
: new SingleValueArgumentEntry());
}
}
return arguments;
}
private Map<String, ArgumentEntry> mapToArgumentsWithFetchedValue(CalculatedFieldCtx ctx, List<String> removedTelemetryKeys) {
Map<String, Argument> deletedArguments = ctx.getArguments().entrySet().stream()
.filter(entry -> removedTelemetryKeys.contains(entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Map<String, ArgumentEntry> fetchedArgs = cfService.fetchArgsFromDb(tenantId, entityId, deletedArguments);
fetchedArgs.values().forEach(arg -> arg.setForceResetPrevious(true));
return fetchedArgs;
}
private static List<CalculatedFieldId> getCalculatedFieldIds(CalculatedFieldTelemetryMsgProto proto) {
List<CalculatedFieldId> cfIds = new LinkedList<>();
for (var cfId : proto.getPreviousCalculatedFieldsList()) {
cfIds.add(new CalculatedFieldId(new UUID(cfId.getCalculatedFieldIdMSB(), cfId.getCalculatedFieldIdLSB())));
}
return cfIds;
}
private UUID toTbMsgId(CalculatedFieldTelemetryMsgProto proto) {
if (proto.getTbMsgIdMSB() != 0 && proto.getTbMsgIdLSB() != 0) {
return new UUID(proto.getTbMsgIdMSB(), proto.getTbMsgIdLSB());
}
return null;
}
private TbMsgType toTbMsgType(CalculatedFieldTelemetryMsgProto proto) {
if (!proto.getTbMsgType().isEmpty()) {
return TbMsgType.valueOf(proto.getTbMsgType());
}
return null;
}
}

40
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldException.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Builder;
import lombok.Getter;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.service.cf.ctx.state.ArgumentEntry;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import java.util.Map;
import java.util.UUID;
@Getter
@Builder
public class CalculatedFieldException extends Exception {
private final CalculatedFieldCtx ctx;
private final EntityId eventEntity;
private final UUID msgId;
private final TbMsgType msgType;
private Map<String, ArgumentEntry> arguments;
private String errorMessage;
private Exception cause;
}

40
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldLinkedTelemetryMsg.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldLinkedTelemetryMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto;
@Data
public class CalculatedFieldLinkedTelemetryMsg implements ToCalculatedFieldSystemMsg {
private final TenantId tenantId;
private final EntityId entityId;
private final CalculatedFieldLinkedTelemetryMsgProto proto;
private final TbCallback callback;
@Override
public MsgType getMsgType() {
return MsgType.CF_LINKED_TELEMETRY_MSG;
}
}

90
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActor.java

@ -0,0 +1,90 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldInitMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldLinkInitMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg;
/**
* Created by ashvayka on 15.03.18.
*/
@Slf4j
public class CalculatedFieldManagerActor extends AbstractCalculatedFieldActor {
private final CalculatedFieldManagerMessageProcessor processor;
public CalculatedFieldManagerActor(ActorSystemContext systemContext, TenantId tenantId) {
super(systemContext, tenantId);
this.processor = new CalculatedFieldManagerMessageProcessor(systemContext, tenantId);
}
@Override
public void init(TbActorCtx ctx) throws TbActorException {
super.init(ctx);
log.debug("[{}] Starting CF manager actor.", processor.tenantId);
try {
processor.init(ctx);
log.debug("[{}] CF manager actor started.", processor.tenantId);
} catch (Exception e) {
log.warn("[{}] Unknown failure", processor.tenantId, e);
throw new TbActorException("Failed to initialize manager actor", e);
}
}
@Override
protected boolean doProcessCfMsg(ToCalculatedFieldSystemMsg msg) throws CalculatedFieldException {
switch (msg.getMsgType()) {
case CF_PARTITIONS_CHANGE_MSG:
processor.onPartitionChange((CalculatedFieldPartitionChangeMsg) msg);
break;
case CF_INIT_MSG:
processor.onFieldInitMsg((CalculatedFieldInitMsg) msg);
break;
case CF_LINK_INIT_MSG:
processor.onLinkInitMsg((CalculatedFieldLinkInitMsg) msg);
break;
case CF_STATE_RESTORE_MSG:
processor.onStateRestoreMsg((CalculatedFieldStateRestoreMsg) msg);
break;
case CF_ENTITY_LIFECYCLE_MSG:
processor.onEntityLifecycleMsg((CalculatedFieldEntityLifecycleMsg) msg);
break;
case CF_TELEMETRY_MSG:
processor.onTelemetryMsg((CalculatedFieldTelemetryMsg) msg);
break;
case CF_LINKED_TELEMETRY_MSG:
processor.onLinkedTelemetryMsg((CalculatedFieldLinkedTelemetryMsg) msg);
break;
default:
return false;
}
return true;
}
@Override
void logProcessingException(Exception e) {
log.warn("[{}] Processing failure", tenantId, e);
}
}

46
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerActorCreator.java

@ -0,0 +1,46 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActor;
import org.thingsboard.server.actors.TbActorId;
import org.thingsboard.server.actors.TbEntityActorId;
import org.thingsboard.server.actors.TbStringActorId;
import org.thingsboard.server.actors.service.ContextBasedCreator;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
public class CalculatedFieldManagerActorCreator extends ContextBasedCreator {
private final TenantId tenantId;
public CalculatedFieldManagerActorCreator(ActorSystemContext context, TenantId tenantId) {
super(context);
this.tenantId = tenantId;
}
@Override
public TbActorId createActorId() {
return new TbStringActorId("CFM|" + tenantId);
}
@Override
public TbActor createActor() {
return new CalculatedFieldManagerActor(context, tenantId);
}
}

471
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java

@ -0,0 +1,471 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.DebugModeUtil;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.actors.TbCalculatedFieldEntityActorId;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.cf.CalculatedFieldEntityLifecycleMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldInitMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldLinkInitMsg;
import org.thingsboard.server.common.msg.cf.CalculatedFieldPartitionChangeMsg;
import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.dao.cf.CalculatedFieldService;
import org.thingsboard.server.service.cf.CalculatedFieldProcessingService;
import org.thingsboard.server.service.cf.CalculatedFieldStateService;
import org.thingsboard.server.service.cf.cache.CalculatedFieldEntityProfileCache;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.thingsboard.server.utils.CalculatedFieldUtils.fromProto;
/**
* @author Andrew Shvayka
*/
@Slf4j
public class CalculatedFieldManagerMessageProcessor extends AbstractContextAwareMsgProcessor {
private final Map<CalculatedFieldId, CalculatedFieldCtx> calculatedFields = new HashMap<>();
private final Map<EntityId, List<CalculatedFieldCtx>> entityIdCalculatedFields = new HashMap<>();
private final Map<EntityId, List<CalculatedFieldLink>> entityIdCalculatedFieldLinks = new HashMap<>();
private final CalculatedFieldProcessingService cfExecService;
private final CalculatedFieldStateService cfStateService;
private final CalculatedFieldEntityProfileCache cfEntityCache;
private final CalculatedFieldService cfDaoService;
private final TbAssetProfileCache assetProfileCache;
private final TbDeviceProfileCache deviceProfileCache;
protected final TenantId tenantId;
protected TbActorCtx ctx;
CalculatedFieldManagerMessageProcessor(ActorSystemContext systemContext, TenantId tenantId) {
super(systemContext);
this.cfEntityCache = systemContext.getCalculatedFieldEntityProfileCache();
this.cfExecService = systemContext.getCalculatedFieldProcessingService();
this.cfStateService = systemContext.getCalculatedFieldStateService();
this.cfDaoService = systemContext.getCalculatedFieldService();
this.assetProfileCache = systemContext.getAssetProfileCache();
this.deviceProfileCache = systemContext.getDeviceProfileCache();
this.tenantId = tenantId;
}
void init(TbActorCtx ctx) {
this.ctx = ctx;
}
public void onFieldInitMsg(CalculatedFieldInitMsg msg) throws CalculatedFieldException {
log.info("[{}] Processing CF init message.", msg.getCf().getId());
var cf = msg.getCf();
var cfCtx = new CalculatedFieldCtx(cf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService());
try {
cfCtx.init();
} catch (Exception e) {
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
}
calculatedFields.put(cf.getId(), cfCtx);
// We use copy on write lists to safely pass the reference to another actor for the iteration.
// Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead)
entityIdCalculatedFields.computeIfAbsent(cf.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(cfCtx);
msg.getCallback().onSuccess();
}
public void onLinkInitMsg(CalculatedFieldLinkInitMsg msg) {
log.info("[{}] Processing CF link init message for entity [{}].", msg.getLink().getCalculatedFieldId(), msg.getLink().getEntityId());
var link = msg.getLink();
// We use copy on write lists to safely pass the reference to another actor for the iteration.
// Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead)
entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(link);
msg.getCallback().onSuccess();
}
public void onStateRestoreMsg(CalculatedFieldStateRestoreMsg msg) {
var cfId = msg.getId().cfId();
var calculatedField = calculatedFields.get(cfId);
if (calculatedField != null) {
msg.getState().setRequiredArguments(calculatedField.getArgNames());
log.info("Pushing CF state restore msg to specific actor [{}]", msg.getId().entityId());
getOrCreateActor(msg.getId().entityId()).tell(msg);
} else {
cfStateService.removeState(msg.getId(), msg.getCallback());
}
}
public void onEntityLifecycleMsg(CalculatedFieldEntityLifecycleMsg msg) throws CalculatedFieldException {
log.info("Processing entity lifecycle event: [{}] for entity: [{}]", msg.getData().getEvent(), msg.getData().getEntityId());
var entityType = msg.getData().getEntityId().getEntityType();
var event = msg.getData().getEvent();
switch (entityType) {
case CALCULATED_FIELD: {
switch (event) {
case CREATED:
onCfCreated(msg.getData(), msg.getCallback());
break;
case UPDATED:
onCfUpdated(msg.getData(), msg.getCallback());
break;
case DELETED:
onCfDeleted(msg.getData(), msg.getCallback());
break;
default:
msg.getCallback().onSuccess();
break;
}
break;
}
case DEVICE:
case ASSET: {
switch (event) {
case CREATED:
onEntityCreated(msg.getData(), msg.getCallback());
break;
case UPDATED:
onEntityUpdated(msg.getData(), msg.getCallback());
break;
case DELETED:
onEntityDeleted(msg.getData(), msg.getCallback());
break;
default:
msg.getCallback().onSuccess();
break;
}
break;
}
default: {
msg.getCallback().onSuccess();
}
}
}
private void onEntityCreated(ComponentLifecycleMsg msg, TbCallback callback) {
EntityId entityId = msg.getEntityId();
EntityId profileId = getProfileId(tenantId, entityId);
cfEntityCache.add(tenantId, profileId, entityId);
var entityIdFields = getCalculatedFieldsByEntityId(entityId);
var profileIdFields = getCalculatedFieldsByEntityId(profileId);
var fieldsCount = entityIdFields.size() + profileIdFields.size();
if (fieldsCount > 0) {
MultipleTbCallback multiCallback = new MultipleTbCallback(fieldsCount, callback);
entityIdFields.forEach(ctx -> initCfForEntity(entityId, ctx, true, multiCallback));
profileIdFields.forEach(ctx -> initCfForEntity(entityId, ctx, true, multiCallback));
} else {
callback.onSuccess();
}
}
private void onEntityUpdated(ComponentLifecycleMsg msg, TbCallback callback) {
if (msg.getOldProfileId() != null && msg.getOldProfileId() != msg.getProfileId()) {
cfEntityCache.update(tenantId, msg.getOldProfileId(), msg.getProfileId(), msg.getEntityId());
var oldProfileCfs = getCalculatedFieldsByEntityId(msg.getOldProfileId());
var newProfileCfs = getCalculatedFieldsByEntityId(msg.getProfileId());
var fieldsCount = oldProfileCfs.size() + newProfileCfs.size();
if (fieldsCount > 0) {
MultipleTbCallback multiCallback = new MultipleTbCallback(fieldsCount, callback);
var entityId = msg.getEntityId();
oldProfileCfs.forEach(ctx -> deleteCfForEntity(entityId, ctx.getCfId(), callback));
newProfileCfs.forEach(ctx -> initCfForEntity(entityId, ctx, true, multiCallback));
} else {
callback.onSuccess();
}
}
}
private void onEntityDeleted(ComponentLifecycleMsg msg, TbCallback callback) {
cfEntityCache.evict(tenantId, msg.getEntityId());
log.info("Pushing entity lifecycle msg to specific actor [{}]", msg.getEntityId());
getOrCreateActor(msg.getEntityId()).tell(new CalculatedFieldEntityDeleteMsg(tenantId, msg.getEntityId(), callback));
}
private void onCfCreated(ComponentLifecycleMsg msg, TbCallback callback) throws CalculatedFieldException {
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
if (calculatedFields.containsKey(cfId)) {
log.warn("[{}] CF was already initialized [{}]", tenantId, cfId);
callback.onSuccess();
} else {
var cf = cfDaoService.findById(msg.getTenantId(), cfId);
if (cf == null) {
log.warn("[{}] Failed to lookup CF by id [{}]", tenantId, cfId);
callback.onSuccess();
} else {
var cfCtx = new CalculatedFieldCtx(cf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService());
try {
cfCtx.init();
} catch (Exception e) {
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
}
calculatedFields.put(cf.getId(), cfCtx);
// We use copy on write lists to safely pass the reference to another actor for the iteration.
// Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead)
entityIdCalculatedFields.computeIfAbsent(cf.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(cfCtx);
addLinks(cf);
initCf(cfCtx, callback, false);
}
}
}
private void onCfUpdated(ComponentLifecycleMsg msg, TbCallback callback) throws CalculatedFieldException {
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
var oldCfCtx = calculatedFields.get(cfId);
if (oldCfCtx == null) {
onCfCreated(msg, callback);
} else {
var newCf = cfDaoService.findById(msg.getTenantId(), cfId);
if (newCf == null) {
log.warn("[{}] Failed to lookup CF by id [{}]", tenantId, cfId);
callback.onSuccess();
} else {
var newCfCtx = new CalculatedFieldCtx(newCf, systemContext.getTbelInvokeService(), systemContext.getApiLimitService());
try {
newCfCtx.init();
} catch (Exception e) {
throw CalculatedFieldException.builder().ctx(newCfCtx).eventEntity(newCfCtx.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build();
}
calculatedFields.put(newCf.getId(), newCfCtx);
List<CalculatedFieldCtx> oldCfList = entityIdCalculatedFields.get(newCf.getEntityId());
List<CalculatedFieldCtx> newCfList = new CopyOnWriteArrayList<>();
boolean found = false;
for (CalculatedFieldCtx oldCtx : oldCfList) {
if (oldCtx.getCfId().equals(newCf.getId())) {
newCfList.add(newCfCtx);
found = true;
} else {
newCfList.add(oldCtx);
}
}
if (!found) {
newCfList.add(newCfCtx);
}
entityIdCalculatedFields.put(newCf.getEntityId(), newCfList);
deleteLinks(oldCfCtx);
addLinks(newCf);
// We use copy on write lists to safely pass the reference to another actor for the iteration.
// Alternative approach would be to use any list but avoid modifications to the list (change the complete map value instead)
var stateChanges = newCfCtx.hasStateChanges(oldCfCtx);
if (stateChanges || newCfCtx.hasOtherSignificantChanges(oldCfCtx)) {
initCf(newCfCtx, callback, stateChanges);
} else {
callback.onSuccess();
}
}
}
}
private void onCfDeleted(ComponentLifecycleMsg msg, TbCallback callback) {
var cfId = new CalculatedFieldId(msg.getEntityId().getId());
var cfCtx = calculatedFields.remove(cfId);
if (cfCtx == null) {
log.warn("[{}] CF was already deleted [{}]", tenantId, cfId);
callback.onSuccess();
} else {
entityIdCalculatedFields.get(cfCtx.getEntityId()).remove(cfCtx);
deleteLinks(cfCtx);
EntityId entityId = cfCtx.getEntityId();
EntityType entityType = cfCtx.getEntityId().getEntityType();
if (isProfileEntity(entityType)) {
var entityIds = cfEntityCache.getMyEntityIdsByProfileId(tenantId, entityId);
if (!entityIds.isEmpty()) {
//TODO: no need to do this if we cache all created actors and know which one belong to us;
var multiCallback = new MultipleTbCallback(entityIds.size(), callback);
entityIds.forEach(id -> deleteCfForEntity(entityId, cfId, multiCallback));
} else {
callback.onSuccess();
}
} else {
deleteCfForEntity(entityId, cfId, callback);
}
}
}
public void onTelemetryMsg(CalculatedFieldTelemetryMsg msg) {
EntityId entityId = msg.getEntityId();
log.info("Received telemetry msg from entity [{}]", entityId);
// 2 = 1 for CF processing + 1 for links processing
MultipleTbCallback callback = new MultipleTbCallback(2, msg.getCallback());
// process all cfs related to entity, or it's profile;
var entityIdFields = getCalculatedFieldsByEntityId(entityId);
var profileIdFields = getCalculatedFieldsByEntityId(getProfileId(tenantId, entityId));
if (!entityIdFields.isEmpty() || !profileIdFields.isEmpty()) {
log.info("Pushing telemetry msg to specific actor [{}]", entityId);
getOrCreateActor(entityId).tell(new EntityCalculatedFieldTelemetryMsg(msg, entityIdFields, profileIdFields, callback));
} else {
callback.onSuccess();
}
// process all links (if any);
List<CalculatedFieldEntityCtxId> linkedCalculatedFields = filterCalculatedFieldLinks(msg);
var linksSize = linkedCalculatedFields.size();
if (linksSize > 0) {
cfExecService.pushMsgToLinks(msg, linkedCalculatedFields, callback);
} else {
callback.onSuccess();
}
}
public void onLinkedTelemetryMsg(CalculatedFieldLinkedTelemetryMsg msg) {
EntityId sourceEntityId = msg.getEntityId();
log.info("Received linked telemetry msg from entity [{}]", sourceEntityId);
var proto = msg.getProto();
var linksList = proto.getLinksList();
for (var linkProto : linksList) {
var link = fromProto(linkProto);
var targetEntityId = link.entityId();
var targetEntityType = targetEntityId.getEntityType();
var cf = calculatedFields.get(link.cfId());
if (EntityType.DEVICE_PROFILE.equals(targetEntityType) || EntityType.ASSET_PROFILE.equals(targetEntityType)) {
// iterate over all entities that belong to profile and push the message for corresponding CF
var entityIds = cfEntityCache.getMyEntityIdsByProfileId(tenantId, targetEntityId);
if (!entityIds.isEmpty()) {
MultipleTbCallback callback = new MultipleTbCallback(entityIds.size(), msg.getCallback());
var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, callback);
entityIds.forEach(entityId -> {
log.info("Pushing linked telemetry msg to specific actor [{}]", entityId);
getOrCreateActor(entityId).tell(newMsg);
});
} else {
msg.getCallback().onSuccess();
}
} else {
log.info("Pushing linked telemetry msg to specific actor [{}]", targetEntityId);
var newMsg = new EntityCalculatedFieldLinkedTelemetryMsg(tenantId, sourceEntityId, proto.getMsg(), cf, msg.getCallback());
getOrCreateActor(targetEntityId).tell(newMsg);
}
}
}
private List<CalculatedFieldEntityCtxId> filterCalculatedFieldLinks(CalculatedFieldTelemetryMsg msg) {
EntityId entityId = msg.getEntityId();
var proto = msg.getProto();
List<CalculatedFieldEntityCtxId> result = new ArrayList<>();
for (var link : getCalculatedFieldLinksByEntityId(entityId)) {
CalculatedFieldCtx ctx = calculatedFields.get(link.getCalculatedFieldId());
if (ctx.linkMatches(entityId, proto)) {
result.add(ctx.toCalculatedFieldEntityCtxId());
}
}
return result;
}
private List<CalculatedFieldCtx> getCalculatedFieldsByEntityId(EntityId entityId) {
if (entityId == null) {
return Collections.emptyList();
}
var result = entityIdCalculatedFields.get(entityId);
if (result == null) {
result = Collections.emptyList();
}
return result;
}
private List<CalculatedFieldLink> getCalculatedFieldLinksByEntityId(EntityId entityId) {
if (entityId == null) {
return Collections.emptyList();
}
var result = entityIdCalculatedFieldLinks.get(entityId);
if (result == null) {
result = Collections.emptyList();
}
return result;
}
private void initCf(CalculatedFieldCtx cfCtx, TbCallback callback, boolean forceStateReinit) {
EntityId entityId = cfCtx.getEntityId();
EntityType entityType = cfCtx.getEntityId().getEntityType();
if (isProfileEntity(entityType)) {
var entityIds = cfEntityCache.getMyEntityIdsByProfileId(tenantId, entityId);
if (!entityIds.isEmpty()) {
var multiCallback = new MultipleTbCallback(entityIds.size(), callback);
entityIds.forEach(id -> initCfForEntity(id, cfCtx, forceStateReinit, multiCallback));
} else {
callback.onSuccess();
}
} else {
initCfForEntity(entityId, cfCtx, forceStateReinit, callback);
}
}
private void deleteCfForEntity(EntityId entityId, CalculatedFieldId cfId, TbCallback callback) {
log.info("Pushing delete CF msg to specific actor [{}]", entityId);
getOrCreateActor(entityId).tell(new CalculatedFieldEntityDeleteMsg(tenantId, cfId, callback));
}
private void initCfForEntity(EntityId entityId, CalculatedFieldCtx cfCtx, boolean forceStateReinit, TbCallback callback) {
log.info("Pushing entity init CF msg to specific actor [{}]", entityId);
getOrCreateActor(entityId).tell(new EntityInitCalculatedFieldMsg(tenantId, cfCtx, callback, forceStateReinit));
}
private static boolean isProfileEntity(EntityType entityType) {
return EntityType.DEVICE_PROFILE.equals(entityType) || EntityType.ASSET_PROFILE.equals(entityType);
}
private EntityId getProfileId(TenantId tenantId, EntityId entityId) {
return switch (entityId.getEntityType()) {
case ASSET -> assetProfileCache.get(tenantId, (AssetId) entityId).getId();
case DEVICE -> deviceProfileCache.get(tenantId, (DeviceId) entityId).getId();
default -> null;
};
}
private TbActorRef getOrCreateActor(EntityId entityId) {
return ctx.getOrCreateChildActor(new TbCalculatedFieldEntityActorId(entityId),
() -> DefaultActorService.CF_ENTITY_DISPATCHER_NAME,
() -> new CalculatedFieldEntityActorCreator(systemContext, tenantId, entityId),
() -> true);
}
private void addLinks(CalculatedField newCf) {
var newLinks = newCf.getConfiguration().buildCalculatedFieldLinks(tenantId, newCf.getEntityId(), newCf.getId());
newLinks.forEach(link -> entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).add(link));
}
private void deleteLinks(CalculatedFieldCtx cfCtx) {
var oldCf = cfCtx.getCalculatedField();
var oldLinks = oldCf.getConfiguration().buildCalculatedFieldLinks(tenantId, oldCf.getEntityId(), oldCf.getId());
oldLinks.forEach(link -> entityIdCalculatedFieldLinks.computeIfAbsent(link.getEntityId(), id -> new CopyOnWriteArrayList<>()).remove(link));
}
public void onPartitionChange(CalculatedFieldPartitionChangeMsg msg) {
ctx.broadcastToChildren(msg, true);
}
}

40
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldStateRestoreMsg.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.service.cf.ctx.CalculatedFieldEntityCtxId;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldState;
@Data
public class CalculatedFieldStateRestoreMsg implements ToCalculatedFieldSystemMsg {
private final CalculatedFieldEntityCtxId id;
private final CalculatedFieldState state;
@Override
public MsgType getMsgType() {
return MsgType.CF_STATE_RESTORE_MSG;
}
@Override
public TenantId getTenantId() {
return id.tenantId();
}
}

39
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldTelemetryMsg.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto;
@Data
public class CalculatedFieldTelemetryMsg implements ToCalculatedFieldSystemMsg {
private final TenantId tenantId;
private final EntityId entityId;
private final CalculatedFieldTelemetryMsgProto proto;
private final TbCallback callback;
@Override
public MsgType getMsgType() {
return MsgType.CF_TELEMETRY_MSG;
}
}

42
application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityCalculatedFieldLinkedTelemetryMsg.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import java.util.List;
@Data
public class EntityCalculatedFieldLinkedTelemetryMsg implements ToCalculatedFieldSystemMsg {
private final TenantId tenantId;
private final EntityId entityId;
private final CalculatedFieldTelemetryMsgProto proto;
private final CalculatedFieldCtx ctx;
private final TbCallback callback;
@Override
public MsgType getMsgType() {
return MsgType.CF_LINKED_TELEMETRY_MSG;
}
}

56
application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityCalculatedFieldTelemetryMsg.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import java.util.List;
@Data
public class EntityCalculatedFieldTelemetryMsg implements ToCalculatedFieldSystemMsg {
private final TenantId tenantId;
private final EntityId entityId;
private final CalculatedFieldTelemetryMsgProto proto;
// Both lists are effectively immutable in CalculatedFieldManagerMessageProcessor and must stay so.
private final List<CalculatedFieldCtx> entityIdFields;
private final List<CalculatedFieldCtx> profileIdFields;
private final TbCallback callback;
public EntityCalculatedFieldTelemetryMsg(CalculatedFieldTelemetryMsg msg,
List<CalculatedFieldCtx> entityIdFields,
List<CalculatedFieldCtx> profileIdFields,
TbCallback callback) {
this.tenantId = msg.getTenantId();
this.entityId = msg.getEntityId();
this.proto = msg.getProto();
this.entityIdFields = entityIdFields;
this.profileIdFields = profileIdFields;
this.callback = callback;
}
@Override
public MsgType getMsgType() {
return MsgType.CF_ENTITY_TELEMETRY_MSG;
}
}

41
application/src/main/java/org/thingsboard/server/actors/calculatedField/EntityInitCalculatedFieldMsg.java

@ -0,0 +1,41 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToCalculatedFieldSystemMsg;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.CalculatedFieldTelemetryMsgProto;
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldCtx;
import java.util.List;
@Data
public class EntityInitCalculatedFieldMsg implements ToCalculatedFieldSystemMsg {
private final TenantId tenantId;
private final CalculatedFieldCtx ctx;
private final TbCallback callback;
private final boolean forceReinit;
@Override
public MsgType getMsgType() {
return MsgType.CF_ENTITY_INIT_CF_MSG;
}
}

56
application/src/main/java/org/thingsboard/server/actors/calculatedField/MultipleTbCallback.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016-2025 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.calculatedField;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.msg.queue.TbCallback;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class MultipleTbCallback implements TbCallback {
@Getter
private final UUID id;
private final AtomicInteger counter;
private final TbCallback callback;
public MultipleTbCallback(int count, TbCallback callback) {
id = UUID.randomUUID();
this.counter = new AtomicInteger(count);
this.callback = callback;
}
@Override
public void onSuccess() {
onSuccess(1);
}
public void onSuccess(int number) {
log.trace("[{}][{}] onSuccess({})", id, callback.getId(), number);
if (counter.addAndGet(-number) <= 0) {
log.trace("[{}][{}] Done.", id, callback.getId());
callback.onSuccess();
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}][{}] onFailure.", id, callback.getId());
callback.onFailure(t);
}
}

2
application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorCreator.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

8
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.
@ -126,8 +126,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
final TenantId tenantId;
final DeviceId deviceId;
final LinkedHashMapRemoveEldest<UUID, SessionInfoMetaData> sessions;
private final Map<UUID, SessionInfo> attributeSubscriptions;
private final Map<UUID, SessionInfo> rpcSubscriptions;
final Map<UUID, SessionInfo> attributeSubscriptions;
final Map<UUID, SessionInfo> rpcSubscriptions;
private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
private final boolean rpcSequential;
private final RpcSubmitStrategy rpcSubmitStrategy;
@ -865,6 +865,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) {
attributeSubscriptions.remove(sessionId);
rpcSubscriptions.remove(sessionId);
notifyTransportAboutClosedSession(sessionId, sessionMd, TransportSessionCloseReason.MAX_CONCURRENT_SESSIONS_LIMIT_REACHED);
}

2
application/src/main/java/org/thingsboard/server/actors/device/SessionInfo.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/device/SessionInfoMetaData.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/device/SessionTimeoutCheckMsg.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/device/ToDeviceRpcRequestMetadata.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/device/ToServerRpcRequestMetadata.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/device/TransportSessionCloseReason.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

15
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.
@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.channel.EventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Arrays;
import org.thingsboard.common.util.DebugModeUtil;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ListeningExecutor;
import org.thingsboard.rule.engine.api.MailService;
@ -64,7 +65,6 @@ 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;
@ -79,6 +79,7 @@ import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.cf.CalculatedFieldService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
@ -178,7 +179,7 @@ public class DefaultTbContext implements TbContext {
.resetRuleNodeId()
.build();
tbMsg.pushToStack(nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId());
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getQueueName(), getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(msg);
doEnqueue(tpi, tbMsg, new SimpleTbQueueCallback(md -> ack(msg), t -> tellFailure(msg, t)));
}
@ -195,8 +196,7 @@ public class DefaultTbContext implements TbContext {
@Override
public void enqueue(TbMsg tbMsg, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getQueueName(), getTenantId(), tbMsg.getOriginator());
enqueue(tpi, tbMsg, onFailure, onSuccess);
enqueue(tbMsg, tbMsg.getQueueName(), onSuccess, onFailure);
}
@Override
@ -897,6 +897,11 @@ public class DefaultTbContext implements TbContext {
return mainCtx.getSlackService();
}
@Override
public CalculatedFieldService getCalculatedFieldService() {
return mainCtx.getCalculatedFieldService();
}
@Override
public boolean isExternalNodeForceAck() {
return mainCtx.isExternalNodeForceAck();

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainInputMsg.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainOutputMsg.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleChainMsg.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainToRuleNodeMsg.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
* Copyright © 2016-2025 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.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save