Browse Source

Merge remote-tracking branch 'upstream/master' into feature/dashboard-layout-scada

pull/11430/head
Vladyslav_Prykhodko 2 years ago
parent
commit
acf85dc2ba
  1. 312
      application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg
  2. 171
      application/src/main/data/json/system/scada_symbols/bottom-right-elbow-pipe.svg
  3. 387
      application/src/main/data/json/system/scada_symbols/bottom-tee-pipe.svg
  4. 150
      application/src/main/data/json/system/scada_symbols/centrifugal-pump.svg
  5. 500
      application/src/main/data/json/system/scada_symbols/cross-pipe.svg
  6. 82
      application/src/main/data/json/system/scada_symbols/horizontal-ball-valve.svg
  7. 318
      application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg
  8. 186
      application/src/main/data/json/system/scada_symbols/horizontal-pipe.svg
  9. 20
      application/src/main/data/json/system/scada_symbols/horizontal-tank-with-screen.svg
  10. 82
      application/src/main/data/json/system/scada_symbols/horizontal-wheel-valve.svg
  11. 171
      application/src/main/data/json/system/scada_symbols/left-bottom-elbow-pipe.svg
  12. 166
      application/src/main/data/json/system/scada_symbols/left-drain-pipe.svg
  13. 181
      application/src/main/data/json/system/scada_symbols/left-elbow-drain-pipe.svg
  14. 318
      application/src/main/data/json/system/scada_symbols/left-flow-meter.svg
  15. 196
      application/src/main/data/json/system/scada_symbols/left-motor-pump.svg
  16. 387
      application/src/main/data/json/system/scada_symbols/left-tee-pipe.svg
  17. 171
      application/src/main/data/json/system/scada_symbols/left-top-elbow-pipe.svg
  18. 318
      application/src/main/data/json/system/scada_symbols/level_and_fan.svg
  19. 11
      application/src/main/data/json/system/scada_symbols/long-bottom-filter.svg
  20. 167
      application/src/main/data/json/system/scada_symbols/long-horizontal-pipe.svg
  21. 11
      application/src/main/data/json/system/scada_symbols/long-top-filter.svg
  22. 167
      application/src/main/data/json/system/scada_symbols/long-vertical-pipe.svg
  23. 167
      application/src/main/data/json/system/scada_symbols/right-drain-pipe.svg
  24. 178
      application/src/main/data/json/system/scada_symbols/right-elbow-drain-pipe.svg
  25. 312
      application/src/main/data/json/system/scada_symbols/right-flow-meter.svg
  26. 196
      application/src/main/data/json/system/scada_symbols/right-motor-pump.svg
  27. 387
      application/src/main/data/json/system/scada_symbols/right-tee-pipe.svg
  28. 11
      application/src/main/data/json/system/scada_symbols/short-bottom-filter.svg
  29. 160
      application/src/main/data/json/system/scada_symbols/short-left-drain-pipe.svg
  30. 160
      application/src/main/data/json/system/scada_symbols/short-right-drain-pipe.svg
  31. 11
      application/src/main/data/json/system/scada_symbols/short-top-filter.svg
  32. 174
      application/src/main/data/json/system/scada_symbols/small-left-motor-pump.svg
  33. 123
      application/src/main/data/json/system/scada_symbols/small-right-motor-pump.svg
  34. 313
      application/src/main/data/json/system/scada_symbols/top-flow-meter.svg
  35. 171
      application/src/main/data/json/system/scada_symbols/top-right-elbow-pipe.svg
  36. 387
      application/src/main/data/json/system/scada_symbols/top-tee-pipe.svg
  37. 82
      application/src/main/data/json/system/scada_symbols/vertical-ball-valve.svg
  38. 315
      application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg
  39. 168
      application/src/main/data/json/system/scada_symbols/vertical-pipe.svg
  40. 24
      application/src/main/data/json/system/scada_symbols/vertical-tank-with-level.svg
  41. 1394
      application/src/main/data/json/system/scada_symbols/vertical-tank.svg
  42. 112
      application/src/main/data/json/system/scada_symbols/vertical-wheel-valve.svg
  43. 10
      application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json
  44. 26
      application/src/main/data/upgrade/3.7.0/schema_update.sql
  45. 68
      application/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java
  46. 2
      application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java
  47. 4
      application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java
  48. 3
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  49. 26
      application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
  50. 4
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  51. 2
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  52. 46
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  53. 1
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  54. 4
      application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
  55. 29
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  56. 17
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  57. 19
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  58. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  59. 24
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java
  60. 15
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java
  61. 12
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java
  62. 17
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  63. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java
  64. 2
      application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java
  65. 4
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  66. 36
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  67. 5
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java
  68. 2
      application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
  69. 5
      application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
  70. 3
      application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java
  71. 3
      application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java
  72. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java
  73. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java
  74. 4
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java
  75. 14
      application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java
  76. 4
      application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java
  77. 30
      application/src/main/resources/thingsboard.yml
  78. 2
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  79. 30
      application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest.java
  80. 36
      application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest_DedicatedEventsDataSource.java
  81. 30
      application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
  82. 66
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  83. 2
      application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
  84. 2
      application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java
  85. 8
      application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
  86. 10
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  87. 58
      application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
  88. 4
      application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java
  89. 6
      application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java
  90. 28
      application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java
  91. 6
      application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/AbstractDeviceProcessorTest.java
  92. 33
      application/src/test/java/org/thingsboard/server/system/RestTemplateConvertersTest.java
  93. 10
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java
  94. 10
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java
  95. 23
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java
  96. 10
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedTbCache.java
  97. 2
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheEvictEvent.java
  98. 4
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCaffeineCache.java
  99. 4
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceRedisCache.java
  100. 5
      common/data/src/main/java/org/thingsboard/server/common/data/Customer.java

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

@ -51,6 +51,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
@ -78,13 +83,32 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 0,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": 0,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "flowRate"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -93,13 +117,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -108,13 +152,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -123,13 +187,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "broken",
@ -138,13 +222,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.broken}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "displayClick",
@ -153,13 +257,16 @@
"group": "{i18n:scada.symbol.display}",
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "fluid",
@ -168,13 +275,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
@ -183,13 +310,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
@ -198,13 +345,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
@ -213,13 +380,32 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
@ -228,13 +414,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -509,6 +715,22 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><mask id="path-7-inside-1_1253_89545" fill="white">
@ -703,7 +925,7 @@
<feComposite in2="SourceAlpha" operator="in" result="effect1_backgroundBlur_1281_42354"/>
<feBlend in="SourceGraphic" in2="effect1_backgroundBlur_1281_42354" result="shape"/>
</filter>
</defs><rect x="14" y="64" width="372" height="72" fill="#fff"/><rect x="14" y="64" width="372" height="72" fill="url(#paint0_linear_1310_41908)" style="fill:url(#paint0_linear_1310_41908)"/><rect x="15.5" y="65.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="14" y="64" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="64" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="m164 164v-28.421l36-31.579 36 31.579v28.421z" fill="#fff"/><path d="m164 164v-28.421l36-31.579 36 31.579v28.421z" fill="url(#paint1_linear_1310_41908)" style="fill:url(#paint1_linear_1310_41908)"/><path d="m165.5 162.5v-26.242l34.5-30.263 34.5 30.263v26.242z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="387.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><path transform="translate(0,150)" d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z" fill="#4a4848" mask="url(#path-7-inside-1_1253_89545)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="200" cy="275" r="111" fill="#fff" tb:tag="background"/><rect x="119.5" y="255.5" width="161" height="39" rx="3.8571" fill="#4a4848" fill-opacity=".06" tb:tag="border"/><path d="m123.36 254c-2.9419 0-5.3574 2.4155-5.3574 5.3574v31.285c0 2.9419 2.4155 5.3574 5.3574 5.3574h153.29c2.9419 0 5.3574-2.4155 5.3574-5.3574v-31.285c0-2.9419-2.4155-5.3574-5.3574-5.3574zm0 3h153.29c1.3318 0 2.3574 1.0256 2.3574 2.3574v31.285c0 1.3318-1.0256 2.3574-2.3574 2.3574h-153.29c-1.3318 0-2.3574-1.0256-2.3574-2.3574v-31.285c0-1.3318 1.0256-2.3574 2.3574-2.3574z" fill="#4a4848" tb:tag="border"/><circle cx="166" cy="223" r="16" fill="url(#paint2_linear_1253_89545)" style="fill:url(#paint2_linear_1253_89545)"/><circle cx="166" cy="223" r="15.5" stroke="#000" stroke-opacity=".12"/><circle cx="234" cy="223" r="16" fill="url(#paint3_linear_1253_89545)" style="fill:url(#paint3_linear_1253_89545)"/><circle cx="234" cy="223" r="15.5" stroke="#000" stroke-opacity=".12"/><text x="198.96094" y="276.94531" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:32px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="value" xml:space="preserve"><tspan>0</tspan></text><text x="199.47363" y="324.4729" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:22px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="valueUnits" xml:space="preserve"><tspan>m³/hr</tspan></text><g transform="translate(0 150)" style="display: none;" tb:tag="broken">
</defs><rect x="14" y="64" width="372" height="72" fill="#fff" tb:tag="pipe-background"/><rect x="14" y="64" width="372" height="72" fill="url(#paint0_linear_1310_41908)" style="fill:url(#paint0_linear_1310_41908)"/><rect x="15.5" y="65.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="14" y="64" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="64" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="m164 164v-28.421l36-31.579 36 31.579v28.421z" fill="#fff" tb:tag="pipe-background"/><path d="m164 164v-28.421l36-31.579 36 31.579v28.421z" fill="url(#paint1_linear_1310_41908)" style="fill:url(#paint1_linear_1310_41908)"/><path d="m165.5 162.5v-26.242l34.5-30.263 34.5 30.263v26.242z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="387.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><path transform="translate(0,150)" d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z" fill="#4a4848" mask="url(#path-7-inside-1_1253_89545)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="200" cy="275" r="111" fill="#fff" tb:tag="background"/><rect x="119.5" y="255.5" width="161" height="39" rx="3.8571" fill="#4a4848" fill-opacity=".06" tb:tag="border"/><path d="m123.36 254c-2.9419 0-5.3574 2.4155-5.3574 5.3574v31.285c0 2.9419 2.4155 5.3574 5.3574 5.3574h153.29c2.9419 0 5.3574-2.4155 5.3574-5.3574v-31.285c0-2.9419-2.4155-5.3574-5.3574-5.3574zm0 3h153.29c1.3318 0 2.3574 1.0256 2.3574 2.3574v31.285c0 1.3318-1.0256 2.3574-2.3574 2.3574h-153.29c-1.3318 0-2.3574-1.0256-2.3574-2.3574v-31.285c0-1.3318 1.0256-2.3574 2.3574-2.3574z" fill="#4a4848" tb:tag="border"/><circle cx="166" cy="223" r="16" fill="url(#paint2_linear_1253_89545)" style="fill:url(#paint2_linear_1253_89545)"/><circle cx="166" cy="223" r="15.5" stroke="#000" stroke-opacity=".12"/><circle cx="234" cy="223" r="16" fill="url(#paint3_linear_1253_89545)" style="fill:url(#paint3_linear_1253_89545)"/><circle cx="234" cy="223" r="15.5" stroke="#000" stroke-opacity=".12"/><text x="198.96094" y="276.94531" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:32px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="value" xml:space="preserve"><tspan>0</tspan></text><text x="199.47363" y="324.4729" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:22px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="valueUnits" xml:space="preserve"><tspan>m³/hr</tspan></text><g transform="translate(0 150)" style="display: none;" tb:tag="broken">
<g filter="url(#filter0_b_1281_42354)" style="filter: url(&quot;#filter0_b_1281_42354-3&quot;);">
<circle cx="200" cy="125" r="111" fill="#000" fill-opacity=".24" style=""/>
</g>

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 74 KiB

171
application/src/main/data/json/system/scada_symbols/bottom-right-elbow-pipe.svg

@ -30,6 +30,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "vertical-fluid",
"stateRenderFunction": "var fluid = ctx.values.fluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}\n",
@ -41,71 +46,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -124,18 +233,34 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g transform="rotate(-90,100,100)" clip-path="url(#clip0_1245_66459)">
<rect x="14" y="64" width="50" height="72" fill="#fff"/>
<rect x="14" y="64" width="50" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="14" y="64" width="50" height="72" fill="url(#paint0_linear_1245_66459)" style="fill:url(#paint0_linear_1245_66459)"/>
<rect x="15.5" y="65.5" width="47" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m64 186v-50h72v50z" fill="#fff"/>
<path d="m64 186v-50h72v50z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-50h72v50z" fill="url(#paint1_linear_1245_66459)" style="fill:url(#paint1_linear_1245_66459)"/>
<path d="m65.5 184.5v-47h69v47z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(-90,51.5,198.5)" x="51.5" y="198.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="url(#paint2_linear_1245_66459)" style="fill:url(#paint2_linear_1245_66459)"/>
<path d="m65.5 134.5v-68.865c0.8334 0.0861 1.9717 0.2211 3.3584 0.4273 3.1196 0.464 7.4889 1.2873 12.47 2.7228 9.9828 2.8767 22.316 8.1809 32.01 17.875 9.695 9.6942 14.999 22.027 17.875 32.01 1.436 4.982 2.259 9.351 2.723 12.471 0.206 1.386 0.341 2.525 0.428 3.358z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
</g><defs>

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 51 KiB

387
application/src/main/data/json/system/scada_symbols/bottom-tee-pipe.svg

@ -40,6 +40,11 @@
"stateRenderFunction": "var fluid = (ctx.values.leftFluid || ctx.values.rightFluid ||\n ctx.values.bottomFluid) && !ctx.values.leak;\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "right-fluid",
"stateRenderFunction": "var fluid = ctx.values.rightFluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
@ -59,13 +64,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlow",
@ -74,13 +99,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowDirection",
@ -89,13 +134,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowAnimationSpeed",
@ -104,13 +169,32 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFluid",
@ -119,13 +203,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlow",
@ -134,13 +238,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowDirection",
@ -149,13 +273,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowAnimationSpeed",
@ -164,13 +308,32 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFluid",
@ -179,13 +342,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlow",
@ -194,13 +377,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowDirection",
@ -209,13 +412,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowAnimationSpeed",
@ -224,27 +447,67 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -295,13 +558,29 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g clip-path="url(#clip0_1245_66617)">
<path d="m14 64h172v72h-172z" fill="#fff"/>
<path d="m14 64h172v72h-172z" fill="#fff" tb:tag="pipe-background"/>
<path d="m14 64h172v72h-172z" fill="url(#paint0_linear_1245_66617)" style="fill:url(#paint0_linear_1245_66617)"/>
<path d="m15.5 65.5h169v69h-169z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m136 186v-51l-36-35-36 35v51z" fill="#fff"/>
<path d="m136 186v-51l-36-35-36 35v51z" fill="#fff" tb:tag="pipe-background"/>
<path d="m136 186v-51l-36-35-36 35v51z" fill="url(#paint1_linear_1245_66617)" style="fill:url(#paint1_linear_1245_66617)"/>
<path d="m134.5 184.5v-48.866l-34.5-33.542-34.5 33.542v48.866z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 58 KiB

150
application/src/main/data/json/system/scada_symbols/centrifugal-pump.svg

@ -67,13 +67,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -82,13 +102,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -97,13 +137,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -112,13 +172,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rotationAnimationSpeed",
@ -127,13 +207,32 @@
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
@ -142,13 +241,16 @@
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
}
],
"properties": [

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 30 KiB

500
application/src/main/data/json/system/scada_symbols/cross-pipe.svg

@ -40,6 +40,11 @@
"stateRenderFunction": "var fluid = (ctx.values.leftFluid || ctx.values.rightFluid ||\n ctx.values.topFluid || ctx.values.bottomFluid) && !ctx.values.leak;\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "right-fluid",
"stateRenderFunction": "var fluid = ctx.values.rightFluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
@ -69,13 +74,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlow",
@ -84,13 +109,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowDirection",
@ -99,13 +144,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowAnimationSpeed",
@ -114,13 +179,32 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFluid",
@ -129,13 +213,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlow",
@ -144,13 +248,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowDirection",
@ -159,13 +283,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowAnimationSpeed",
@ -174,13 +318,32 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFluid",
@ -189,13 +352,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlow",
@ -204,13 +387,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowDirection",
@ -219,13 +422,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowAnimationSpeed",
@ -234,13 +457,32 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFluid",
@ -249,13 +491,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlow",
@ -264,13 +526,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowDirection",
@ -279,13 +561,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowAnimationSpeed",
@ -294,27 +596,67 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -381,18 +723,34 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g clip-path="url(#clip0_1245_66442)">
<path d="m64 186v-172h72v172h-72z" fill="#fff"/>
<path d="m64 186v-172h72v172h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-172h72v172h-72z" fill="url(#paint0_linear_1245_66442)"/>
<path d="m65.5 184.5v-169h69v169h-69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="M14 64H65.873L100 100L65.873 136H14V64Z" fill="#fff"/>
<path d="M14 64H65.873L100 100L65.873 136H14V64Z" fill="#fff" tb:tag="pipe-background"/>
<path d="M14 64H65.873L100 100L65.873 136H14V64Z" fill="url(#paint1_linear_1245_66442)"/>
<path d="m15.5 65.5h49.728l32.705 34.5-32.705 34.5h-49.728v-69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="M136 64H134.127L100 100L134.127 136H136V64Z" fill="url(#paint2_linear_1245_66442)"/>
<path d="M134.5 65.7868L102.067 100L134.5 134.213V65.7868Z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="M186 64H134.127L100 100L134.127 136H186V64Z" fill="#fff"/>
<path d="M186 64H134.127L100 100L134.127 136H186V64Z" fill="#fff" tb:tag="pipe-background"/>
<path d="M186 64H134.127L100 100L134.127 136H186V64Z" fill="url(#paint3_linear_1245_66442)"/>
<path d="m184.5 65.5h-49.728l-32.705 34.5 32.705 34.5h49.728v-69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 59 KiB

82
application/src/main/data/json/system/scada_symbols/horizontal-ball-valve.svg

@ -37,13 +37,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.opened}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "open",
@ -52,13 +72,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": true,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": true,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "close",
@ -67,13 +106,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
}
],
"properties": [

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

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

@ -1,4 +1,5 @@
<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[{
<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": "Horizontal inline flow meter",
"description": "Horizontal inline flow meter component used to display flow related value and render various states. Includes pipe fluid and leak visualizations.",
"searchTags": [
@ -50,6 +51,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
@ -77,13 +83,32 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 0,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": 0,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "flowRate"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -92,13 +117,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -107,13 +152,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -122,13 +187,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "broken",
@ -137,13 +222,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.broken}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "displayClick",
@ -152,13 +257,16 @@
"group": "{i18n:scada.symbol.display}",
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "fluid",
@ -167,13 +275,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
@ -182,13 +310,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
@ -197,13 +345,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
@ -212,13 +380,32 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
@ -227,13 +414,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -508,10 +715,25 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<defs>
}]]></tb:metadata><defs>
<pattern id="liquid" width="172" height="72" patternTransform="translate(14,-8)" patternUnits="userSpaceOnUse">
<circle transform="rotate(-90)" cx="-15" cy="21" r="8" fill="url(#paint28_linear_1182_32781-5)"/>
<circle transform="rotate(-90)" cx="-15" cy="150" r="8" fill="url(#paint29_linear_1182_32781-1)"/>
@ -683,7 +905,7 @@
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs><rect x="14" y="64" width="372" height="72" fill="#fff"/><rect x="14" y="64" width="372" height="72" fill="url(#paint0_linear_1595_102361)" style="fill:url(#paint0_linear_1595_102361)"/><rect x="15.5" y="65.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="387.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect x="14" y="64" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="64" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="m300 100c0 55.228-44.772 100-100 100s-100-44.772-100-100c0-55.228 44.772-100 100-100s100 44.772 100 100zm-188 0c0 48.603 39.401 88.004 88.004 88.004s88.004-39.401 88.004-88.004c0-48.604-39.401-88.004-88.004-88.004s-88.004 39.401-88.004 88.004z" fill="#4a4848" mask="url(#path-5-inside-1_1595_102361)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="200" cy="100" r="88" fill="#fff" tb:tag="background"/><path d="m135.5 88.357c0-2.1302 1.727-3.8571 3.857-3.8571h121.29c2.13 0 3.857 1.7269 3.857 3.8571v23.286c0 2.13-1.727 3.857-3.857 3.857h-121.29c-2.13 0-3.857-1.727-3.857-3.857z" fill="#4a4848" fill-opacity=".06" tb:tag="border"/><path d="m139.36 83c-2.9408 0-5.3574 2.4165-5.3574 5.3574v23.285c0 2.9408 2.4166 5.3574 5.3574 5.3574h121.29c2.9408 0 5.3574-2.4166 5.3574-5.3574v-23.285c0-2.9409-2.4167-5.3574-5.3574-5.3574zm0 3h121.29c1.3192 0 2.3574 1.0379 2.3574 2.3574v23.285c0 1.3192-1.0382 2.3574-2.3574 2.3574h-121.29c-1.3192 0-2.3574-1.0382-2.3574-2.3574v-23.285c0-1.3195 1.0382-2.3574 2.3574-2.3574z" fill="#4a4848" tb:tag="border"/><circle cx="173.5" cy="59.5" r="12.5" fill="url(#paint1_linear_1595_102361)" style="fill:url(#paint1_linear_1595_102361)"/><circle cx="173.5" cy="59.5" r="12" stroke="#000" stroke-opacity=".12"/><circle cx="226.5" cy="59.5" r="12.5" fill="url(#paint2_linear_1595_102361)" style="fill:url(#paint2_linear_1595_102361)"/><circle cx="226.5" cy="59.5" r="12" stroke="#000" stroke-opacity=".12"/><text x="200.00586" y="102.19141" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:24px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="value" xml:space="preserve"><tspan>0</tspan></text><text x="199.61719" y="138.77344" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:16px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="valueUnits" xml:space="preserve"><tspan>m³/hr</tspan></text><g style="display: none;" tb:tag="broken">
</defs><rect x="14" y="64" width="372" height="72" fill="#fff" tb:tag="pipe-background"/><rect x="14" y="64" width="372" height="72" fill="url(#paint0_linear_1595_102361)" style="fill:url(#paint0_linear_1595_102361)"/><rect x="15.5" y="65.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="387.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect x="14" y="64" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="64" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="m300 100c0 55.228-44.772 100-100 100s-100-44.772-100-100c0-55.228 44.772-100 100-100s100 44.772 100 100zm-188 0c0 48.603 39.401 88.004 88.004 88.004s88.004-39.401 88.004-88.004c0-48.604-39.401-88.004-88.004-88.004s-88.004 39.401-88.004 88.004z" fill="#4a4848" mask="url(#path-5-inside-1_1595_102361)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="200" cy="100" r="88" fill="#fff" tb:tag="background"/><path d="m135.5 88.357c0-2.1302 1.727-3.8571 3.857-3.8571h121.29c2.13 0 3.857 1.7269 3.857 3.8571v23.286c0 2.13-1.727 3.857-3.857 3.857h-121.29c-2.13 0-3.857-1.727-3.857-3.857z" fill="#4a4848" fill-opacity=".06" tb:tag="border"/><path d="m139.36 83c-2.9408 0-5.3574 2.4165-5.3574 5.3574v23.285c0 2.9408 2.4166 5.3574 5.3574 5.3574h121.29c2.9408 0 5.3574-2.4166 5.3574-5.3574v-23.285c0-2.9409-2.4167-5.3574-5.3574-5.3574zm0 3h121.29c1.3192 0 2.3574 1.0379 2.3574 2.3574v23.285c0 1.3192-1.0382 2.3574-2.3574 2.3574h-121.29c-1.3192 0-2.3574-1.0382-2.3574-2.3574v-23.285c0-1.3195 1.0382-2.3574 2.3574-2.3574z" fill="#4a4848" tb:tag="border"/><circle cx="173.5" cy="59.5" r="12.5" fill="url(#paint1_linear_1595_102361)" style="fill:url(#paint1_linear_1595_102361)"/><circle cx="173.5" cy="59.5" r="12" stroke="#000" stroke-opacity=".12"/><circle cx="226.5" cy="59.5" r="12.5" fill="url(#paint2_linear_1595_102361)" style="fill:url(#paint2_linear_1595_102361)"/><circle cx="226.5" cy="59.5" r="12" stroke="#000" stroke-opacity=".12"/><text x="200.00586" y="102.19141" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:24px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="value" xml:space="preserve"><tspan>0</tspan></text><text x="199.61719" y="138.77344" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:16px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="valueUnits" xml:space="preserve"><tspan>m³/hr</tspan></text><g style="display: none;" tb:tag="broken">
<circle cx="200" cy="100" r="88" fill="#000" fill-opacity=".24" style=""/>
<path d="m253.92 84.931c-0.174 0.5228-0.309 1.1931-0.577 1.6622-0.577 1.6623-1.583 3.2037-2.991 4.1417-0.697 0.3484-1.488 0.6431-2.226 0.8439-1.971 0.6161-3.848 1.2858-5.725 1.9555-1.636 0.6832-3.513 1.3529-5.122 0.682 0.013-0.2414-0.027-0.3888-0.013-0.6302-2.977 0.4146-0.922 2.811-8.792 8.1516-11.067 2.815-27.05 6.112-29.128 6.044-0.724-0.041-1.408 0.066-2.038 0.08-1.113-0.014-2.172-0.122-3.218-0.471-0.764-0.188-1.515-0.617-2.225-0.899-2.038-0.792-4.183-1.396-6.448-1.571 0.603 0.47 1.206 0.939 1.809 1.409 0 0 0.121 0.442 0.081 0.295 0.201-0.134 0.482 0.027 0.616 0.228-0.751 0.442-1.502 0.884-2.199 1.232-3.647 2.024-7.389 3.993-11.304 5.614 3.875-0.897 7.683-2.33 11.411-4.058 0.845-0.389 1.595-0.831 2.574-1.018 2.306-0.549 4.666 0.551 6.824 1.785l0.094 0.054c0.187 0.107 0.469 0.268 0.455 0.509 0.081 0.295-0.228 0.617-0.483 0.845-1.368 1.085-2.642 2.225-3.916 3.364-1.582 1.461-2.883 3.082-4.56 4.489-1.475 1.274-3.084 2.346-4.64 3.324-0.75 0.442-1.408 0.938-2.239 1.085-0.885 0.241-1.756 0.241-2.668 0.093-1.153-0.161-2.212-0.269-3.311-0.524-6.314-1.37-17.674 5.248-23.787 2.873 2.44 1.395 5.027 2.75 7.466 4.145-3.66 0.522-7.133 2.022-10.7 3.469-0.992 0.428-1.984 0.857-3.111 1.085-0.884 0.241-1.903 0.281-2.775 0.28-1.743 0-3.526-0.148-4.973-1.101 0.831 0.724 1.769 1.261 2.667 1.65-0.255 0.228-0.563 0.55-0.818 0.778-2.146 2.01-4.198 4.074-6.29 5.99 1.14-0.469 2.199-1.232 3.165-2.05 1.274-1.139 2.226-2.587 3.768-3.324 1.743-0.87 3.808-0.561 5.806-0.789 3.164-0.307 6.154-1.834 9.131-3.12s5.228-3.036 10.264-4.758c3.63-1.241 9.543-3.131 12.411-2.486 0.912 0.148 1.864 0.443 2.762 0.832 0.617 0.228 1.18 0.55 1.836 0.926 0.751 0.429 1.408 0.805 2.212 1.14 0.469 0.268 0.992 0.443 1.475 0.47 0.576 0.08 1.22-0.174 1.716-0.388 1.783-0.724 3.567-1.447 5.35-2.17-0.295 0.08-0.737 0.2-1.032 0.281-2.36 0.642-4.666 1.191-6.972 0.869-0.483-0.027-1.006-0.202-1.327-0.51-0.094-0.054-0.188-0.107-0.134-0.201-0.362-0.456-0.214-1.368 0.322-1.435 0.282-0.71 1.046-1.394 1.556-1.849 4.157-3.351 8.408-6.647 12.606-9.85 0.456-0.362 0.912-0.724 1.555-0.978 0.644-0.255 1.18-0.322 1.81-0.335 4.572-0.374 19.939-3.129 23.095-4.335 5.049-1.929 5.348-0.685 7.45-3.208 0.335 0.067 7.054-8.7036 7.39-8.6365 2.252 0.4164 4.343 1.9858 6.099 3.4875 1.45 1.565-2.243 6.524-2.272 7.287-0.032 0.867-18.866 17.201-20.503 18.31-0.751 0.442-2.24 0.274-2.533 0.546-0.103 0.096-0.557 1.991 0.264 0.492-0.764-0.029-8.29 2.174-13.599 1.877-0.482-0.027-1.059-0.108-1.582-0.282-0.335-0.067-0.616-0.228-0.844-0.483-0.094-0.054-0.134-0.201-0.174-0.349-0.04-0.147 0.107-0.188 0.161-0.281 2.803-3.378 6.276-6.621 7.203-11.072-0.201 0.134-0.255 0.228-0.456 0.362-0.054 0.094-0.201 0.134-0.308 0.321-1.744 1.743-3.085 4.089-5.003 5.482-0.858 0.63-1.81 1.206-2.441 2.091-0.791 1.166-1.033 2.895-2.052 3.807-0.51 0.455-1.207 0.804-1.797 0.964-1.623 0.442-3.419 0.535-5.001 1.125-0.792 0.295-1.596 0.831-2.387 1.125-0.148 0.04-0.201 0.134-0.349 0.174-1.783 0.724-7.731 4.384-9.661 4.276 1.689 0.095 7.288-3.392 8.963-3.056-1.797 1.836-3.54 3.579-5.525 5.307 1.904-1.152 3.674-2.505 5.311-4.06 0.818-0.777 1.636-1.555 2.588-2.131 1.85-1.058 4.451-0.816 5.51 1.034 0.134 0.202 0.268 0.403 0.254 0.644 0.415 1.234 0.241 2.628 0.12 3.928-0.551 3.794-1.236 7.387-2.029 11.168-0.591 2.775-1.275 5.496-2.764 7.882-0.108 0.188-0.309 0.322-0.496 0.214 0.442 0.751 0.106 1.555-0.417 2.252-0.429 0.751-1.046 1.394-1.569 2.091-0.47 0.604-0.939 1.207-1.409 1.81-2.239 2.828-4.573 5.602-7.135 8.122 1.556-0.978 3.031-2.251 4.184-3.833 0.886-1.112 1.583-2.332 2.509-3.297 0.979-1.059 2.226-1.715 3.205-2.774 0.416-0.51 0.885-1.113 1.395-1.568 0.066 1.407 0.039 2.761 0.106 4.169 0.201-1.005 0.403-2.011 0.604-3.016 0.215-1.247 0.524-2.44 0.832-3.633 0.94-2.949 2.885-5.697 3.395-8.767 0.014-0.241 0.027-0.483 0.041-0.724-0.201 0.134-0.309 0.322-0.51 0.456-0.04-0.148 0.067-0.336 0.027-0.483 0.055-2.708 0.646-5.483 1.371-8.057 0.563-2.293 1.235-4.773 2.965-6.274 0.509-0.455 1.113-0.857 1.622-1.313 0.51-0.456 0.845-1.26 0.738-1.944 3.178 0.323 6.66 1.265 9.329-0.571 2.065-1.434 4.621-0.818 5.075-2.713-0.792 0.735 1.103 1.189 2.659 0.483s23.915-14.808 24.838-26.835c-0.911-1.8908-2.519-3.4332-4.316-4.2114-0.094-0.0537-0.094-0.0537-0.187-0.1073 2.923-1.1923 5.993-2.4246 8.796-4.0593 0.805-0.536 1.703-1.0184 2.28-1.8092 0.671-1.6086 0.832-3.633 0.94-5.5635z" fill="#5c5a5a" style=""/>
<path d="m221.07 127.62c-1.005-0.202-1.526-0.498-2.558-0.217-2.065 0.562-2.591 1.325-4.575 2.183-2.226 0.844-3.364 0.839-5.79 0.946-1.261 0.026-2.467-0.041-3.62-0.203 0.187 0.979-1.8 2.31-2.564 2.993-0.764 0.684-1.717 1.26-2.106 2.158-0.442 0.992-0.309 2.065-0.416 3.124-0.162 1.153-0.658 2.238-1.061 3.378-0.711 2.332-0.605 4.759-0.351 7.146 0.054-0.094 0.054-0.094 0.108-0.188 0.054-2.708 0.645-5.483 1.37-8.057 0.564-2.293 1.235-4.773 2.965-6.274 0.51-0.455 1.113-0.857 1.623-1.313 0.509-0.456 2.054-1.666 1.946-2.349 3.178 0.322 5.388 1.065 8.056-0.771 2.066-1.434 2.736-1.244 5.15-1.98 0.402-0.268 1.14-0.469 1.823-0.576z" fill="#8b8b8b" style=""/>

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 76 KiB

186
application/src/main/data/json/system/scada_symbols/horizontal-pipe.svg

@ -23,6 +23,11 @@
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
@ -30,71 +35,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -113,12 +222,37 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><defs>
<linearGradient id="paint0_linear_955_27716" x1="48.475" x2="48.475" y1="136" y2="64" gradientTransform="translate(-.50002)" gradientUnits="userSpaceOnUse">
}]]></tb:metadata>
<path d="m186 136h-172v-72h172z" fill="#fff" tb:tag="pipe-background"/><path d="m186 136h-172v-72h172z" fill="url(#paint0_linear_955_27716)"/><g stroke-width="3">
<path d="m184.5 134.5h-169v-69h169z" stroke="#000" stroke-opacity=".12"/>
<rect transform="scale(-1)" x="-198.5" y="-148.5" width="11.286" height="97" rx="5.6429" fill="#d9d9d9" stroke="#727171"/>
<rect transform="scale(-1)" x="-12.5" y="-148.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171"/>
</g><defs>
<linearGradient id="paint0_linear_955_27716" x1="48.475" x2="48.475" y1="136" y2="64" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#fff" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<pattern id="liquid" width="172" height="72" patternTransform="translate(14,-8)" patternUnits="userSpaceOnUse">
@ -272,11 +406,7 @@
<stop stop-color="#727171" offset=".89138"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs><path d="m186 136h-172v-72h172z" fill="url(#paint0_linear_955_27716)"/><g stroke-width="3">
<path d="m184.5 134.5h-169v-69h169z" stroke="#000" stroke-opacity=".12"/>
<rect transform="scale(-1)" x="-198.5" y="-148.5" width="11.286" height="97" rx="5.6429" fill="#d9d9d9" stroke="#727171"/>
<rect transform="scale(-1)" x="-12.5" y="-148.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171"/>
</g><rect x="14" y="64" width="172" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="64" width="172" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><g transform="translate(5.0806 -.22515)" style="display: none;" tb:tag="leak">
</defs><rect x="14" y="64" width="172" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="64" width="172" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><g transform="translate(5.0806 -.22515)" style="display: none;" tb:tag="leak">
<path d="m34.253 100.01c0.5162 0.38 1.0932 0.926 1.6625 1.191 1.6625 1.191 3.6513 1.98 5.617 1.926 0.8955-0.136 1.8442-0.387 2.6789-0.691 2.2843-0.737 4.5155-1.36 6.7467-1.983 2.0111-0.448 4.2423-1.0713 5.3196-2.7869-0.1747-0.2201-0.2354-0.387-0.41-0.6071 3.1037-1.6023 5.7289-4.2595 7.671-7.2353 1.3504-2.004 2.3515-4.4482 4.2789-5.906 0.6601-0.5239 1.381-0.8808 1.9881-1.2909 1.0471-0.7592 1.9804-1.5716 2.7391-2.604 0.5994-0.6909 1.0241-1.6018 1.5096-2.3457 1.4035-2.1179 3.0347-4.1297 5.0683-5.8152-0.2579 0.8501-0.5156 1.7002-0.7734 2.5504 0 0 0.1823 0.5008 0.1215 0.3339-0.2808 0.0077-0.4401 0.3492-0.4324 0.63 1.0094-0.0837 2.0188-0.1675 2.9143-0.3044 4.8193-0.5251 9.6917-1.1641 14.496-2.2508-4.2801 1.7469-8.8563 2.9398-13.554 3.7988-1.0625 0.1977-2.0719 0.2815-3.1267 0.7599-2.5575 1.0254-4.0594 3.6519-5.2806 6.2707l-0.0531 0.1138c-0.1062 0.2278-0.2655 0.5694-0.0909 0.7894 0.1216 0.3339 0.6301 0.4324 1.0248 0.4778 2.0265 0.1133 3.9999 0.3404 5.9733 0.5675 2.4819 0.3256 4.8045 0.9928 7.3396 1.2046 2.2542 0.2194 4.5007 0.1581 6.6334 0.0436 1.0094-0.0838 1.9657-0.0537 2.8536-0.4714 1.0017-0.3646 1.8288-0.9493 2.5952-1.7009 0.9863-0.9262 1.9193-1.7386 2.7923-2.7179 5.076-5.5344 20.298-6.8695 24.508-13.223-1.38 2.9604-2.928 5.9816-4.308 8.942 3.824-1.9593 8.128-2.8637 12.484-3.882 1.229-0.2585 2.459-0.5169 3.68-1.0561 1.002-0.3646 1.996-1.01 2.823-1.5947 1.655-1.1693 3.248-2.5055 3.984-4.3804-0.304 1.2448-0.834 2.3834-1.426 3.3551 0.394 0.0454 0.903 0.1439 1.298 0.1893 3.385 0.4696 6.717 1.053 9.988 1.4695-1.396 0.3192-2.914 0.3044-4.379 0.1758-1.973-0.2271-3.848-0.9627-5.806-0.6282-2.239 0.3422-3.992 2.02-6.041 3.1439-3.21 1.8301-7.073 2.3853-10.761 3.1605-3.688 0.7753-6.999 0.6235-12.935 2.3663-4.278 1.256-11.158 3.4266-13.449 5.9623-0.767 0.7516-1.472 1.6701-2.064 2.6418-0.4326 0.6301-0.7511 1.3132-1.1228 2.1103-0.4247 0.9108-0.7964 1.7079-1.335 2.5657-0.2655 0.5693-0.6448 1.0855-1.0849 1.4348-0.4932 0.4631-1.2749 0.6531-1.8896 0.7823-2.1781 0.5091-4.3562 1.0183-6.5343 1.5274 0.3339-0.1215 0.8348-0.3038 1.1687-0.4254 2.6713-0.9722 5.2287-1.9976 7.2015-3.8501 0.4401-0.3492 0.8194-0.8654 0.9179-1.374 0.0531-0.1138 0.1062-0.2277-0.0076-0.2808 0.0377-0.6755-0.7139-1.4418-1.2679-1.1457-0.7439-0.4855-1.9279-0.6217-2.7173-0.7126-6.1933-0.3928-12.44-0.6718-18.572-0.8977-0.6755-0.0377-1.351-0.0755-2.1326 0.1145-0.7817 0.19-1.3357 0.4861-1.9427 0.8961-4.591 2.7109-9.3413 5.7634-12.482 10.121-0.0531 0.1139-0.1062 0.2277-0.1593 0.3416 0 0 0.1138 0.0531 0.2277 0.1062-0.2731 0.288-0.607 0.41-0.8802 0.698-1.8589 1.906-2.7916 4.798-3.4511 7.401-0.3281 2.459-0.3109 3.65 0.2287 4.356 0.6125 0.801 2.6907 3.528 4.9877 3.483 1.0094-0.084 2.3108-1.242 2.7709-1.18 0.1625 0.022 1.8643 1.516 0.0794 0.644 0.7057-0.539 9.3274-3.495 14.168-7.337 0.4401-0.35 0.9333-0.813 1.3126-1.329 0.2732-0.288 0.4325-0.63 0.4779-1.025 0.0531-0.113-0.0077-0.28-0.0684-0.447-0.0608-0.167-0.2278-0.107-0.3416-0.16-4.9261-1.326-10.398-2.076-14.263-5.6801 0.2809-0.0077 0.3947 0.0454 0.6755 0.0377 0.1139 0.0531 0.2808-0.0077 0.5086 0.0985 2.8235 0.4849 5.67 1.8119 8.4251 1.8499 1.2371 0.022 2.5273-0.07 3.719 0.348 1.5333 0.576 2.9227 2.056 4.5014 2.237 0.7894 0.091 1.6849-0.046 2.3527-0.289 1.8365-0.668 3.6046-1.785 5.5019-2.286 0.9486-0.251 2.0719-0.281 3.0205-0.532 0.167-0.061 0.2809-8e-3 0.4478-0.069 2.1781-0.509 10.278-1.0219 12.04-2.4189-1.541 1.2224-9.1936 1.667-10.559 3.1099 2.9374 0.538 5.7609 1.023 8.8043 1.333-2.58 0.183-5.1684 0.085-7.7642-0.294-1.2979-0.189-2.5958-0.379-3.886-0.287-2.4666 0.236-4.7733 2.21-4.5373 4.676 0.0077 0.281 0.0154 0.562 0.19 0.782 0.433 1.45 1.5339 2.656 2.5209 3.809 3.0672 3.233 6.1267 6.184 9.414 9.242 2.4216 2.238 4.8966 4.363 7.9106 5.629 0.227 0.106 0.508 0.099 0.614-0.129 0.084 1.009 0.942 1.548 1.906 1.859 0.911 0.425 1.928 0.622 2.892 0.932l2.55 0.774c4.023 1.183 8.099 2.252 12.22 2.926-2.132 0.114-4.386-0.105-6.542-0.833-1.587-0.463-3.067-1.153-4.592-1.448-1.64-0.349-3.264-0.136-4.903-0.485-0.737-0.204-1.587-0.462-2.376-0.553 0.881 1.381 1.815 2.648 2.695 4.029-0.865-0.819-1.731-1.638-2.596-2.458-1.04-1.039-2.133-1.965-3.227-2.891-2.869-2.169-6.5581-3.474-9.1014-6.046-0.1746-0.22-0.3493-0.44-0.5239-0.66 0.2808-8e-3 0.5086 0.098 0.7894 0.091-0.0608-0.167-0.2885-0.273-0.3493-0.44-1.8678-2.535-4.2896-4.773-6.7037-6.73-2.0725-1.799-4.3727-3.703-7.0216-3.968-0.7894-0.091-1.6318-0.068-2.4212-0.158-0.7894-0.091-1.6471-0.63-2.0041-1.351-2.7999 2.437-5.4747 5.667-9.2392 5.713-2.922 0.024-4.9354 2.323-6.6372 0.828 1.2453 0.166-0.2492 1.868-2.2002 2.242-1.951 0.373-7.5425-5.058-8.0062-7.631-0.403-2.406 0.0896-4.949 1.273-6.892l0.1062-0.228c-3.5745 0.828-7.316 1.718-11.073 2.045-1.1233 0.031-2.2996 0.175-3.3775-0.189-1.7156-1.077-3.2264-2.89-4.6235-4.651z" fill="#5c5a5a" style=""/>
<g fill="#8b8b8b" style="">
<path d="m60.493 117.31c0.8195-0.865 1.1148-1.496 2.2835-1.921 2.3374-0.851 3.3482-0.479 5.8071-0.996 2.679-0.691 3.756-1.458 6.1312-2.985 1.2141-0.82 2.3143-1.693 3.3007-2.619 0.4785 1.055 3.2567 0.986 4.4408 1.122 1.184 0.136 2.4742 0.045 3.4459 0.637 1.0855 0.644 1.6778 1.752 2.4902 2.686 0.9262 0.986 2.1256 1.684 3.2719 2.496 2.2395 1.737 3.7657 4.112 5.125 6.548-0.1139-0.053-0.1139-0.053-0.2277-0.106-1.8678-2.535-4.2896-4.773-6.7037-6.73-2.0726-1.798-4.3728-3.703-7.0217-3.968-0.7893-0.09-1.6318-0.067-2.4211-0.158-0.7894-0.091-3.0668-0.204-3.4237-0.925-2.7999 2.437-4.4002 4.623-8.1647 4.67-2.9221 0.023-3.4312 0.654-6.2164 1.573-0.5616 0.016-1.3964 0.319-2.1173 0.676z" style=""/>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

20
application/src/main/data/json/system/scada_symbols/horizontal-tank-with-screen.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

82
application/src/main/data/json/system/scada_symbols/horizontal-wheel-valve.svg

@ -37,13 +37,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.opened}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "open",
@ -52,13 +72,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": true,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": true,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "close",
@ -67,13 +106,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
}
],
"properties": [

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

171
application/src/main/data/json/system/scada_symbols/left-bottom-elbow-pipe.svg

@ -29,6 +29,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "vertical-fluid",
"stateRenderFunction": "var fluid = ctx.values.fluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}\n",
@ -40,71 +45,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -123,19 +232,35 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<g clip-path="url(#clip0_1245_66459)">
<rect x="14" y="64" width="50" height="72" fill="#fff"/>
<rect x="14" y="64" width="50" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="14" y="64" width="50" height="72" fill="url(#paint0_linear_1245_66459)" style="fill:url(#paint0_linear_1245_66459)"/>
<rect x="15.5" y="65.5" width="47" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m64 186v-50h72v50z" fill="#fff"/>
<path d="m64 186v-50h72v50z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-50h72v50z" fill="url(#paint1_linear_1245_66459)" style="fill:url(#paint1_linear_1245_66459)"/>
<path d="m65.5 184.5v-47h69v47z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(-90,51.5,198.5)" x="51.5" y="198.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="url(#paint2_linear_1245_66459)" style="fill:url(#paint2_linear_1245_66459)"/>
<path d="m65.5 134.5v-68.865c0.8334 0.0861 1.9717 0.2211 3.3584 0.4273 3.1196 0.464 7.4889 1.2873 12.47 2.7228 9.9828 2.8767 22.316 8.1809 32.01 17.875 9.695 9.6942 14.999 22.027 17.875 32.01 1.436 4.982 2.259 9.351 2.723 12.471 0.206 1.386 0.341 2.525 0.428 3.358z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
</g><defs>

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 51 KiB

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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

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

@ -0,0 +1,181 @@
<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",
"searchTags": [
"pipe",
"drain"
],
"widgetSizeX": 2,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": "var color = ctx.properties.fluidColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"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",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"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
}
],
"properties": [
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#8CAA5C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<g tb:tag="clickArea">
<rect x="100" y="64" width="164" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="100" y="64" width="164" height="72" fill="url(#paint0_linear_1924_305496)"/>
<rect x="101.5" y="65.5" width="161" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m264 64v-50h72v50h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m264 64v-50h72v50h-72z" fill="url(#paint1_linear_1924_305496)"/>
<path d="m265.5 62.5v-47h69v47h-69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect transform="rotate(-90 251.5 12.5)" x="251.5" y="12.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<path d="m336 64s-1.718 30.518-21.6 50.4c-19.882 19.882-50.4 21.6-50.4 21.6v-72h72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m336 64s-1.718 30.518-21.6 50.4c-19.882 19.882-50.4 21.6-50.4 21.6v-72h72z" fill="url(#paint2_linear_1924_305496)"/>
<path d="m265.5 65.5h68.865c-0.087 0.8334-0.222 1.9717-0.428 3.3584-0.464 3.1196-1.287 7.4889-2.723 12.47-2.876 9.9828-8.18 22.316-17.875 32.01-9.694 9.695-22.027 14.999-32.01 17.875-4.982 1.436-9.351 2.259-12.471 2.723-1.386 0.206-2.525 0.341-3.358 0.428v-68.865z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<ellipse transform="rotate(-90 100 100)" cx="100" cy="100" rx="36" ry="4" fill="#5D5C5C"/>
<path d="m76.285 94.5c4-9.2 20-22.333 24.001-24.5 0 0 1.499 18.101 1.499 30.5 0 12.399-1.499 33-1.499 33 0 6.5 1.5 9 3.999 14.5 5.089 11.196 3.5 10.5 12.5 13.5s24 2.5 21.5 11.5-32-8-34-4 16 17 10.5 21.5-17.5-10.5-21.5-10.5-4.5 10.5-10 10.5-7-11.5-16-10.5-12 13-18 17-14-2-14.5-11 20.5-7 20.5-13-39 1.5-45.5-4c-6.5-5.5-0.50001-6.5 6.5-10.5 7-4 38 5.5 39 0s-16.5 0-15-5.5 16.5-2 23-10.5 8-36.5 13-48z" fill="#8CAA5C" tb:tag="fluid-background"/>
</g>
<g style="display:none" tb:tag="leak">
<path d="m246.99 88.724c-0.447 0.4587-0.927 1.092-1.445 1.447-1.445 1.447-3.278 2.5504-5.226 2.8189-0.906 0.0114-1.883-0.0808-2.756-0.244-2.374-0.3533-4.677-0.603-6.98-0.8527-2.058-0.1135-4.361-0.3632-5.704-1.8795 0.136-0.2456 0.169-0.4203 0.305-0.6659-3.324-1.0731-6.348-3.2652-8.751-5.8832-1.66-1.7562-3.047-4.0038-5.187-5.1267-0.737-0.4088-1.507-0.6431-2.173-0.9483-1.157-0.5778-2.21-1.2265-3.128-2.121-0.704-0.5835-1.272-1.4126-1.873-2.0672-1.731-1.8598-3.669-3.5777-5.951-4.9079 0.394 0.7965 0.787 1.593 1.18 2.3895 0 0-0.098 0.524-0.065 0.3493 0.278-0.0384 0.491 0.2726 0.53 0.5509-1.01 0.0824-2.019 0.1648-2.925 0.1763-4.841 0.2702-9.752 0.4367-14.669 0.1503 4.508 1.0234 9.218 1.4518 13.993 1.5309 1.081 0.0212 2.09-0.0613 3.209 0.2382 2.691 0.5933 4.602 2.9388 6.235 5.3226l0.071 0.1036c0.142 0.2073 0.355 0.5182 0.219 0.7639-0.066 0.3493-0.551 0.5296-0.933 0.639-1.981 0.4432-3.89 0.99-5.8 1.5368-2.395 0.7272-4.578 1.7653-7.044 2.3888-2.188 0.5852-4.414 0.8921-6.537 1.1279-1.009 0.0825-1.948 0.2686-2.892 0.0017-1.048-0.1959-1.96-0.6373-2.838-1.2535-1.125-0.7524-2.179-1.4012-3.2-2.2247-5.912-4.6296-21.148-3.4571-26.341-9.0368 1.846 2.6948 3.867 5.4222 5.713 8.1169-4.093-1.3073-8.487-1.4958-12.951-1.7879-1.255-0.0539-2.51-0.1077-3.803-0.4398-1.048-0.1959-2.135-0.67-3.046-1.1115-1.823-0.883-3.614-1.9407-4.646-3.67 0.502 1.1785 1.212 2.2149 1.955 3.0767-0.382 0.1094-0.867 0.2897-1.249 0.3991-3.263 1.0169-6.455 2.1374-9.614 3.0833 1.43 0.0865 2.925-0.1763 4.349-0.5427 1.91-0.5468 3.639-1.5792 5.625-1.5695 2.265-0.0285 4.269 1.34 6.474 2.1137 3.466 1.2804 7.368 1.1963 11.133 1.3578 3.766 0.1616 7.007-0.5296 13.148 0.2188 4.426 0.5394 11.568 1.5556 14.243 3.6823 0.879 0.6161 1.726 1.4069 2.468 2.2687 0.53 0.5509 0.956 1.1728 1.453 1.8983 0.568 0.8291 1.065 1.5546 1.737 2.3128 0.355 0.5182 0.813 0.9654 1.305 1.238 0.562 0.3762 1.364 0.4358 1.992 0.4627 2.232 0.1461 4.464 0.2921 6.696 0.4382-0.349-0.0653-0.873-0.1632-1.223-0.2285-2.794-0.5223-5.485-1.1156-7.734-2.6204-0.491-0.2726-0.95-0.7198-1.13-1.2054-0.071-0.1037-0.142-0.2073-0.039-0.2783-0.147-0.6602 0.469-1.5392 1.064-1.3376 0.654-0.6007 1.8-0.9287 2.564-1.1475 6.046-1.4005 12.162-2.6973 18.175-3.9232 0.661-0.1477 1.321-0.2954 2.123-0.2359 0.802 0.0596 1.397 0.2612 2.063 0.5664 4.973 1.9235 10.158 4.1579 13.969 7.9429 0.071 0.1037 0.142 0.2073 0.213 0.311 0 0-0.103 0.071-0.207 0.142 0.317 0.2399 0.666 0.3052 0.983 0.5451 2.145 1.5759 3.538 4.2764 4.615 6.7369 0.726 2.3722 0.904 3.5502 0.487 4.3342-0.474 0.891-2.078 3.92-4.351 4.252-1.01 0.082-2.483-0.847-2.927-0.711-0.157 0.048-1.591 1.801 0.027 0.649-0.784-0.417-9.773-1.923-15.178-4.922-0.491-0.272-1.053-0.649-1.512-1.096-0.317-0.24-0.53-0.551-0.639-0.933-0.071-0.103-0.038-0.278-6e-3 -0.453 0.033-0.174 0.208-0.142 0.311-0.213 4.643-2.1141 9.919-3.7488 13.142-7.9363-0.278 0.0384-0.382 0.1094-0.66 0.1477-0.104 0.0711-0.279 0.0384-0.486 0.1804-2.706 0.9402-5.297 2.7153-8.009 3.2025-1.217 0.2244-2.505 0.3452-3.612 0.9516-1.418 0.8194-2.547 2.5063-4.075 2.9441-0.764 0.218-1.67 0.23-2.368 0.099-1.921-0.3588-3.848-1.1708-5.802-1.3552-0.977-0.0923-2.09 0.0612-3.067-0.031-0.174-0.0327-0.278 0.0384-0.453 0.0057-2.232-0.146-10.307 0.6727-12.272-0.4176 1.719 0.954 9.342 0.141 10.925 1.3406-2.81 1.0115-5.516 1.9515-8.468 2.7555 2.576-0.242 5.113-0.762 7.612-1.56 1.249-0.399 2.499-0.798 3.786-0.919 2.472-0.17 5.071 1.4 5.242 3.872 0.038 0.278 0.076 0.556-0.06 0.802-0.19 1.501-1.079 2.871-1.864 4.17-2.497 3.691-5.033 7.103-7.776 10.657-2.023 2.604-4.117 5.105-6.883 6.847-0.207 0.142-0.485 0.181-0.627-0.027 0.082 1.01-0.676 1.681-1.576 2.146-0.829 0.568-1.8 0.929-2.7 1.393-0.797 0.393-1.593 0.787-2.39 1.18-3.775 1.825-7.622 3.546-11.577 4.885 2.123-0.236 4.31-0.821 6.318-1.891 1.489-0.716 2.837-1.639 4.294-2.18 1.56-0.612 3.197-0.668 4.757-1.28 0.693-0.322 1.49-0.716 2.254-0.934-0.643 1.506-1.357 2.909-2.001 4.415 0.72-0.949 1.44-1.899 2.16-2.849 0.856-1.196 1.783-2.288 2.71-3.38 2.476-2.61 5.902-4.5 7.99-7.453 0.137-0.246 0.273-0.492 0.409-0.737-0.278 0.038-0.485 0.18-0.764 0.218 0.033-0.174 0.24-0.316 0.273-0.491 1.428-2.806 3.451-5.41 5.513-7.736 1.75-2.113 3.708-4.368 6.278-5.062 0.764-0.219 1.599-0.334 2.362-0.553 0.764-0.218 1.523-0.89 1.757-1.66 3.161 1.947 6.328 4.695 10.049 4.125 2.887-0.454 5.249 1.484 6.683-0.268-1.201 0.367 0.552 1.802 2.537 1.851 1.986 0.049 6.614-6.224 6.651-8.838 4e-3 -2.439-0.898-4.8669-2.383-6.5905-0.071-0.1037-0.071-0.1037-0.142-0.2073 3.661 0.2325 7.498 0.4977 11.258 0.2063 1.113-0.1534 2.297-0.2032 3.301-0.7386 1.516-1.3433 2.71-3.3795 3.8-5.3447z" fill="#5C5A5A"/>
<path d="m223.93 110.09c-0.95-0.72-1.344-1.294-2.567-1.522-2.445-0.457-3.381 0.075-5.892-0.033-2.756-0.244-3.944-0.825-6.537-1.942-1.331-0.61-2.56-1.292-3.684-2.044-0.3 1.119-3.052 1.505-4.198 1.833-1.145 0.328-2.433 0.449-3.295 1.192-0.966 0.813-1.369 2.003-2.017 3.057-0.753 1.124-1.822 2.009-2.82 2.997-1.925 2.081-3.042 4.673-3.985 7.299 0.104-0.071 0.104-0.071 0.207-0.142 1.428-2.806 3.452-5.41 5.513-7.736 1.75-2.113 3.708-4.368 6.278-5.062 0.764-0.219 1.599-0.334 2.363-0.553s2.992-0.703 3.226-1.472c3.161 1.946 5.097 3.841 8.819 3.271 2.886-0.454 3.492 0.085 6.39 0.536 0.556-0.077 1.429 0.086 2.199 0.321z" fill="#8B8B8B"/>
<path d="m218.39 92.781c-0.279 0.0384-0.382 0.1094-0.66 0.1477-0.104 0.071-0.279 0.0384-0.486 0.1804l-0.104 0.071c-0.654 0.6007-1.309 1.2013-2.209 1.6657-0.622 0.426-1.418 0.8194-2.111 1.1418-2.979 1.4314-6.384 2.241-9.04 4.3651 0.497 0.726 0.994 1.451 1.627 1.931-0.316-0.24-0.529-0.551-0.639-0.933-0.071-0.103-0.038-0.278-5e-3 -0.453 0.032-0.174 0.207-0.142 0.31-0.213 4.889-1.9779 10.094-3.7162 13.317-7.9037z" fill="#8B8B8B"/>
<path d="m196 101.87c-0.568-0.829-1.518-1.548-2.533-1.9189-1.015-0.3705-2.27-0.4244-3.389-0.7239-0.175-0.0326-0.278 0.0384-0.453 0.0057-2.232-0.146-9.367 0.6164-11.332-0.4739 1.719 0.954 8.401 0.1973 9.985 1.397-2.81 1.011-5.788 1.87-8.74 2.674 2.576-0.242 5.385-0.68 7.884-1.478 1.249-0.4 2.498-0.799 3.786-0.919 2.472-0.171 5.071 1.399 5.241 3.871 0.039 0.278 0.077 0.557-0.059 0.802 0.201-0.595 0.299-1.119 0.223-1.675-6e-3 -0.453-0.258-1.042-0.613-1.561z" fill="#8B8B8B"/>
<path d="m190.09 120.87c-0.944-0.267-2.123 0.236-2.674 0.766-0.998 0.988-1.925 2.08-2.955 3.243-0.895 0.917-1.718 1.938-2.684 2.752-2.449 1.982-5.466 3.135-8.489 3.836 0 0-0.103 0.071-0.174-0.033-3.776 1.825-7.622 3.546-11.578 4.885 2.123-0.235 4.311-0.821 6.319-1.891 1.489-0.716 2.836-1.639 4.293-2.18 1.56-0.612 3.198-0.668 4.758-1.28 0.693-0.322 1.489-0.716 2.253-0.934-0.643 1.506-1.357 2.909-2 4.416 0.72-0.95 1.44-1.9 2.159-2.85 0.856-1.196 1.784-2.288 2.711-3.38 2.721-2.473 5.973-4.396 8.061-7.35z" fill="#8B8B8B"/>
<path d="m246.99 88.724c-0.447 0.4586-0.927 1.0919-1.445 1.4469-0.72 0.95-1.407 1.7253-2.514 2.3317-1.315 0.7484-2.843 1.1858-4.234 1.3776-2.505 0.3452-4.982 0.0629-7.46-0.2194-1.152-0.1249-2.336-0.0751-3.269 0.5639 0.175 0.0327 0.175 0.0327 0.35 0.0653-0.071-0.1037-0.071-0.1036-0.142-0.2073 3.662 0.2326 7.498 0.4977 11.258 0.2063 1.113-0.1534 2.297-0.2032 3.301-0.7386 1.871-0.8251 3.065-2.8613 4.155-4.8264z" fill="#8B8B8B"/>
<path d="m221.74 89.88c-0.775-0.6872-1.79-1.0577-2.566-1.7448-0.879-0.6162-1.55-1.3743-2.254-1.9578-1.442-0.9924-3.085-1.3898-4.455-2.2785-0.808-0.5125-1.687-1.1287-2.598-1.5702-1.681-0.6757-3.662-0.2325-5.365 0.1723-6.427 1.5098-12.751 2.9487-19.179 4.4586-1.042 0.2571-2.395 0.7271-2.52 1.8787 6e-3 0.4529 0.186 0.9385 0.399 1.2494-0.071-0.1036-0.142-0.2073-0.038-0.2783-0.148-0.6602 0.468-1.5391 1.063-1.3376 0.655-0.6006 1.801-0.9287 2.564-1.1475 6.046-1.4005 12.163-2.6973 18.176-3.9232 0.66-0.1477 1.32-0.2954 2.123-0.2358 0.802 0.0595 1.397 0.2611 2.063 0.5663 4.972 1.9235 10.158 4.1579 13.969 7.943 0.071 0.1036 0.142 0.2072 0.213 0.3109 0 0-0.104 0.071-0.207 0.142 0.316 0.2399 0.666 0.3052 0.982 0.5451-0.71-1.0364-1.42-2.0728-2.37-2.7926z" fill="#8B8B8B"/>
<path d="m202.09 73.644c-1.305-1.238-3.226-1.597-5.044-2.0271-0.174-0.0326-0.174-0.0326-0.349-0.0653-4.84 0.2702-9.752 0.4367-14.669 0.1503 4.509 1.0234 9.218 1.4518 13.993 1.5309 1.081 0.0212 2.09-0.0612 3.209 0.2382 2.691 0.5933 4.602 2.9388 6.235 5.3226-0.18-0.4856-0.606-1.1074-0.748-1.3147-0.426-0.6219-0.781-1.1401-1.207-1.762-0.29-0.8675-0.716-1.4894-1.42-2.0729z" fill="#8B8B8B"/>
<path d="m192.15 94.682c-0.349-0.0653-0.873-0.1632-1.222-0.2285-0.104 0.071-0.279 0.0383-0.279 0.0383-1.08-0.0212-2.161-0.0424-3.138-0.1346-1.921-0.359-3.262-0.8768-4.534-2.2895-0.672-0.7581-1.382-2.5157-1.95-3.3449-1.633-2.3838-4.364-3.5327-6.581-5.2122-1.971-1.5433-8.279-0.7511-11.049-1.2173-2.771-0.4661-3.371-0.2823-5.407-0.3364-3.263-0.0867-3.209-0.2383-4.814-0.3574-2.128-0.217-4.055-1.029-6.183-1.2461-1.43-0.0865-2.925 0.1763-4.251 0.0188-2.232-0.146-4.269-1.34-6.299-2.081-1.119-0.2995-2.238-0.599-3.318-0.6202-3.263 1.0169-6.455 2.1375-9.614 3.0834 1.43 0.0865 2.925-0.1763 4.349-0.5428 1.91-0.5468 3.639-1.5792 5.625-1.5694 2.265-0.0286 4.269 1.34 6.474 2.1136 3.466 1.2804 7.368 1.1963 11.133 1.3579 3.766 0.1615 6.58-0.174 9.741 1.7724 2.986 1.9137 13.062 0.1541 15.738 2.2808 0.879 0.6162 1.725 1.407 2.468 2.2688 0.529 0.5509 0.955 1.1727 1.452 1.8982 0.568 0.8292 1.065 1.5547 1.737 2.3128 0.355 0.5183 0.814 0.9655 1.305 1.238 0.562 0.3763 1.364 0.4358 1.992 0.4628 2.232 0.146 4.464 0.2921 6.625 0.3345z" fill="#8B8B8B"/>
</g>
<defs>
<linearGradient id="paint0_linear_1924_305496" x1="142.64" x2="142.44" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_1924_305496" x1="264" x2="336" y1="51" y2="51.654" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint2_linear_1924_305496" x1="311" x2="263.66" y1="114" y2="64.321" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".35637"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".4903"/>
<stop stop-color="#fff" stop-opacity="0" offset=".56651"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".63374"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".75781"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

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

@ -1,4 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="400" fill="none" version="1.1" viewBox="0 0 400 400"><tb:metadata xmlns=""><![CDATA[{
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="400" fill="none" version="1.1" viewBox="0 0 400 400">
<tb:metadata xmlns=""><![CDATA[{
"title": "Left flow meter",
"description": "Left flow meter component used to display flow related value and render various states. Includes pipe fluid and leak visualizations.",
"searchTags": [
@ -50,6 +51,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
@ -77,13 +83,32 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 0,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": 0,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "flowRate"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -92,13 +117,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -107,13 +152,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -122,13 +187,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "broken",
@ -137,13 +222,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.broken}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "displayClick",
@ -152,13 +257,16 @@
"group": "{i18n:scada.symbol.display}",
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "fluid",
@ -167,13 +275,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
@ -182,13 +310,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
@ -197,13 +345,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
@ -212,13 +380,32 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
@ -227,13 +414,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -508,10 +715,25 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<mask id="path-7-inside-1_1253_89545" fill="white">
}]]></tb:metadata><mask id="path-7-inside-1_1253_89545" fill="white">
<path d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z"/>
</mask><defs>
<linearGradient id="paint2_linear_1253_89545" x1="155.2" x2="177.6" y1="61" y2="85.4" gradientTransform="translate(-75,75)" gradientUnits="userSpaceOnUse">
@ -697,7 +919,7 @@
<circle transform="rotate(-90)" cx="-58.5" cy="95.5" r="9.5" fill="url(#paint55_linear_1182_32781-8)"/>
<path d="m0 0h172v72h-172z" fill="url(#paint84_linear_1182_32781-8)" stroke-width=".57735"/>
</pattern>
</defs><rect transform="rotate(-90)" x="-386" y="264" width="372" height="72" fill="#fff"/><rect transform="rotate(-90)" x="-386" y="264" width="372" height="72" fill="url(#paint0_linear_1310_41909)" style="fill:url(#paint0_linear_1310_41909)"/><rect transform="rotate(-90)" x="-384.5" y="265.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(90)" x="14" y="-336" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect transform="rotate(90)" x="14" y="-336" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="m236 164h28.421l31.579 36-31.579 36h-28.421z" fill="#fff"/><path d="m236 164h28.421l31.579 36-31.579 36h-28.421z" fill="url(#paint1_linear_1310_41909)" style="fill:url(#paint1_linear_1310_41909)"/><path d="m237.5 165.5h26.242l30.263 34.5-30.263 34.5h-26.242z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(-90)" x="-398.5" y="251.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(-90)" x="-12.5" y="251.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><path transform="translate(-75,75)" d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z" fill="#4a4848" mask="url(#path-7-inside-1_1253_89545)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="125" cy="200" r="111" fill="#fff" tb:tag="background"/><rect x="44.5" y="180.5" width="161" height="39" rx="3.8571" fill="#4a4848" fill-opacity=".06" tb:tag="border"/><path d="m48.36 179c-2.9419 0-5.3574 2.4155-5.3574 5.3574v31.285c0 2.9419 2.4155 5.3574 5.3574 5.3574h153.29c2.9419 0 5.3574-2.4155 5.3574-5.3574v-31.285c0-2.9419-2.4155-5.3574-5.3574-5.3574zm0 3h153.29c1.3318 0 2.3574 1.0256 2.3574 2.3574v31.285c0 1.3318-1.0256 2.3574-2.3574 2.3574h-153.29c-1.3318 0-2.3574-1.0256-2.3574-2.3574v-31.285c0-1.3318 1.0256-2.3574 2.3574-2.3574z" fill="#4a4848" tb:tag="border"/><circle cx="91" cy="148" r="16" fill="url(#paint2_linear_1253_89545)" style="fill:url(#paint2_linear_1253_89545)"/><circle cx="91" cy="148" r="15.5" stroke="#000" stroke-opacity=".12"/><circle cx="159" cy="148" r="16" fill="url(#paint3_linear_1253_89545)" style="fill:url(#paint3_linear_1253_89545)"/><circle cx="159" cy="148" r="15.5" stroke="#000" stroke-opacity=".12"/><text x="123.96094" y="201.94531" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:32px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="value" xml:space="preserve"><tspan>0</tspan></text><text x="124.47363" y="249.4729" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:22px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="valueUnits" xml:space="preserve"><tspan>m³/hr</tspan></text><g transform="translate(-75,75.001)" style="display: none;" tb:tag="broken">
</defs><rect transform="rotate(-90)" x="-386" y="264" width="372" height="72" fill="#fff" tb:tag="pipe-background"/><rect transform="rotate(-90)" x="-386" y="264" width="372" height="72" fill="url(#paint0_linear_1310_41909)" style="fill:url(#paint0_linear_1310_41909)"/><rect transform="rotate(-90)" x="-384.5" y="265.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(90)" x="14" y="-336" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect transform="rotate(90)" x="14" y="-336" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="m236 164h28.421l31.579 36-31.579 36h-28.421z" fill="#fff" tb:tag="pipe-background"/><path d="m236 164h28.421l31.579 36-31.579 36h-28.421z" fill="url(#paint1_linear_1310_41909)" style="fill:url(#paint1_linear_1310_41909)"/><path d="m237.5 165.5h26.242l30.263 34.5-30.263 34.5h-26.242z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(-90)" x="-398.5" y="251.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(-90)" x="-12.5" y="251.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><path transform="translate(-75,75)" d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z" fill="#4a4848" mask="url(#path-7-inside-1_1253_89545)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="125" cy="200" r="111" fill="#fff" tb:tag="background"/><rect x="44.5" y="180.5" width="161" height="39" rx="3.8571" fill="#4a4848" fill-opacity=".06" tb:tag="border"/><path d="m48.36 179c-2.9419 0-5.3574 2.4155-5.3574 5.3574v31.285c0 2.9419 2.4155 5.3574 5.3574 5.3574h153.29c2.9419 0 5.3574-2.4155 5.3574-5.3574v-31.285c0-2.9419-2.4155-5.3574-5.3574-5.3574zm0 3h153.29c1.3318 0 2.3574 1.0256 2.3574 2.3574v31.285c0 1.3318-1.0256 2.3574-2.3574 2.3574h-153.29c-1.3318 0-2.3574-1.0256-2.3574-2.3574v-31.285c0-1.3318 1.0256-2.3574 2.3574-2.3574z" fill="#4a4848" tb:tag="border"/><circle cx="91" cy="148" r="16" fill="url(#paint2_linear_1253_89545)" style="fill:url(#paint2_linear_1253_89545)"/><circle cx="91" cy="148" r="15.5" stroke="#000" stroke-opacity=".12"/><circle cx="159" cy="148" r="16" fill="url(#paint3_linear_1253_89545)" style="fill:url(#paint3_linear_1253_89545)"/><circle cx="159" cy="148" r="15.5" stroke="#000" stroke-opacity=".12"/><text x="123.96094" y="201.94531" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:32px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="value" xml:space="preserve"><tspan>0</tspan></text><text x="124.47363" y="249.4729" dominant-baseline="middle" fill="#000000" fill-opacity=".87" text-anchor="middle" style="font-family:Roboto;font-size:22px;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-weight:500" tb:tag="valueUnits" xml:space="preserve"><tspan>m³/hr</tspan></text><g transform="translate(-75,75.001)" style="display: none;" tb:tag="broken">
<g filter="url(#filter0_b_1281_42354)" style="">
<circle cx="200" cy="125" r="111" fill="#000" fill-opacity=".24" style=""/>
</g>

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 73 KiB

196
application/src/main/data/json/system/scada_symbols/left-motor-pump.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 40 KiB

387
application/src/main/data/json/system/scada_symbols/left-tee-pipe.svg

@ -40,6 +40,11 @@
"stateRenderFunction": "var fluid = (ctx.values.leftFluid ||\n ctx.values.topFluid || ctx.values.bottomFluid) && !ctx.values.leak;\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "top-fluid",
"stateRenderFunction": "var fluid = ctx.values.topFluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
@ -59,13 +64,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlow",
@ -74,13 +99,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowDirection",
@ -89,13 +134,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowAnimationSpeed",
@ -104,13 +169,32 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFluid",
@ -119,13 +203,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlow",
@ -134,13 +238,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowDirection",
@ -149,13 +273,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowAnimationSpeed",
@ -164,13 +308,32 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFluid",
@ -179,13 +342,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlow",
@ -194,13 +377,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowDirection",
@ -209,13 +412,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowAnimationSpeed",
@ -224,27 +447,67 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -295,13 +558,29 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g clip-path="url(#clip0_1245_66573)">
<path d="m64 186v-172h72v172z" fill="#fff"/>
<path d="m64 186v-172h72v172z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-172h72v172z" fill="url(#paint0_linear_1245_66573)" style="fill:url(#paint0_linear_1245_66442)"/>
<path d="m65.5 184.5v-169h69v169z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m14 64h51l35 36-35 36h-51z" fill="#fff"/>
<path d="m14 64h51l35 36-35 36h-51z" fill="#fff" tb:tag="pipe-background"/>
<path d="m14 64h51l35 36-35 36h-51z" fill="url(#paint1_linear_1245_66573)" style="fill:url(#paint1_linear_1245_66442)"/>
<path d="m15.5 65.5h48.866l33.542 34.5-33.542 34.5h-48.866z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 58 KiB

171
application/src/main/data/json/system/scada_symbols/left-top-elbow-pipe.svg

@ -29,6 +29,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "vertical-fluid",
"stateRenderFunction": "var fluid = ctx.values.fluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}\n",
@ -40,71 +45,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -123,19 +232,35 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<g transform="rotate(90,100,100)" clip-path="url(#clip0_1245_66459)">
<rect x="14" y="64" width="50" height="72" fill="#fff"/>
<rect x="14" y="64" width="50" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="14" y="64" width="50" height="72" fill="url(#paint0_linear_1245_66459)" style="fill:url(#paint0_linear_1245_66459)"/>
<rect x="15.5" y="65.5" width="47" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path class="UnoptimicedTransforms" d="m64 186v-50h72v50z" fill="#fff"/>
<path class="UnoptimicedTransforms" d="m64 186v-50h72v50z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-50h72v50z" fill="url(#paint1_linear_1245_66459)" style="fill:url(#paint1_linear_1245_66459)"/>
<path d="m65.5 184.5v-47h69v47z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(-90,51.5,198.5)" x="51.5" y="198.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="url(#paint2_linear_1245_66459)" style="fill:url(#paint2_linear_1245_66459)"/>
<path d="m65.5 134.5v-68.865c0.8334 0.0861 1.9717 0.2211 3.3584 0.4273 3.1196 0.464 7.4889 1.2873 12.47 2.7228 9.9828 2.8767 22.316 8.1809 32.01 17.875 9.695 9.6942 14.999 22.027 17.875 32.01 1.436 4.982 2.259 9.351 2.723 12.471 0.206 1.386 0.341 2.525 0.428 3.358z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
</g><defs>

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 51 KiB

318
application/src/main/data/json/system/scada_symbols/level_and_fan.svg

@ -1,318 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="100" height="100" version="1.1" viewBox="0 0 100 100">
<tb:metadata xmlns=""><![CDATA[{
"title": "Level and Fan",
"description": "Level and Fan Symbol",
"searchTags": [
"level",
"fan"
],
"widgetSizeX": 3,
"widgetSizeY": 3,
"stateRenderFunction": "var showMinMaxLevel = ctx.properties.showMinMaxLevel;\nvar minLevelElement = ctx.tags.minLevel[0];\nvar maxLevelElement = ctx.tags.maxLevel[0];\nvar minLevel = ctx.properties.minLevel; \nvar maxLevel = ctx.properties.maxLevel;\n\nif (showMinMaxLevel) {\n \n var minMaxLevelFont = ctx.properties.minMaxLevelFont;\n var minMaxLevelColor = ctx.properties.minMaxLevelColor;\n \n ctx.api.text(minLevelElement, minLevel);\n ctx.api.text(maxLevelElement, maxLevel);\n \n ctx.api.font(minLevelElement, minMaxLevelFont, minMaxLevelColor);\n ctx.api.font(maxLevelElement, minMaxLevelFont, minMaxLevelColor);\n \n} else {\n minLevelElement.hide();\n maxLevelElement.hide();\n}\n\nvar disabled = ctx.values.disabled;\nvar on = ctx.values.on;\nvar level = ctx.values.level;\n\nvar onButton = ctx.tags.onButton;\nvar offButton = ctx.tags.offButton;\nvar levelUpButton = ctx.tags.levelUpButton;\nvar levelDownButton = ctx.tags.levelDownButton;\n\nvar onButtonEnabled = !disabled && !on;\nvar offButtonEnabled = !disabled && on;\nvar levelUpEnabled = !disabled && level < maxLevel;\nvar levelDownEnabled = !disabled && level > minLevel;\n\nif (onButtonEnabled) {\n ctx.api.enable(onButton);\n onButton[0].findOne('rect').attr({fill: '#12ed19'});\n} else {\n ctx.api.disable(onButton);\n onButton[0].findOne('rect').attr({fill: '#777'});\n}\n \nif (offButtonEnabled) {\n ctx.api.enable(offButton);\n offButton[0].findOne('rect').attr({fill: '#ed121f'});\n} else {\n ctx.api.disable(offButton);\n offButton[0].findOne('rect').attr({fill: '#777'});\n} \n\n\nif (levelUpEnabled) {\n ctx.api.enable(levelUpButton);\n levelUpButton[0].findOne('rect').attr({fill: '#fff'});\n} else {\n ctx.api.disable(levelUpButton);\n levelUpButton[0].findOne('rect').attr({fill: '#777'});\n}\n \nif (levelDownEnabled) {\n ctx.api.enable(levelDownButton);\n levelDownButton[0].findOne('rect').attr({fill: '#fff'});\n} else {\n ctx.api.disable(levelDownButton);\n levelDownButton[0].findOne('rect').attr({fill: '#777'});\n}",
"tags": [
{
"tag": "fan",
"stateRenderFunction": "var on = ctx.values.on;\nvar hasAnimation = element.remember('hasAnimation');\nif (on) {\n\n var level = ctx.values.level; \n var minLevel = ctx.properties.minLevel; \n var maxLevel = ctx.properties.maxLevel;\n\n var speed = (level - minLevel) / (maxLevel - minLevel);\n speed = Math.max(0, Math.min(1, speed))*2; \n\n if (!hasAnimation) {\n element.remember('hasAnimation', true);\n element.animate(1000).ease('-').rotate(360).loop();\n } else {\n element.timeline().play();\n }\n element.timeline().speed(speed);\n} else {\n if (hasAnimation) {\n element.timeline().pause();\n }\n}\n",
"actions": null
},
{
"tag": "level",
"stateRenderFunction": "var level = ctx.values.level; \nvar disabled = ctx.values.disabled;\nvar minLevel = ctx.properties.minLevel; \nvar maxLevel = ctx.properties.maxLevel;\n\nvar height = (level - minLevel) / (maxLevel - minLevel);\nheight = Math.max(0, Math.min(1, height))*80; \n\nctx.api.animate(element, 200).attr({height: height});\n\nvar fill;\nif (disabled) {\n fill = ctx.properties.disabledLevelBackground;\n} else {\n var colorProcessor = ctx.properties.levelBackground;\n colorProcessor.update(level);\n fill = colorProcessor.color;\n}\n\nelement.attr({fill: fill});",
"actions": null
},
{
"tag": "levelDownButton",
"actions": {
"click": {
"actionFunction": "var level = ctx.values.level; \nvar minLevel = ctx.properties.minLevel;\n\nvar newLevel = Math.max(minLevel, level - 5);\nctx.api.setValue('level', newLevel);\nctx.api.callAction(event, 'levelUpdateState', newLevel, {\n error: () => {\n ctx.api.setValue('level', level);\n }\n});"
}
}
},
{
"tag": "levelTitle",
"stateRenderFunction": "var showLevelTitle = ctx.properties.showLevelTitle;\n\nif (showLevelTitle) {\n var levelTitle = ctx.properties.levelTitle;\n var levelTitleFont = ctx.properties.levelTitleFont;\n var levelTitleColor = ctx.properties.levelTitleColor;\n \n ctx.api.text(element, levelTitle);\n ctx.api.font(element, levelTitleFont, levelTitleColor);\n \n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "levelUpButton",
"actions": {
"click": {
"actionFunction": "var level = ctx.values.level; \nvar maxLevel = ctx.properties.maxLevel;\n\nvar newLevel = Math.min(maxLevel, level + 5);\nctx.api.setValue('level', newLevel);\nctx.api.callAction(event, 'levelUpdateState', newLevel, {\n error: () => {\n ctx.api.setValue('level', level);\n }\n});"
}
}
},
{
"tag": "levelValue",
"stateRenderFunction": "var showValue = ctx.properties.showValue;\n\nif (showValue) {\n var valueFont = ctx.properties.valueFont;\n var valueColor = ctx.properties.valueColor;\n var level = ctx.values.level; \n \n var levelText = ctx.api.formatValue(level, ctx.properties.valueDecimals, ctx.properties.valueUnits, false);\n\n ctx.api.font(element, valueFont, valueColor);\n ctx.api.text(element, levelText);\n\n} else {\n element.hide();\n ctx.tags.levelValueBackground[0].hide();\n}",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'levelValueClick');"
}
}
},
{
"tag": "offButton",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'offUpdateState', undefined, {\n next: () => {\n ctx.api.setValue('on', false);\n }\n});\n"
}
}
},
{
"tag": "onButton",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'onUpdateState', undefined, {\n next: () => {\n ctx.api.setValue('on', true);\n }\n});\n\n"
}
}
},
{
"tag": "ramka",
"stateRenderFunction": "var on = ctx.values.on;\nvar hasAnimation = element.remember('hasAnimation');\nif (on) {\n if (!hasAnimation) {\n element.remember('hasAnimation', true);\n ctx.api.animate(element, 1000).scale(1.1).loop(0, true);\n } else {\n element.timeline().play();\n }\n} else {\n if (hasAnimation) {\n element.timeline().pause();\n }\n}",
"actions": null
}
],
"behavior": [
{
"id": "level",
"name": "Level",
"hint": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 10,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": null,
"constantValue": null,
"valueToDataFunction": null
},
{
"id": "disabled",
"name": "{i18n:widgets.rpc-state.disabled-state}",
"hint": "{i18n:widgets.rpc-state.disabled-state-hint}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "",
"falseLabel": "",
"stateLabel": "{i18n:widgets.rpc-state.disabled}",
"valueToDataType": null,
"constantValue": null,
"valueToDataFunction": null
},
{
"id": "on",
"name": "On/Off state",
"hint": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:widgets.rpc-state.on}",
"falseLabel": "{i18n:widgets.rpc-state.off}",
"stateLabel": "{i18n:widgets.rpc-state.on}",
"valueToDataType": null,
"constantValue": null,
"valueToDataFunction": null
},
{
"id": "onUpdateState",
"name": "{i18n:widgets.rpc-state.turn-on}",
"hint": "{i18n:widgets.rpc-state.turn-on-hint}",
"type": "action",
"valueType": "BOOLEAN",
"valueToDataType": "CONSTANT",
"constantValue": true
},
{
"id": "offUpdateState",
"name": "{i18n:widgets.rpc-state.turn-off}",
"hint": "{i18n:widgets.rpc-state.turn-off-hint}",
"type": "action",
"valueType": "BOOLEAN",
"valueToDataType": "CONSTANT",
"constantValue": false
},
{
"id": "levelUpdateState",
"name": "Update level",
"type": "action",
"valueType": "DOUBLE",
"valueToDataType": "VALUE",
"constantValue": 0
},
{
"id": "levelValueClick",
"name": "On level value click",
"type": "widgetAction"
}
],
"properties": [
{
"id": "showLevelTitle",
"name": "Level title",
"type": "switch",
"default": true,
"rowClass": "column-xs"
},
{
"id": "levelTitle",
"name": "Level title",
"type": "text",
"default": "{i18n:widgets.battery-level.value}",
"disableOnProperty": "showLevelTitle",
"fieldClass": "flex"
},
{
"id": "levelTitleFont",
"name": "Level title",
"type": "font",
"default": {
"size": 6,
"sizeUnit": "px",
"family": "Roboto",
"weight": "normal",
"style": "normal"
},
"disableOnProperty": "showLevelTitle"
},
{
"id": "levelTitleColor",
"name": "Level title",
"type": "color",
"default": "#000000",
"disableOnProperty": "showLevelTitle"
},
{
"id": "showValue",
"name": "Value",
"type": "switch",
"default": true,
"rowClass": "column-xs"
},
{
"id": "valueUnits",
"name": "Value",
"type": "units",
"default": "",
"disableOnProperty": "showValue",
"fieldClass": "flex"
},
{
"id": "valueDecimals",
"name": "Value",
"type": "number",
"default": 2,
"min": 0,
"max": 15,
"step": 1,
"fieldSuffix": "decimals",
"disableOnProperty": "showValue",
"fieldClass": "flex"
},
{
"id": "valueFont",
"name": "Value",
"type": "font",
"default": {
"size": 6,
"sizeUnit": "px",
"family": "Roboto",
"weight": "normal",
"style": "normal"
},
"disableOnProperty": "showValue"
},
{
"id": "valueColor",
"name": "Value",
"type": "color",
"default": "#000000",
"disableOnProperty": "showValue"
},
{
"id": "minLevel",
"name": "Level range",
"subLabel": "min",
"type": "number",
"required": true,
"default": 0,
"min": 0,
"rowClass": "column-xs",
"fieldClass": "flex-xs"
},
{
"id": "maxLevel",
"name": "Level range",
"subLabel": "max",
"type": "number",
"required": true,
"default": 100,
"min": 0,
"fieldClass": "flex-xs"
},
{
"id": "showMinMaxLevel",
"name": "Min/Max label",
"type": "switch",
"default": true
},
{
"id": "minMaxLevelFont",
"name": "Min/Max label",
"type": "font",
"default": {
"size": 6,
"sizeUnit": "px",
"family": "Roboto",
"weight": "normal",
"style": "normal"
},
"disableOnProperty": "showMinMaxLevel"
},
{
"id": "minMaxLevelColor",
"name": "Min/Max label",
"type": "color",
"default": "#666",
"disableOnProperty": "showMinMaxLevel"
},
{
"id": "levelBackground",
"name": "Level background",
"subLabel": "Enabled",
"type": "color_settings",
"default": {
"type": "constant",
"color": "#1abb48",
"colorFunction": "return value > 70 ? '#d5280d' : '#1abb48';"
},
"divider": true,
"rowClass": "column-xs"
},
{
"id": "disabledLevelBackground",
"name": "Level background",
"subLabel": "Disabled",
"type": "color",
"default": "#ccc"
}
]
}]]></tb:metadata><rect width="100" height="100" rx="0" fill="none" stroke="#ccc" stroke-width="2" tb:tag="rect"/><rect x="8" y="15" width="20" height="81" rx="2" fill="#ececec" stroke="#000" stroke-width="1.0359"/><rect transform="scale(1 -1)" x="8.5" y="-95.5" width="19" height="40" rx="1.5" fill="#1abb48" tb:tag="level"/><text x="32" y="95" fill="#666" font-family="Roboto" font-size="6px" tb:tag="minLevel" xml:space="preserve"><tspan x="31.354307" y="96.626251">min</tspan></text><rect x="10.941" y="49.544" width="14.155" height="12" rx="2.8668" fill="#fff" fill-opacity=".45148" tb:tag="levelValueBackground"/><g font-family="Roboto" font-size="6px">
<text x="18" y="56" dominant-baseline="middle" fill="#000000" text-anchor="middle" tb:tag="levelValue" xml:space="preserve"><tspan>N/A</tspan></text>
<text x="17.636805" y="10.347933" dominant-baseline="middle" fill="#000000" text-anchor="middle" tb:tag="levelTitle" xml:space="preserve"><tspan>Level</tspan></text>
<text x="32" y="20" fill="#666666" tb:tag="maxLevel" xml:space="preserve"><tspan x="29.865496" y="18.716127">max</tspan></text>
</g><path d="m82.143 27.538h-20.499c-1.1045 0-1.9999 0.8954-1.9999 1.9999v20.499c0 1.1045 0.8954 1.9999 1.9999 1.9999h20.499c1.1045 0 1.9999-0.8954 1.9999-1.9999v-20.499c0-1.1045-0.8954-1.9999-1.9999-1.9999zm-20.499 1.4999c0.27613 0 0.49998 0.22385 0.49998 0.49998s-0.22385 0.49998-0.49998 0.49998-0.49998-0.22385-0.49998-0.49998 0.22385-0.49998 0.49998-0.49998zm0 21.499c-0.27613 0-0.49998-0.22385-0.49998-0.49998s0.22385-0.49998 0.49998-0.49998 0.49998 0.22385 0.49998 0.49998-0.22385 0.49998-0.49998 0.49998zm10.249-0.79997c-5.495 0-9.9495-4.4545-9.9495-9.9495s4.4545-9.9495 9.9495-9.9495 9.9495 4.4545 9.9495 9.9495-4.4545 9.9495-9.9495 9.9495zm10.249 0.79997c-0.27613 0-0.49998-0.22385-0.49998-0.49998s0.22385-0.49998 0.49998-0.49998 0.49998 0.22385 0.49998 0.49998-0.22385 0.49998-0.49998 0.49998zm0-20.499c-0.27613 0-0.49998-0.22385-0.49998-0.49998s0.22385-0.49998 0.49998-0.49998 0.49998 0.22385 0.49998 0.49998-0.22385 0.49998-0.49998 0.49998z" fill="#080d7d" stroke-width=".040031" tb:tag="ramka"/><path d="m80.66 37.983c-0.04956-0.24078-0.15288-0.4126-0.34242-0.56916-1.5148-1.2518-3.6154-1.1677-6.3021 0.25239-0.17533-0.17533-0.36512-0.32353-0.5778-0.45114 1.8517-1.6612 3.5396-2.3586 5.0637-2.0924 0.44934 0.0785 0.7605-0.43637 0.48209-0.79765-0.61795-0.80201-1.3212-1.455-2.1668-2.0119-0.20536-0.13522-0.39987-0.18358-0.64461-0.16032-1.9563 0.18594-3.3822 1.7308-4.2778 4.6347-0.24799 0-0.48697 0.02938-0.72756 0.08955 0.1347-2.484 0.83504-4.1707 2.101-5.0601 0.37324-0.2622 0.22918-0.84629-0.22313-0.90497-1.0041-0.13014-1.9631-0.09459-2.9548 0.10956-0.24078 0.04956-0.4126 0.15288-0.56916 0.34242-1.2518 1.5148-1.1677 3.6154 0.25239 6.3021-0.17533 0.17533-0.32353 0.36512-0.45114 0.5778-1.6612-1.8517-2.3586-3.5396-2.0924-5.0637 0.0785-0.44934-0.43637-0.7605-0.79765-0.48213-0.80201 0.61795-1.455 1.3212-2.0119 2.1668-0.13522 0.20536-0.18358 0.39986-0.16032 0.64461 0.18594 1.9563 1.7308 3.3822 4.6347 4.2778 0 0.24799 0.02938 0.48697 0.08955 0.72756-2.484-0.1347-4.1707-0.83504-5.0601-2.101-0.2622-0.37324-0.84629-0.22918-0.90497 0.22313-0.13014 1.0041-0.09459 1.9631 0.10956 2.9548 0.04956 0.24078 0.15288 0.4126 0.34242 0.56916 1.5148 1.2518 3.6154 1.1677 6.3021-0.25239 0.17533 0.17533 0.36512 0.32353 0.5778 0.45114-1.8517 1.6612-3.5396 2.3586-5.0637 2.0924-0.44934-0.0785-0.7605 0.43637-0.48209 0.79765 0.61795 0.80201 1.3212 1.455 2.1668 2.0119 0.20536 0.13522 0.39986 0.18358 0.64461 0.16032 1.9563-0.18594 3.3822-1.7308 4.2778-4.6347 0.24799 0 0.48697-0.02938 0.72756-0.08959-0.1347 2.484-0.83504 4.1707-2.101 5.0601-0.37324 0.2622-0.22918 0.84629 0.22313 0.90497 1.0041 0.13014 1.9631 0.09459 2.9548-0.10956 0.24078-0.04956 0.4126-0.15288 0.56916-0.34242 1.2518-1.5148 1.1677-3.6154-0.25239-6.3021 0.17533-0.17533 0.32353-0.36512 0.45114-0.5778 1.6612 1.8517 2.3586 3.5396 2.0924 5.0637-0.0785 0.44934 0.43637 0.7605 0.79765 0.48209 0.80201-0.61795 1.455-1.3212 2.0119-2.1668 0.13522-0.20536 0.18358-0.39987 0.16032-0.64461-0.18594-1.9563-1.7308-3.3822-4.6347-4.2778 0-0.24799-0.02938-0.48697-0.08955-0.72756 2.484 0.1347 4.1707 0.83504 5.0601 2.101 0.2622 0.37324 0.84629 0.22918 0.90497-0.22313 0.13014-1.004 0.09459-1.963-0.10956-2.9547zm-8.7657 4.3043c-1.3806 0-2.4999-1.1193-2.4999-2.4999s1.1193-2.4999 2.4999-2.4999 2.4999 1.1193 2.4999 2.4999-1.1193 2.4999-2.4999 2.4999z" fill="#7d081e" stroke-width=".040031" tb:tag="fan"/><g tb:tag="onButton">
<rect x="54.702" y="60.372" width="14.263" height="7.4261" rx="1.5" fill="#12ed19" stroke="#000" tb:tag="onButtonBackground"/>
<text x="61.855518" y="64.49128" dominant-baseline="middle" fill="#000000" font-family="Roboto" font-size="4.4461px" stroke-width=".74101" text-anchor="middle" xml:space="preserve"><tspan stroke-width=".74101">On</tspan></text>
</g><g tb:tag="offButton">
<rect x="74.367" y="60.311" width="14.263" height="7.4261" rx="1.5" fill="#ed121f" stroke="#000" tb:tag="offButtonBackground"/>
<text x="81.365944" y="64.518349" dominant-baseline="middle" fill="#000000" font-family="Roboto" font-size="4.4461px" stroke-width=".74101" text-anchor="middle" xml:space="preserve"><tspan stroke-width=".74101">Off</tspan></text>
</g><g stroke="#000" stroke-miterlimit="1" tb:tag="levelUpButton">
<rect x="34.97" y="39.377" width="9.5005" height="12.584" rx=".75" fill="none"/>
<path transform="matrix(.4753 -.2938 .2938 .4753 4.8821 31.04)" d="m41.544 60.24-7.893-4.9762 8.256-4.3474-0.18153 4.6618z" fill="#12ed19"/>
</g><g stroke="#000" stroke-miterlimit="1" tb:tag="levelDownButton">
<rect x="34.97" y="59.039" width="9.5005" height="12.584" rx=".75" fill="none"/>
<path transform="matrix(-.47514 .29405 -.29405 -.47514 74.568 79.771)" d="m41.544 60.24-7.893-4.9762 8.256-4.3474-0.18153 4.6618z" fill="#12ed19"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

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

@ -26,13 +26,16 @@
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
}
],
"properties": []

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

167
application/src/main/data/json/system/scada_symbols/long-horizontal-pipe.svg

@ -23,6 +23,11 @@
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
@ -30,71 +35,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -113,9 +222,25 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><path d="m14 64h372v72h-372v-72z" fill="#fff"/><path d="m14 64h372v72h-372v-72z" fill="url(#paint0_linear_1239_51060)"/><g stroke-width="3">
}]]></tb:metadata><path d="m14 64h372v72h-372v-72z" fill="#fff" tb:tag="pipe-background"/><path d="m14 64h372v72h-372v-72z" fill="url(#paint0_linear_1239_51060)"/><g stroke-width="3">
<path d="m15.5 65.5h369v69h-369v-69z" stroke="#000" stroke-opacity=".12"/>
<rect x="387.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171"/>

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 46 KiB

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

@ -26,13 +26,16 @@
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
}
],
"properties": []

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

167
application/src/main/data/json/system/scada_symbols/long-vertical-pipe.svg

@ -23,6 +23,11 @@
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
@ -30,71 +35,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -113,9 +222,25 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><path d="m64 386v-372h72v372h-72z" fill="#fff"/><path d="m64 386v-372h72v372h-72z" fill="url(#paint0_linear_1242_51682)"/><g stroke-width="3">
}]]></tb:metadata><path d="m64 386v-372h72v372h-72z" fill="#fff" tb:tag="pipe-background"/><path d="m64 386v-372h72v372h-72z" fill="url(#paint0_linear_1242_51682)"/><g stroke-width="3">
<path d="m65.5 384.5v-369h69v369h-69z" stroke="#000" stroke-opacity=".12"/>
<rect transform="rotate(-90 51.5 398.5)" x="51.5" y="398.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171"/>
<rect transform="rotate(-90 51.5 12.5)" x="51.5" y="12.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171"/>

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 39 KiB

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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

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

@ -0,0 +1,178 @@
<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",
"searchTags": [
"pipe",
"drain"
],
"widgetSizeX": 2,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": "var color = ctx.properties.fluidColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"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",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"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
}
],
"properties": [
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#8CAA5C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g tb:tag="clickArea">
<rect x="136" y="64" width="164" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="136" y="64" width="164" height="72" fill="url(#paint0_linear_1924_304906)"/>
<rect x="137.5" y="65.5" width="161" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m64 64v-50h72v50h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 64v-50h72v50h-72z" fill="url(#paint1_linear_1924_304906)"/>
<path d="m65.5 62.5v-47h69v47h-69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect transform="rotate(-90 51.5 12.5)" x="51.5" y="12.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<path d="m136 136s-30.518-1.718-50.4-21.6-21.6-50.4-21.6-50.4h72v72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m136 136s-30.518-1.718-50.4-21.6-21.6-50.4-21.6-50.4h72v72z" fill="url(#paint2_linear_1924_304906)"/>
<path d="m134.5 65.5v68.865c-0.833-0.087-1.972-0.222-3.358-0.428-3.12-0.464-7.489-1.287-12.471-2.723-9.983-2.876-22.316-8.18-32.01-17.875-9.6942-9.694-14.998-22.027-17.875-32.01-1.4355-4.9815-2.2588-9.3508-2.7228-12.47-0.2062-1.3867-0.3412-2.525-0.4273-3.3584h68.865z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<ellipse transform="rotate(-90 300 100)" cx="300" cy="100" rx="36" ry="4" fill="#5D5C5C"/>
<path d="m323 94.5c-4-9.2-20-22.333-24-24.5 0 0-1.5 18.101-1.5 30.5s1.5 33 1.5 33c0 6.5-1.5 9-4 14.5-5.089 11.196-3.5 10.5-12.5 13.5s-24 2.5-21.5 11.5 32-8 34-4-16 17-10.5 21.5 17.5-10.5 21.5-10.5 4.5 10.5 10 10.5 7-11.5 16-10.5 12 13 18 17 14-2 14.5-11-20.5-7-20.5-13 39 1.5 45.5-4 0.5-6.5-6.5-10.5-38 5.5-39 0 16.5 0 15-5.5-16.5-2-23-10.5-8-36.5-13-48z" fill="#8CAA5C" tb:tag="fluid-background"/>
</g><g style="display: none;" tb:tag="leak">
<path d="m153.01 88.724c0.447 0.4587 0.927 1.092 1.445 1.447 1.445 1.447 3.278 2.5504 5.226 2.8189 0.906 0.0114 1.883-0.0808 2.756-0.244 2.374-0.3533 4.677-0.603 6.98-0.8527 2.058-0.1135 4.361-0.3632 5.704-1.8795-0.136-0.2456-0.169-0.4203-0.305-0.6659 3.324-1.0731 6.348-3.2652 8.751-5.8832 1.66-1.7562 3.047-4.0038 5.187-5.1267 0.737-0.4088 1.507-0.6431 2.173-0.9483 1.157-0.5778 2.21-1.2265 3.128-2.121 0.704-0.5835 1.272-1.4126 1.873-2.0672 1.731-1.8598 3.669-3.5777 5.951-4.9079-0.394 0.7965-0.787 1.593-1.18 2.3895 0 0 0.098 0.524 0.065 0.3493-0.278-0.0384-0.491 0.2726-0.53 0.5509 1.01 0.0824 2.019 0.1648 2.925 0.1763 4.841 0.2702 9.752 0.4367 14.669 0.1503-4.508 1.0234-9.218 1.4518-13.993 1.5309-1.081 0.0212-2.09-0.0613-3.209 0.2382-2.691 0.5933-4.602 2.9388-6.235 5.3226l-0.071 0.1036c-0.142 0.2073-0.355 0.5182-0.219 0.7639 0.066 0.3493 0.551 0.5296 0.933 0.639 1.981 0.4432 3.89 0.99 5.8 1.5368 2.395 0.7272 4.578 1.7653 7.044 2.3888 2.188 0.5852 4.414 0.8921 6.537 1.1279 1.009 0.0825 1.948 0.2686 2.892 0.0017 1.048-0.1959 1.96-0.6373 2.838-1.2535 1.125-0.7524 2.179-1.4012 3.2-2.2247 5.912-4.6296 21.148-3.4571 26.341-9.0368-1.846 2.6948-3.867 5.4222-5.713 8.1169 4.093-1.3073 8.487-1.4958 12.951-1.7879 1.255-0.0539 2.51-0.1077 3.803-0.4398 1.048-0.1959 2.135-0.67 3.046-1.1115 1.823-0.883 3.614-1.9407 4.646-3.67-0.502 1.1785-1.212 2.2149-1.955 3.0767 0.382 0.1094 0.867 0.2897 1.249 0.3991 3.263 1.0169 6.455 2.1374 9.614 3.0833-1.43 0.0865-2.925-0.1763-4.349-0.5427-1.91-0.5468-3.639-1.5792-5.625-1.5695-2.265-0.0285-4.269 1.34-6.474 2.1137-3.466 1.2804-7.368 1.1963-11.133 1.3578-3.766 0.1616-7.007-0.5296-13.148 0.2188-4.426 0.5394-11.568 1.5556-14.243 3.6823-0.879 0.6161-1.726 1.4069-2.468 2.2687-0.53 0.5509-0.956 1.1728-1.453 1.8983-0.568 0.8291-1.065 1.5546-1.737 2.3128-0.355 0.5182-0.813 0.9654-1.305 1.238-0.562 0.3762-1.364 0.4358-1.992 0.4627-2.232 0.1461-4.464 0.2921-6.696 0.4382 0.349-0.0653 0.873-0.1632 1.223-0.2285 2.794-0.5223 5.485-1.1156 7.734-2.6204 0.491-0.2726 0.95-0.7198 1.13-1.2054 0.071-0.1037 0.142-0.2073 0.039-0.2783 0.147-0.6602-0.469-1.5392-1.064-1.3376-0.654-0.6007-1.8-0.9287-2.564-1.1475-6.046-1.4005-12.162-2.6973-18.175-3.9232-0.661-0.1477-1.321-0.2954-2.123-0.2359-0.802 0.0596-1.397 0.2612-2.063 0.5664-4.973 1.9235-10.158 4.1579-13.969 7.9429-0.071 0.1037-0.142 0.2073-0.213 0.311 0 0 0.103 0.071 0.207 0.142-0.317 0.2399-0.666 0.3052-0.983 0.5451-2.145 1.5759-3.538 4.2764-4.615 6.7369-0.726 2.3722-0.904 3.5502-0.487 4.3342 0.474 0.891 2.078 3.92 4.351 4.252 1.01 0.082 2.483-0.847 2.927-0.711 0.157 0.048 1.591 1.801-0.027 0.649 0.784-0.417 9.773-1.923 15.178-4.922 0.491-0.272 1.053-0.649 1.512-1.096 0.317-0.24 0.53-0.551 0.639-0.933 0.071-0.103 0.038-0.278 6e-3 -0.453-0.033-0.174-0.208-0.142-0.311-0.213-4.643-2.1141-9.919-3.7488-13.142-7.9363 0.278 0.0384 0.382 0.1094 0.66 0.1477 0.104 0.0711 0.279 0.0384 0.486 0.1804 2.706 0.9402 5.297 2.7153 8.009 3.2025 1.217 0.2244 2.505 0.3452 3.612 0.9516 1.418 0.8194 2.547 2.5063 4.075 2.9441 0.764 0.218 1.67 0.23 2.368 0.099 1.921-0.3588 3.848-1.1708 5.802-1.3552 0.977-0.0923 2.09 0.0612 3.067-0.031 0.174-0.0327 0.278 0.0384 0.453 0.0057 2.232-0.146 10.307 0.6727 12.272-0.4176-1.719 0.954-9.342 0.141-10.925 1.3406 2.81 1.0115 5.516 1.9515 8.468 2.7555-2.576-0.242-5.113-0.762-7.612-1.56-1.249-0.399-2.499-0.798-3.786-0.919-2.472-0.17-5.071 1.4-5.242 3.872-0.038 0.278-0.076 0.556 0.06 0.802 0.19 1.501 1.079 2.871 1.864 4.17 2.497 3.691 5.033 7.103 7.776 10.657 2.023 2.604 4.117 5.105 6.883 6.847 0.207 0.142 0.485 0.181 0.627-0.027-0.082 1.01 0.676 1.681 1.576 2.146 0.829 0.568 1.8 0.929 2.7 1.393 0.797 0.393 1.593 0.787 2.39 1.18 3.775 1.825 7.622 3.546 11.577 4.885-2.123-0.236-4.31-0.821-6.318-1.891-1.489-0.716-2.837-1.639-4.294-2.18-1.56-0.612-3.197-0.668-4.757-1.28-0.693-0.322-1.49-0.716-2.254-0.934 0.643 1.506 1.357 2.909 2.001 4.415-0.72-0.949-1.44-1.899-2.16-2.849-0.856-1.196-1.783-2.288-2.71-3.38-2.476-2.61-5.902-4.5-7.99-7.453-0.137-0.246-0.273-0.492-0.409-0.737 0.278 0.038 0.485 0.18 0.764 0.218-0.033-0.174-0.24-0.316-0.273-0.491-1.428-2.806-3.451-5.41-5.513-7.736-1.75-2.113-3.708-4.368-6.278-5.062-0.764-0.219-1.599-0.334-2.362-0.553-0.764-0.218-1.523-0.89-1.757-1.66-3.161 1.947-6.328 4.695-10.049 4.125-2.887-0.454-5.249 1.484-6.683-0.268 1.201 0.367-0.552 1.802-2.537 1.851-1.986 0.049-6.614-6.224-6.651-8.838-4e-3 -2.439 0.898-4.8669 2.383-6.5905 0.071-0.1037 0.071-0.1037 0.142-0.2073-3.661 0.2325-7.498 0.4977-11.258 0.2063-1.113-0.1534-2.297-0.2032-3.301-0.7386-1.516-1.3433-2.71-3.3795-3.8-5.3447z" fill="#5C5A5A" style=""/>
<path d="m176.07 110.09c0.95-0.72 1.344-1.294 2.567-1.522 2.445-0.457 3.381 0.075 5.892-0.033 2.756-0.244 3.944-0.825 6.537-1.942 1.331-0.61 2.56-1.292 3.684-2.044 0.3 1.119 3.052 1.505 4.198 1.833 1.145 0.328 2.433 0.449 3.295 1.192 0.966 0.813 1.369 2.003 2.017 3.057 0.753 1.124 1.822 2.009 2.82 2.997 1.925 2.081 3.042 4.673 3.985 7.299-0.104-0.071-0.104-0.071-0.207-0.142-1.428-2.806-3.452-5.41-5.513-7.736-1.75-2.113-3.708-4.368-6.278-5.062-0.764-0.219-1.599-0.334-2.363-0.553s-2.992-0.703-3.226-1.472c-3.161 1.946-5.097 3.841-8.819 3.271-2.886-0.454-3.492 0.085-6.39 0.536-0.556-0.077-1.429 0.086-2.199 0.321z" fill="#8B8B8B" style=""/>
<path d="m181.61 92.781c0.279 0.0384 0.382 0.1094 0.66 0.1477 0.104 0.071 0.279 0.0384 0.486 0.1804l0.104 0.071c0.654 0.6007 1.309 1.2013 2.209 1.6657 0.622 0.426 1.418 0.8194 2.111 1.1418 2.979 1.4314 6.384 2.241 9.04 4.3651-0.497 0.726-0.994 1.451-1.627 1.931 0.316-0.24 0.529-0.551 0.639-0.933 0.071-0.103 0.038-0.278 5e-3 -0.453-0.032-0.174-0.207-0.142-0.31-0.213-4.889-1.9779-10.094-3.7162-13.317-7.9037z" fill="#8B8B8B" style=""/>
<path d="m204 101.87c0.568-0.829 1.518-1.548 2.533-1.9189 1.015-0.3705 2.27-0.4244 3.389-0.7239 0.175-0.0326 0.278 0.0384 0.453 0.0057 2.232-0.146 9.367 0.6164 11.332-0.4739-1.719 0.954-8.401 0.1973-9.985 1.397 2.81 1.011 5.788 1.87 8.74 2.674-2.576-0.242-5.385-0.68-7.884-1.478-1.249-0.4-2.498-0.799-3.786-0.919-2.472-0.171-5.071 1.399-5.241 3.871-0.039 0.278-0.077 0.557 0.059 0.802-0.201-0.595-0.299-1.119-0.223-1.675 6e-3 -0.453 0.258-1.042 0.613-1.561z" fill="#8B8B8B" style=""/>
<path d="m153.01 88.724c0.447 0.4586 0.927 1.0919 1.445 1.4469 0.72 0.95 1.407 1.7253 2.514 2.3317 1.315 0.7484 2.843 1.1858 4.234 1.3776 2.505 0.3452 4.982 0.0629 7.46-0.2194 1.152-0.1249 2.336-0.0751 3.269 0.5639-0.175 0.0327-0.175 0.0327-0.35 0.0653 0.071-0.1037 0.071-0.1036 0.142-0.2073-3.662 0.2326-7.498 0.4977-11.258 0.2063-1.113-0.1534-2.297-0.2032-3.301-0.7386-1.871-0.8251-3.065-2.8613-4.155-4.8264z" fill="#8B8B8B" style=""/>
<path d="m178.26 89.88c0.775-0.6872 1.79-1.0577 2.566-1.7448 0.879-0.6162 1.55-1.3743 2.254-1.9578 1.442-0.9924 3.085-1.3898 4.455-2.2785 0.808-0.5125 1.687-1.1287 2.598-1.5702 1.681-0.6757 3.662-0.2325 5.365 0.1723 6.427 1.5098 12.751 2.9487 19.179 4.4586 1.042 0.2571 2.395 0.7271 2.52 1.8787-6e-3 0.4529-0.186 0.9385-0.399 1.2494 0.071-0.1036 0.142-0.2073 0.038-0.2783 0.148-0.6602-0.468-1.5391-1.063-1.3376-0.655-0.6006-1.801-0.9287-2.564-1.1475-6.046-1.4005-12.163-2.6973-18.176-3.9232-0.66-0.1477-1.32-0.2954-2.123-0.2358-0.802 0.0595-1.397 0.2611-2.063 0.5663-4.972 1.9235-10.158 4.1579-13.969 7.943-0.071 0.1036-0.142 0.2072-0.213 0.3109 0 0 0.104 0.071 0.207 0.142-0.316 0.2399-0.666 0.3052-0.982 0.5451 0.71-1.0364 1.42-2.0728 2.37-2.7926z" fill="#8B8B8B" style=""/>
<path d="m197.91 73.644c1.305-1.238 3.226-1.597 5.044-2.0271 0.174-0.0326 0.174-0.0326 0.349-0.0653 4.84 0.2702 9.752 0.4367 14.669 0.1503-4.509 1.0234-9.218 1.4518-13.993 1.5309-1.081 0.0212-2.09-0.0612-3.209 0.2382-2.691 0.5933-4.602 2.9388-6.235 5.3226 0.18-0.4856 0.606-1.1074 0.748-1.3147 0.426-0.6219 0.781-1.1401 1.207-1.762 0.29-0.8675 0.716-1.4894 1.42-2.0729z" fill="#8B8B8B" style=""/>
<path d="m207.85 94.682c0.349-0.0653 0.873-0.1632 1.222-0.2285 0.104 0.071 0.279 0.0383 0.279 0.0383 1.08-0.0212 2.161-0.0424 3.138-0.1346 1.921-0.359 3.262-0.8768 4.534-2.2895 0.672-0.7581 1.382-2.5157 1.95-3.3449 1.633-2.3838 4.364-3.5327 6.581-5.2122 1.971-1.5433 8.279-0.7511 11.049-1.2173 2.771-0.4661 3.371-0.2823 5.407-0.3364 3.263-0.0867 3.209-0.2383 4.814-0.3574 2.128-0.217 4.055-1.029 6.183-1.2461 1.43-0.0865 2.925 0.1763 4.251 0.0188 2.232-0.146 4.269-1.34 6.299-2.081 1.119-0.2995 2.238-0.599 3.318-0.6202 3.263 1.0169 6.455 2.1375 9.614 3.0834-1.43 0.0865-2.925-0.1763-4.349-0.5428-1.91-0.5468-3.639-1.5792-5.625-1.5694-2.265-0.0286-4.269 1.34-6.474 2.1136-3.466 1.2804-7.368 1.1963-11.133 1.3579-3.766 0.1615-6.58-0.174-9.741 1.7724-2.986 1.9137-13.062 0.1541-15.738 2.2808-0.879 0.6162-1.725 1.407-2.468 2.2688-0.529 0.5509-0.955 1.1727-1.452 1.8982-0.568 0.8292-1.065 1.5547-1.737 2.3128-0.355 0.5183-0.814 0.9655-1.305 1.238-0.562 0.3763-1.364 0.4358-1.992 0.4628-2.232 0.146-4.464 0.2921-6.625 0.3345z" fill="#8B8B8B" style=""/>
<path d="m209.91 120.87c0.944-0.267 2.123 0.236 2.674 0.766 0.998 0.988 1.925 2.08 2.955 3.243 0.895 0.917 1.718 1.938 2.684 2.752 2.449 1.982 5.466 3.135 8.489 3.836 0 0 0.103 0.071 0.174-0.033 3.776 1.825 7.622 3.546 11.578 4.885-2.123-0.235-4.311-0.821-6.319-1.891-1.489-0.716-2.836-1.639-4.293-2.18-1.56-0.612-3.198-0.668-4.758-1.28-0.693-0.322-1.489-0.716-2.253-0.934 0.643 1.506 1.357 2.909 2 4.416-0.72-0.95-1.44-1.9-2.159-2.85-0.856-1.196-1.784-2.288-2.711-3.38-2.721-2.473-5.973-4.396-8.061-7.35z" fill="#8B8B8B" style=""/>
</g><defs>
<linearGradient id="paint0_linear_1924_304906" x1="178.64" x2="178.44" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_1924_304906" x1="64" x2="136" y1="51" y2="51.654" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<linearGradient id="paint2_linear_1924_304906" x1="86" x2="135.68" y1="111" y2="63.663" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".35637"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".4903"/>
<stop stop-color="#fff" stop-opacity="0" offset=".56651"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".63374"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".75781"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 74 KiB

196
application/src/main/data/json/system/scada_symbols/right-motor-pump.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 40 KiB

387
application/src/main/data/json/system/scada_symbols/right-tee-pipe.svg

@ -30,6 +30,11 @@
"stateRenderFunction": "var fluid = (ctx.values.rightFluid ||\n ctx.values.topFluid || ctx.values.bottomFluid) && !ctx.values.leak;\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "right-fluid",
"stateRenderFunction": "var fluid = ctx.values.rightFluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
@ -59,13 +64,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlow",
@ -74,13 +99,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowDirection",
@ -89,13 +134,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowAnimationSpeed",
@ -104,13 +169,32 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFluid",
@ -119,13 +203,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlow",
@ -134,13 +238,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowDirection",
@ -149,13 +273,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowAnimationSpeed",
@ -164,13 +308,32 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFluid",
@ -179,13 +342,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlow",
@ -194,13 +377,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowDirection",
@ -209,13 +412,33 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "bottomFlowAnimationSpeed",
@ -224,27 +447,67 @@
"group": "{i18n:scada.symbol.bottom-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -295,13 +558,29 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g clip-path="url(#clip0_1245_66636)">
<path d="m64 186v-172h72v172z" fill="#fff"/>
<path d="m64 186v-172h72v172z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-172h72v172z" fill="url(#paint0_linear_1245_66636)" style="fill:url(#paint0_linear_1245_66442)"/>
<path d="m65.5 184.5v-169h69v169z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m186 64h-51l-35 36 35 36h51z" fill="#fff"/>
<path d="m186 64h-51l-35 36 35 36h51z" fill="#fff" tb:tag="pipe-background"/>
<path d="m186 64h-51l-35 36 35 36h51z" fill="url(#paint1_linear_1245_66636)" style="fill:url(#paint3_linear_1245_66442)"/>
<path d="m184.5 65.5h-48.866l-33.542 34.5 33.542 34.5h48.866z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="187.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 58 KiB

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

@ -26,13 +26,16 @@
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
}
],
"properties": []

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

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

@ -0,0 +1,160 @@
<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",
"searchTags": [
"pipe",
"drain"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": "var color = ctx.properties.fluidColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"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",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"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
}
],
"properties": [
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#8CAA5C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<g clip-path="url(#clip0_1924_305497)" tb:tag="clickArea">
<rect x="101" y="64" width="85" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="101" y="64" width="85" height="72" fill="url(#paint0_linear_1924_305497)"/>
<rect x="102.5" y="65.5" width="82" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="187.5" y="51.5" width="11.07" height="97" rx="5.5352" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<ellipse transform="rotate(-90 100 100)" cx="100" cy="100" rx="36" ry="4" fill="#5D5C5C"/>
<path d="m76.285 94.5c4-9.2 20-22.333 24.001-24.5 0 0 1.499 18.101 1.499 30.5 0 12.399-1.499 33-1.499 33 0 6.5 1.5 9 3.999 14.5 5.089 11.196 3.5 10.5 12.5 13.5s24 2.5 21.5 11.5-32-8-34-4 16 17 10.5 21.5-17.5-10.5-21.5-10.5-4.5 10.5-10 10.5-7-11.5-16-10.5-12 13-18 17-14-2-14.5-11 20.5-7 20.5-13-39 1.5-45.5-4c-6.5-5.5-0.50001-6.5 6.5-10.5 7-4 38 5.5 39 0s-16.5 0-15-5.5 16.5-2 23-10.5 8-36.5 13-48z" fill="#8CAA5C" tb:tag="fluid-background"/>
<g style="display:none" tb:tag="leak">
<path d="m110.95 127.83c0.101-0.428 0.159-0.97 0.338-1.362 0.338-1.362 1.025-2.66 2.076-3.507 0.528-0.327 1.136-0.619 1.707-0.832 1.521-0.632 2.964-1.3 4.406-1.969 1.25-0.661 2.693-1.329 4.019-0.913 7e-3 0.193 0.05 0.307 0.057 0.5 2.334-0.546 4.888-0.327 7.228 0.362 1.597 0.446 3.208 1.276 4.864 1.179 0.578-0.02 1.114-0.155 1.613-0.211 0.885-0.07 1.734-0.061 2.59 0.14 0.621 0.094 1.248 0.381 1.833 0.553 1.676 0.481 3.424 0.806 5.237 0.78-0.514-0.329-1.027-0.658-1.54-0.987 0 0-0.128-0.343-0.085-0.229-0.15 0.121-0.386 0.014-0.507-0.136 0.565-0.406 1.129-0.812 1.658-1.139 2.751-1.872 5.58-3.708 8.573-5.28-3.014 0.994-5.935 2.409-8.771 4.052-0.643 0.37-1.207 0.776-1.971 0.996-1.792 0.603-3.746-0.1-5.55-0.924l-0.078-0.035c-0.157-0.072-0.393-0.18-0.399-0.372-0.086-0.229 0.136-0.507 0.322-0.706 1.008-0.962 1.937-1.959 2.867-2.956 1.151-1.276 2.067-2.658 3.296-3.898 1.08-1.118 2.281-2.087 3.445-2.976 0.565-0.406 1.051-0.848 1.7-1.025 0.686-0.256 1.378-0.319 2.113-0.267 0.928 0.044 1.777 0.053 2.669 0.176 5.115 0.63 13.659-5.451 18.687-4.008 1.277 0.273 2.776 0 1.277-1.727-2.001-1.4995-2-1.9996-2-4.9996 0.757-0.4125-2-3.5-2-5-0.5-1 0.377-0.9415 1.069-1.0045 1.385-0.1259 2.812-0.1376 4.031 0.5139-0.713-0.5151-1.497-0.8733-2.239-1.1172 0.186-0.1995 0.408-0.4774 0.594-0.6769 1.559-1.7526 3.039-3.5411 4.562-5.2153-0.872 0.4552-1.658 1.1388-2.366 1.8583-0.929 0.9973-1.581 2.2164-2.752 2.9136-1.322 0.8183-2.984 0.7225-4.555 1.048-2.491 0.4736 3.492 6.84 1.221 8.0776-2.272 1.2375-3.933 2.7911-7.809 4.5241-2.794 1.249-7.353 3.179-9.679 2.875-0.735-0.051-1.513-0.216-2.254-0.46-0.507-0.137-0.977-0.352-1.526-0.603-0.628-0.286-1.177-0.537-1.84-0.745-0.392-0.179-0.82-0.28-1.206-0.266-0.463-0.022-0.956 0.226-1.335 0.433l-4.092 2.112c0.228-0.085 0.571-0.213 0.799-0.299 1.828-0.681 3.621-1.285 5.476-1.196 0.385-0.013 0.813 0.087 1.091 0.309 0.078 0.036 0.157 0.072 0.121 0.15 0.321 0.336 0.269 1.071-0.152 1.163-0.172 0.585-0.73 1.183-1.102 1.582-3.059 2.963-6.197 5.891-9.299 8.739-0.336 0.321-0.672 0.641-1.165 0.89s-0.914 0.341-1.414 0.397c-3.604 0.629-7.444 1.15-11.025 0.273-0.078-0.036-0.157-0.072-0.235-0.107 0 0 0.035-0.079 0.071-0.157-0.271-0.029-0.499 0.056-0.77 0.027-1.82-0.167-3.595-1.262-5.098-2.328-1.267-1.138-1.788-1.768-1.821-2.377-0.036-0.691-0.165-3.04 1.054-4.04 0.565-0.406 1.76-0.38 1.973-0.617 0.075-0.084 0.298-1.622-0.246-0.372 0.609-0.033 6.428-2.328 10.667-2.478 0.386-0.013 0.849 9e-3 1.277 0.11 0.271 0.029 0.507 0.136 0.706 0.322 0.079 0.036 0.121 0.15 0.164 0.264 0.043 0.115-0.072 0.157-0.108 0.236-1.981 2.886-4.505 5.715-4.919 9.318 0.15-0.122 0.186-0.2 0.336-0.321 0.036-0.078 0.15-0.121 0.222-0.278 1.259-1.51 2.154-3.471 3.576-4.717 0.637-0.563 1.351-1.09 1.788-1.838 0.544-0.984 0.611-2.375 1.354-3.173 0.372-0.399 0.901-0.727 1.358-0.897 1.256-0.469 2.677-0.673 3.891-1.256 0.607-0.292 1.207-0.776 1.814-1.067 0.115-0.043 0.15-0.121 0.265-0.164 1.364-0.704 5.823-4.0432 7.364-4.0975-1.348 0.0475-5.543 3.2235-6.899 3.0775 1.295-1.5885 2.553-3.099 4.005-4.6164-1.429 1.0536-2.737 2.2572-3.924 3.6109-0.594 0.6765-1.187 1.3535-1.902 1.8805-1.393 0.975-3.477 0.971-4.452-0.422-0.121-0.15-0.242-0.3-0.249-0.493-0.419-0.9496-0.381-2.0698-0.38-3.1117 0.163-3.0539 0.446-5.9577 0.802-9.0184 0.268-2.2473 0.614-4.4588 1.624-6.4622 0.071-0.1568 0.222-0.2779 0.378-0.2062-0.405-0.5645-0.197-1.2278 0.168-1.8194 0.286-0.6274 0.73-1.1832 1.095-1.7749 0.329-0.5132 0.658-1.0264 0.987-1.5396 1.575-2.4091 3.227-4.7824 5.079-6.9698-1.164 0.8899-2.244 2.0083-3.046 3.3485-0.622 0.9479-1.088 1.9675-1.753 2.8012-0.701 0.9122-1.644 1.5242-2.345 2.4363-0.293 0.4348-0.622 0.948-0.994 1.3469-0.155-1.1135-0.232-2.1911-0.387-3.3046-0.087 0.8133-0.174 1.6267-0.261 2.44-0.08 1.006-0.239 1.9762-0.398 2.9464-0.532 2.411-1.878 4.7349-2.061 7.2107l0.021 0.5781c0.15-0.1211 0.221-0.2779 0.371-0.399 0.043 0.1143-0.029 0.2711 0.014 0.3854 0.153 2.1553-0.115 4.4026-0.504 6.4999-0.282 1.862-0.635 3.8808-1.9 5.1984-0.372 0.399-0.822 0.762-1.194 1.161s-0.58 1.062-0.445 1.598c-2.548-0.026-5.383-0.521-7.37 1.131-1.536 1.288-3.611 0.984-3.835 2.523 0.577-0.642-0.961-0.865-2.147-0.191-1.185 0.674-1.686 6-0.783 7.55 0.861 1.436 2.251 2.545 3.734 3.033 0.078 0.035 0.078 0.035 0.157 0.071-2.236 1.159-4.586 2.361-6.693 3.863-0.601 0.484-1.28 0.933-1.68 1.603-0.417 1.326-0.398 2.946-0.344 4.487z" fill="#5C5A5A"/>
<path d="m116.94 107.1c0.813 0.088 1.248 0.285 2.048-0.013 1.6-0.597 1.962-1.241 3.476-2.066 1.707-0.832 2.611-0.911 4.531-1.171 1-0.113 1.963-0.147 2.891-0.102-0.22-0.764 1.261-1.965 1.819-2.564 0.558-0.598 1.272-1.125 1.516-1.8665 0.28-0.8201 0.096-1.6625 0.105-2.5116 0.044-0.9276 0.36-1.8262 0.597-2.7606 0.396-1.9045 0.135-3.8246-0.24-5.702-0.035 0.0784-0.035 0.0784-0.071 0.1569 0.153 2.1553-0.115 4.4026-0.504 6.4999-0.282 1.8619-0.635 3.8808-1.901 5.1989-0.371 0.399-0.822 0.762-1.193 1.161-0.372 0.399-1.511 1.472-1.376 2.007-2.548-0.026-4.357-0.455-6.344 1.197-1.537 1.289-2.083 1.186-3.947 1.947-0.3 0.242-0.871 0.455-1.407 0.589z" fill="#8B8B8B"/>
<path d="m126.33 115.32c0.15-0.121 0.186-0.2 0.336-0.321 0.036-0.078 0.15-0.121 0.222-0.278 0.036-0.078 0.036-0.078 0.036-0.078 0.172-0.585 0.344-1.17 0.709-1.762 0.215-0.47 0.544-0.983 0.838-1.418 1.245-1.896 2.96-3.577 3.771-5.766-0.549-0.251-1.098-0.502-1.641-0.56 0.272 0.029 0.507 0.137 0.706 0.323 0.079 0.035 0.121 0.15 0.164 0.264s-0.072 0.157-0.108 0.235c-2.174 2.893-4.62 5.758-5.033 9.361z" fill="#8B8B8B"/>
<path d="m136.28 102.05c0.627 0.287 1.44 0.374 2.168 0.232 0.729-0.141 1.486-0.554 2.25-0.773 0.114-0.043 0.15-0.122 0.264-0.164 1.364-0.704 5.29-3.6776 6.832-3.7319-1.349 0.0475-5.011 2.8569-6.366 2.7119 1.294-1.5887 2.741-3.1474 4.193-4.6647-1.429 1.0535-2.926 2.3052-4.113 3.6589-0.594 0.6769-1.187 1.3538-1.902 1.8808-1.393 0.975-3.477 0.971-4.452-0.422-0.121-0.15-0.242-0.3-0.248-0.493 0.092 0.421 0.219 0.764 0.461 1.064 0.164 0.264 0.52 0.522 0.913 0.701z" fill="#8B8B8B"/>
<path d="m133.03 88.789c0.65-0.1773 1.165-0.8899 1.302-1.3963 0.237-0.9344 0.396-1.9046 0.59-2.9533 0.201-0.8559 0.324-1.7477 0.604-2.5678 0.739-2.0324 2.105-3.7782 3.635-5.2598 0 0 0.035-0.0784 0.114-0.0426 1.574-2.4091 3.227-4.7824 5.079-6.9698-1.165 0.8899-2.244 2.0083-3.046 3.3484-0.623 0.948-1.088 1.9676-1.753 2.8013-0.701 0.9121-1.644 1.5241-2.345 2.4363-0.294 0.4347-0.623 0.9479-0.995 1.3469-0.155-1.1135-0.231-2.1912-0.386-3.3047-0.087 0.8134-0.174 1.6267-0.261 2.4401-0.081 1.006-0.239 1.9762-0.398 2.9464-0.725 2.4177-1.957 4.699-2.14 7.1749z" fill="#8B8B8B"/>
<path d="m110.95 127.83c0.101-0.427 0.159-0.97 0.338-1.362 0.087-0.813 0.217-1.512 0.653-2.261 0.509-0.905 1.252-1.703 2.002-2.308 1.351-1.09 2.908-1.8 4.465-2.511 0.721-0.334 1.4-0.783 1.722-1.488-0.114 0.042-0.114 0.042-0.228 0.085 0.078 0.036 0.078 0.036 0.157 0.071-2.236 1.16-4.586 2.361-6.694 3.863-0.6 0.484-1.279 0.933-1.679 1.603-0.809 1.147-0.79 2.767-0.736 4.308z" fill="#8B8B8B"/>
<path d="m125.38 118.21c0.699 0.13 1.427-0.011 2.126 0.118 0.735 0.052 1.399 0.26 2.019 0.354 1.199 0.073 2.306-0.274 3.426-0.237 0.656 0.016 1.391 0.067 2.084 4e-3 1.227-0.198 2.235-1.159 3.093-2 3.245-3.162 6.455-6.246 9.7-9.409 0.522-0.52 1.152-1.275 0.817-1.996-0.163-0.265-0.441-0.486-0.676-0.594 0.078 0.036 0.157 0.072 0.121 0.15 0.32 0.336 0.269 1.071-0.152 1.163-0.173 0.585-0.73 1.183-1.102 1.582-3.06 2.963-6.197 5.891-9.3 8.739-0.335 0.321-0.671 0.641-1.164 0.89s-0.914 0.341-1.414 0.397c-3.605 0.629-7.445 1.15-11.025 0.273-0.079-0.036-0.157-0.072-0.236-0.107 0 0 0.036-0.079 0.072-0.157-0.271-0.029-0.5 0.056-0.771 0.027 0.785 0.358 1.569 0.716 2.382 0.803z" fill="#8B8B8B"/>
<path d="m142.68 120.8c1.206 0.266 2.462-0.202 3.683-0.593 0.114-0.042 0.114-0.042 0.229-0.085 2.75-1.872 5.58-3.708 8.572-5.279-3.013 0.993-5.934 2.408-8.77 4.051-0.643 0.37-1.207 0.776-1.971 0.996-1.792 0.603-3.746-0.1-5.55-0.923 0.278 0.221 0.748 0.436 0.905 0.508 0.471 0.215 0.863 0.394 1.333 0.609 0.478 0.407 0.948 0.622 1.569 0.716z" fill="#8B8B8B"/>
<path d="m141.08 104.92c0.229-0.085 0.572-0.213 0.8-0.298 0.036-0.079 0.15-0.121 0.15-0.121 0.643-0.37 1.286-0.74 1.893-1.032 1.257-0.468 2.229-0.639 3.477-0.258 0.663 0.208 1.703 0.99 2.33 1.277 1.804 0.824 3.817 0.533 5.715 0.736 1.705 0.21 5.134-2.488 6.928-3.195 1.794-0.706 2.082-1.027 3.298-1.715 1.95-1.1037 1.972-0.9957 2.957-1.4934 1.329-0.6256 1.99-0.7126 2.49-2.2126 0-2-2.501-4.493-2.501-6.5 0.867-1.1428 1.494-0.5121 2.95-0.795 0.764-0.2198 1.528-0.4397 2.171-0.8096 1.559-1.7526 3.039-3.5411 4.562-5.2153-0.871 0.4552-1.657 1.1388-2.365 1.8583-0.93 0.9973-1.581 2.2164-2.753 2.9136-1.321 0.8183-2.984 0.7225-4.554 1.0479-2.492 0.4737 3.316 7.4351 1.044 8.6727-2.271 1.2376-3.808 2.4304-6.355 2.4044-2.433-0.068-7.736 4.533-10.062 4.229-0.735-0.052-1.512-0.217-2.254-0.461-0.506-0.137-0.977-0.351-1.526-0.602-0.627-0.287-1.176-0.538-1.84-0.746-0.392-0.179-0.82-0.279-1.205-0.266-0.464-0.022-0.957 0.227-1.335 0.433-1.365 0.704-2.729 1.408-4.015 2.148z" fill="#8B8B8B"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_1924_305497" x1="123.1" x2="122.72" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<clipPath id="clip0_1924_305497">
<rect width="200" height="200" fill="#fff"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

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

@ -0,0 +1,160 @@
<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",
"searchTags": [
"pipe",
"drain"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": "var color = ctx.properties.fluidColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
{
"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",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"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
}
],
"properties": [
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#8CAA5C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<g clip-path="url(#clip0_1924_305417)" tb:tag="clickArea">
<rect x="14" y="64" width="86" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="14" y="64" width="86" height="72" fill="url(#paint0_linear_1924_305417)"/>
<rect x="15.5" y="65.5" width="83" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/>
<ellipse transform="rotate(-90 100 100)" cx="100" cy="100" rx="36" ry="4" fill="#5D5C5C"/>
<path d="m123 94.5c-4-9.2-20-22.333-24-24.5 0 0-1.4995 18.101-1.4995 30.5 0 12.399 1.4995 33 1.4995 33 0 6.5-1.5 9-3.9998 14.5-5.0887 11.196-3.4999 10.5-12.5 13.5s-24 2.5-21.5 11.5 32-8 34-4-16 17-10.5 21.5 17.5-10.5 21.5-10.5 4.5 10.5 10 10.5 7-11.5 16-10.5 12 13 18 17 14-2 14.5-11-20.5-7-20.5-13 39 1.5 45.5-4 0.5-6.5-6.5-10.5-38 5.5-39 0 16.5 0 15-5.5-16.5-2-23-10.5-8-36.5-13-48z" fill="#8CAA5C" tb:tag="fluid-background"/>
<g style="display:none" tb:tag="leak">
<path d="m89.054 127.83c-0.1006-0.428-0.1587-0.97-0.3378-1.362-0.3378-1.362-1.0252-2.66-2.0757-3.507-0.5286-0.327-1.1357-0.619-1.707-0.832-1.521-0.632-2.9637-1.3-4.4064-1.969-1.2499-0.661-2.6926-1.329-4.0191-0.913-0.0068 0.193-0.0494 0.307-0.0562 0.5-2.3344-0.546-4.8887-0.327-7.228 0.362-1.5977 0.446-3.2089 1.276-4.8646 1.179-0.5781-0.02-1.1135-0.155-1.6131-0.211-0.885-0.07-1.7341-0.061-2.5901 0.14-0.6206 0.094-1.2481 0.381-1.8329 0.553-1.6761 0.481-3.4238 0.806-5.2364 0.78l1.5396-0.987s0.1278-0.343 0.0852-0.229c0.1501 0.121 0.3854 0.014 0.5064-0.136-0.5644-0.406-1.1289-0.812-1.6576-1.139-2.7506-1.872-5.5798-3.708-8.5725-5.28 3.0131 0.994 5.9342 2.409 8.7702 4.052 0.6429 0.37 1.2073 0.776 1.9713 0.996 1.7922 0.603 3.7461-0.1 5.55-0.924l0.0785-0.035c0.1568-0.072 0.3921-0.18 0.3989-0.372 0.0852-0.229-0.1365-0.507-0.3224-0.706-1.0079-0.962-1.9373-1.959-2.8668-2.956-1.1511-1.276-2.067-2.658-3.2966-3.898-1.0796-1.118-2.2801-2.087-3.4449-2.976-0.5644-0.406-1.0505-0.848-1.7002-1.025-0.6855-0.256-1.3778-0.319-2.1127-0.267-0.9276 0.044-1.7767 0.053-2.6685 0.176-5.1153 0.63-13.66-5.451-18.688-4.008-1.2766 0.273-2.7759 0-1.2764-1.727 2.0004-1.4995 1.9997-1.9996 1.9997-4.9996-0.7572-0.4125 2-3.5 2-5 0.5-1-0.3767-0.9415-1.069-1.0045-1.3845-0.1259-2.8118-0.1376-4.0308 0.5139 0.7126-0.5151 1.497-0.8733 2.2387-1.1172-0.1859-0.1995-0.4077-0.4774-0.5935-0.6769-1.5588-1.7526-3.0392-3.5411-4.5621-5.2153 0.8714 0.4552 1.6576 1.1388 2.3653 1.8583 0.9295 0.9973 1.581 2.2164 2.7526 2.9136 1.3216 0.8183 2.9841 0.7225 4.5546 1.048 2.4912 0.4736-3.4917 6.84-1.2203 8.0776 2.2714 1.2375 3.9327 2.7911 7.8089 4.5241 2.7935 1.249 7.3531 3.179 9.6789 2.875 0.7349-0.051 1.5124-0.216 2.2541-0.46 0.5064-0.137 0.977-0.352 1.526-0.603 0.6275-0.286 1.1765-0.537 1.8398-0.745 0.3921-0.179 0.8201-0.28 1.2055-0.266 0.4638-0.022 0.9566 0.226 1.3351 0.433 1.3643 0.704 2.7285 1.408 4.0927 2.112-0.2285-0.085-0.5712-0.213-0.7997-0.299-1.8281-0.681-3.6203-1.285-5.4754-1.196-0.3854-0.013-0.8134 0.087-1.0913 0.309-0.0784 0.036-0.1568 0.072-0.121 0.15-0.3205 0.336-0.2693 1.071 0.1519 1.163 0.1723 0.585 0.73 1.183 1.1018 1.582 3.0595 2.963 6.1974 5.891 9.2995 8.739 0.336 0.321 0.6719 0.641 1.1648 0.89 0.4928 0.249 0.914 0.341 1.4136 0.397 3.6047 0.629 7.4448 1.15 11.025 0.273 0.0785-0.036 0.1569-0.072 0.2353-0.107 0 0-0.0358-0.079-0.0716-0.157 0.2711-0.029 0.4996 0.056 0.7707 0.027 1.8194-0.167 3.5943-1.262 5.098-2.328 1.2661-1.138 1.7877-1.768 1.8202-2.377 0.0368-0.691 0.1656-3.04-1.0539-4.04-0.5644-0.406-1.7596-0.38-1.9726-0.617-0.0752-0.084-0.2984-1.622 0.2454-0.372-0.6088-0.033-6.4278-2.328-10.667-2.478-0.3853-0.013-0.8491 9e-3 -1.2771 0.11-0.2711 0.029-0.5064 0.136-0.7059 0.322-0.0784 0.036-0.121 0.15-0.1636 0.264-0.0426 0.115 0.0716 0.157 0.1074 0.236 1.9819 2.886 4.5059 5.715 4.9191 9.318-0.15-0.122-0.1859-0.2-0.3359-0.321-0.0358-0.078-0.1501-0.121-0.2217-0.278-1.2587-1.51-2.1542-3.471-3.5764-4.717-0.6362-0.563-1.3507-1.09-1.7873-1.838-0.5441-0.984-0.6109-2.375-1.3544-3.173-0.3718-0.399-0.9004-0.727-1.3575-0.897-1.2567-0.469-2.6771-0.673-3.8913-1.256-0.6071-0.292-1.2073-0.776-1.8144-1.067-0.1143-0.043-0.1501-0.121-0.2643-0.164-1.3642-0.704-5.8232-4.0432-7.3647-4.0975 1.3488 0.0475 5.5435 3.2235 6.899 3.0775-1.2944-1.5885-2.5531-3.099-4.0044-4.6164 1.4291 1.0536 2.7371 2.2572 3.9241 3.6109 0.5935 0.6765 1.187 1.3535 1.9015 1.8805 1.3933 0.975 3.477 0.971 4.4521-0.422 0.121-0.15 0.2421-0.3 0.2489-0.493 0.4193-0.9496 0.3816-2.0698 0.3797-3.1117-0.1625-3.0539-0.446-5.9577-0.8011-9.0184-0.2681-2.2473-0.6146-4.4588-1.6243-6.4622-0.0717-0.1568-0.2218-0.2779-0.3786-0.2062 0.4057-0.5645 0.1976-1.2278-0.1674-1.8194-0.2866-0.6274-0.73-1.1832-1.095-1.7749-0.3292-0.5132-0.6583-1.0264-0.9875-1.5396-1.5742-2.4091-3.2269-4.7824-5.079-6.9698 1.1647 0.8899 2.2443 2.0083 3.0459 3.3485 0.6225 0.9479 1.0882 1.9675 1.7533 2.8012 0.701 0.9122 1.644 1.5242 2.345 2.4363 0.2933 0.4348 0.6225 0.948 0.9943 1.3469 0.155-1.1135 0.2315-2.1911 0.3865-3.3046 0.0871 0.8133 0.1742 1.6267 0.2613 2.44 0.0803 1.006 0.239 1.9762 0.3978 2.9464 0.5323 2.411 1.8781 4.7349 2.0609 7.2107l-0.0204 0.5781c-0.15-0.1211-0.2217-0.2779-0.3717-0.399-0.0427 0.1143 0.029 0.2711-0.0136 0.3854-0.1531 2.1553 0.1149 4.4026 0.504 6.4999 0.2817 1.862 0.635 3.8808 1.9004 5.1984 0.3718 0.399 0.822 0.762 1.1938 1.161 0.3717 0.399 0.5799 1.062 0.4452 1.598 2.5475-0.026 5.3825-0.521 7.3692 1.131 1.5366 1.288 3.6117 0.984 3.8349 2.523-0.5763-0.642 0.962-0.865 2.1472-0.191 1.1851 0.674 1.6866 6 0.7832 7.55-0.8609 1.436-2.2504 2.545-3.7338 3.033-0.0785 0.035-0.0785 0.035-0.1569 0.071 2.2356 1.159 4.5855 2.361 6.6933 3.863 0.6003 0.484 1.279 0.933 1.6798 1.603 0.4163 1.326 0.3978 2.946 0.3434 4.487z" fill="#5C5A5A"/>
<path d="m83.058 107.1c-0.8133 0.088-1.2484 0.285-2.0481-0.013-1.5995-0.597-1.9619-1.241-3.4762-2.066-1.707-0.832-2.611-0.911-4.531-1.171-0.9992-0.113-1.9627-0.147-2.8902-0.102 0.2198-0.764-1.2619-1.965-1.8196-2.564-0.5576-0.598-1.2722-1.125-1.5161-1.8665-0.2798-0.8201-0.0958-1.6625-0.1044-2.5116-0.0445-0.9276-0.3601-1.8262-0.5973-2.7606-0.3959-1.9045-0.1353-3.8246 0.2395-5.702 0.0359 0.0784 0.0359 0.0784 0.0717 0.1569-0.1531 2.1553 0.1149 4.4026 0.504 6.4999 0.2817 1.8619 0.635 3.8808 1.9004 5.1989 0.3718 0.399 0.822 0.762 1.1938 1.161 0.3717 0.399 1.5107 1.472 1.3761 2.007 2.5474-0.026 4.3568-0.455 6.3435 1.197 1.5366 1.289 2.0832 1.186 3.9471 1.947 0.3001 0.242 0.8714 0.455 1.4068 0.589z" fill="#8B8B8B"/>
<path d="m73.673 115.32c-0.1501-0.121-0.1859-0.2-0.336-0.321-0.0358-0.078-0.15-0.121-0.2217-0.278l-0.0358-0.078c-0.1723-0.585-0.3446-1.17-0.7096-1.762-0.2149-0.47-0.5441-0.983-0.8375-1.418-1.245-1.896-2.9607-3.577-3.7709-5.766 0.549-0.251 1.098-0.502 1.6402-0.56-0.2711 0.029-0.5064 0.137-0.7059 0.323-0.0784 0.035-0.121 0.15-0.1636 0.264s0.0716 0.157 0.1074 0.235c2.1746 2.893 4.6202 5.758 5.0334 9.361z" fill="#8B8B8B"/>
<path d="m63.723 102.05c-0.6274 0.287-1.4407 0.374-2.1689 0.232-0.7281-0.141-1.4852-0.554-2.2492-0.773-0.1142-0.043-0.15-0.122-0.2643-0.164-1.3642-0.704-5.2902-3.6776-6.8317-3.7319 1.3488 0.0475 5.0105 2.8569 6.366 2.7119-1.2944-1.5887-2.7417-3.1474-4.193-4.6647 1.429 1.0535 2.9258 2.3052 4.1127 3.6589 0.5935 0.6769 1.187 1.3538 1.9016 1.8808 1.3932 0.975 3.4769 0.971 4.452-0.422 0.1211-0.15 0.2421-0.3 0.2489-0.493-0.092 0.421-0.2198 0.764-0.4619 1.064-0.1637 0.264-0.52 0.522-0.9122 0.701z" fill="#8B8B8B"/>
<path d="m66.97 88.789c-0.6496-0.1773-1.1647-0.8899-1.3012-1.3963-0.2372-0.9344-0.3959-1.9046-0.5904-2.9533-0.2014-0.8559-0.3243-1.7477-0.6041-2.5678-0.7386-2.0324-2.1047-3.7782-3.6344-5.2598 0 0-0.0359-0.0784-0.1143-0.0426-1.5742-2.4091-3.2269-4.7824-5.079-6.9698 1.1647 0.8899 2.2443 2.0083 3.0459 3.3484 0.6225 0.948 1.0882 1.9676 1.7533 2.8013 0.701 0.9121 1.644 1.5241 2.345 2.4363 0.2933 0.4347 0.6225 0.9479 0.9943 1.3469 0.155-1.1135 0.2315-2.1912 0.3865-3.3047 0.0871 0.8134 0.1742 1.6267 0.2613 2.4401 0.0803 1.006 0.239 1.9762 0.3978 2.9464 0.725 2.4177 1.9565 4.699 2.1393 7.1749z" fill="#8B8B8B"/>
<path d="m89.054 127.83c-0.1007-0.427-0.1587-0.97-0.3378-1.362-0.0871-0.813-0.2168-1.512-0.6535-2.261-0.5082-0.905-1.2518-1.703-2.0022-2.308-1.3506-1.09-2.9075-1.8-4.4644-2.511-0.7213-0.334-1.4001-0.783-1.7224-1.488 0.1142 0.042 0.1142 0.042 0.2285 0.085-0.0785 0.036-0.0785 0.036-0.1569 0.071 2.2356 1.16 4.5855 2.361 6.6933 3.863 0.6003 0.484 1.279 0.933 1.6798 1.603 0.8084 1.147 0.7899 2.767 0.7356 4.308z" fill="#8B8B8B"/>
<path d="m74.616 118.21c-0.6991 0.13-1.4272-0.011-2.1263 0.118-0.7349 0.052-1.3982 0.26-2.0188 0.354-1.1987 0.073-2.3054-0.274-3.4257-0.237-0.6565 0.016-1.3914 0.067-2.0837 4e-3 -1.2277-0.198-2.2356-1.159-3.0934-2-3.2454-3.162-6.4549-6.246-9.7003-9.409-0.5219-0.52-1.1512-1.275-0.8171-1.996 0.1637-0.265 0.4416-0.486 0.6769-0.594-0.0785 0.036-0.1569 0.072-0.1211 0.15-0.3205 0.336-0.2692 1.071 0.1519 1.163 0.1724 0.585 0.73 1.183 1.1018 1.582 3.0595 2.963 6.1974 5.891 9.2995 8.739 0.336 0.321 0.672 0.641 1.1648 0.89s0.914 0.341 1.4136 0.397c3.6048 0.629 7.4448 1.15 11.026 0.273 0.0784-0.036 0.1568-0.072 0.2353-0.107 0 0-0.0359-0.079-0.0717-0.157 0.2711-0.029 0.4996 0.056 0.7707 0.027-0.7843 0.358-1.5686 0.716-2.3819 0.803z" fill="#8B8B8B"/>
<path d="m57.315 120.8c-1.2055 0.266-2.4623-0.202-3.6832-0.593-0.1143-0.042-0.1143-0.042-0.2285-0.085-2.7507-1.872-5.5798-3.708-8.5726-5.279 3.0131 0.993 5.9343 2.408 8.7702 4.051 0.6429 0.37 1.2073 0.776 1.9713 0.996 1.7922 0.603 3.7461-0.1 5.55-0.923-0.2779 0.221-0.7484 0.436-0.9053 0.508-0.4706 0.215-0.8627 0.394-1.3333 0.609-0.4774 0.407-0.948 0.622-1.5686 0.716z" fill="#8B8B8B"/>
<path d="m58.918 104.92c-0.2285-0.085-0.5712-0.213-0.7997-0.298-0.0359-0.079-0.1501-0.121-0.1501-0.121-0.6429-0.37-1.2858-0.74-1.8929-1.032-1.2567-0.468-2.2286-0.639-3.4767-0.258-0.6633 0.208-1.7027 0.99-2.3302 1.277-1.8039 0.824-3.8166 0.533-5.7144 0.736-1.7051 0.21-5.1345-2.488-6.9285-3.195-1.794-0.706-2.0821-1.027-3.2985-1.715-1.9491-1.1037-1.9713-0.9957-2.9569-1.4934-1.3284-0.6256-1.9891-0.7126-2.4891-2.2126 0-2 2.5003-4.493 2.5003-6.5-0.8664-1.1428-1.4939-0.5121-2.9501-0.795-0.7639-0.2198-1.5279-0.4397-2.1708-0.8096-1.5587-1.7526-3.0391-3.5411-4.562-5.2153 0.8714 0.4552 1.6575 1.1388 2.3653 1.8583 0.9295 0.9973 1.581 2.2164 2.7526 2.9136 1.3216 0.8183 2.9841 0.7225 4.5545 1.0479 2.4913 0.4737-3.3159 7.4351-1.0444 8.6727 2.2714 1.2376 3.8077 2.4304 6.3551 2.4044 2.4333-0.068 7.736 4.533 10.062 4.229 0.7349-0.052 1.5125-0.217 2.2542-0.461 0.5064-0.137 0.9769-0.351 1.526-0.602 0.6274-0.287 1.1764-0.538 1.8397-0.746 0.3921-0.179 0.8201-0.279 1.2055-0.266 0.4638-0.022 0.9566 0.227 1.3352 0.433 1.3642 0.704 2.7284 1.408 4.0142 2.148z" fill="#8B8B8B"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_1924_305417" x1="36.36" x2="35.98" y1="64" y2="136" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171" offset="0"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".26388"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".41759"/>
<stop stop-color="#fff" stop-opacity="0" offset=".49829"/>
<stop stop-color="#727171" stop-opacity=".1" offset=".58094"/>
<stop stop-color="#727171" stop-opacity=".35" offset=".71855"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
<clipPath id="clip0_1924_305417">
<rect width="200" height="200" fill="#fff"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

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

@ -26,13 +26,16 @@
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
}
],
"properties": []

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

174
application/src/main/data/json/system/scada_symbols/small-left-motor-pump.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 29 KiB

123
application/src/main/data/json/system/scada_symbols/small-right-motor-pump.svg

@ -32,13 +32,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -47,13 +67,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -62,13 +102,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -77,13 +137,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
@ -92,13 +172,16 @@
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
}
],
"properties": [

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

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

@ -51,6 +51,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
@ -78,13 +83,32 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 0,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": 0,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "flowRate"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -93,13 +117,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -108,13 +152,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -123,13 +187,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "broken",
@ -138,13 +222,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.broken}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "displayClick",
@ -153,13 +257,16 @@
"group": "{i18n:scada.symbol.display}",
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "fluid",
@ -168,13 +275,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
@ -183,13 +310,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
@ -198,13 +345,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
@ -213,13 +380,32 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
@ -228,13 +414,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -509,9 +715,26 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><rect x="14" y="264" width="372" height="72" fill="#fff"/><rect x="14" y="264" width="372" height="72" fill="url(#paint0_linear_1253_89545)"/><rect x="15.5" y="265.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="14" y="264" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="264" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="M236 236V264.421L200 296L164 264.421V236H236Z" fill="#fff"/><path d="M236 236V264.421L200 296L164 264.421V236H236Z" fill="url(#paint1_linear_1253_89545)"/><path d="m234.5 237.5v26.242l-34.5 30.263-34.5-30.263v-26.242h69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="387.5" y="251.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/><rect x="1.5" y="251.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/><mask id="path-7-inside-1_1253_89545" fill="white">
}]]></tb:metadata>
<rect x="14" y="264" width="372" height="72" fill="#fff" tb:tag="pipe-background"/><rect x="14" y="264" width="372" height="72" fill="url(#paint0_linear_1253_89545)"/><rect x="15.5" y="265.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="14" y="264" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect x="14" y="264" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><path d="M236 236V264.421L200 296L164 264.421V236H236Z" fill="#fff" tb:tag="pipe-background"/><path d="M236 236V264.421L200 296L164 264.421V236H236Z" fill="url(#paint1_linear_1253_89545)"/><path d="m234.5 237.5v26.242l-34.5 30.263-34.5-30.263v-26.242h69z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect x="387.5" y="251.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/><rect x="1.5" y="251.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171" stroke-width="3"/><mask id="path-7-inside-1_1253_89545" fill="white">
<path d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z"/>
</mask><path d="m325 125c0 69.036-55.964 125-125 125s-125-55.964-125-125 55.964-125 125-125 125 55.964 125 125zm-236.01 0c0 61.306 49.699 111 111.01 111s111-49.699 111-111c0-61.306-49.699-111.01-111-111.01s-111.01 49.699-111.01 111.01z" fill="#4A4848" mask="url(#path-7-inside-1_1253_89545)" stroke="#727171" stroke-width="6" tb:tag="border"/><circle cx="200" cy="125" r="111" fill="#fff" tb:tag="background"/><rect x="119.5" y="105.5" width="161" height="39" rx="3.8571" fill="#4A4848" fill-opacity=".06" tb:tag="border"/><path d="m123.36 104c-2.9419 0-5.3574 2.4155-5.3574 5.3574v31.285c0 2.9419 2.4155 5.3574 5.3574 5.3574h153.29c2.9419 0 5.3574-2.4155 5.3574-5.3574v-31.285c0-2.9419-2.4155-5.3574-5.3574-5.3574zm0 3h153.29c1.3318 0 2.3574 1.0256 2.3574 2.3574v31.285c0 1.3318-1.0256 2.3574-2.3574 2.3574h-153.29c-1.3318 0-2.3574-1.0256-2.3574-2.3574v-31.285c0-1.3318 1.0256-2.3574 2.3574-2.3574z" fill="#4A4848" tb:tag="border"/><circle cx="166" cy="73" r="16" fill="url(#paint2_linear_1253_89545)"/><circle cx="166" cy="73" r="15.5" stroke="#000" stroke-opacity=".12"/><circle cx="234" cy="73" r="16" fill="url(#paint3_linear_1253_89545)"/><circle cx="234" cy="73" r="15.5" stroke="#000" stroke-opacity=".12"/><defs>
<linearGradient id="paint0_linear_1253_89545" x1="110.72" x2="110.63" y1="264" y2="336.01" gradientUnits="userSpaceOnUse">

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 72 KiB

171
application/src/main/data/json/system/scada_symbols/top-right-elbow-pipe.svg

@ -29,6 +29,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "vertical-fluid",
"stateRenderFunction": "var fluid = ctx.values.fluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}\n",
@ -40,71 +45,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -123,19 +232,35 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<g transform="rotate(180,100,100)" clip-path="url(#clip0_1245_66459)">
<rect x="14" y="64" width="50" height="72" fill="#fff"/>
<rect x="14" y="64" width="50" height="72" fill="#fff" tb:tag="pipe-background"/>
<rect x="14" y="64" width="50" height="72" fill="url(#paint0_linear_1245_66459)" style="fill:url(#paint0_linear_1245_66459)"/>
<rect x="15.5" y="65.5" width="47" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m64 186v-50h72v50z" fill="#fff"/>
<path d="m64 186v-50h72v50z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 186v-50h72v50z" fill="url(#paint1_linear_1245_66459)" style="fill:url(#paint1_linear_1245_66459)"/>
<path d="m65.5 184.5v-47h69v47z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="1.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(-90,51.5,198.5)" x="51.5" y="198.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="#fff" tb:tag="pipe-background"/>
<path d="m64 64s30.518 1.7177 50.4 21.6c19.882 19.882 21.6 50.4 21.6 50.4h-72z" fill="url(#paint2_linear_1245_66459)" style="fill:url(#paint2_linear_1245_66459)"/>
<path d="m65.5 134.5v-68.865c0.8334 0.0861 1.9717 0.2211 3.3584 0.4273 3.1196 0.464 7.4889 1.2873 12.47 2.7228 9.9828 2.8767 22.316 8.1809 32.01 17.875 9.695 9.6942 14.999 22.027 17.875 32.01 1.436 4.982 2.259 9.351 2.723 12.471 0.206 1.386 0.341 2.525 0.428 3.358z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
</g><defs>

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 51 KiB

387
application/src/main/data/json/system/scada_symbols/top-tee-pipe.svg

@ -30,6 +30,11 @@
"stateRenderFunction": "var fluid = (ctx.values.leftFluid || ctx.values.rightFluid ||\n ctx.values.topFluid) && !ctx.values.leak;\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "right-fluid",
"stateRenderFunction": "var fluid = ctx.values.rightFluid && !ctx.values.leak;\n\nif (fluid) {\n element.show();\n} else {\n element.hide();\n}",
@ -59,13 +64,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlow",
@ -74,13 +99,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowDirection",
@ -89,13 +134,33 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leftFlowAnimationSpeed",
@ -104,13 +169,32 @@
"group": "{i18n:scada.symbol.left-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFluid",
@ -119,13 +203,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlow",
@ -134,13 +238,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowDirection",
@ -149,13 +273,33 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "rightFlowAnimationSpeed",
@ -164,13 +308,32 @@
"group": "{i18n:scada.symbol.right-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFluid",
@ -179,13 +342,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlow",
@ -194,13 +377,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowDirection",
@ -209,13 +412,33 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "topFlowAnimationSpeed",
@ -224,27 +447,67 @@
"group": "{i18n:scada.symbol.top-pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -295,13 +558,29 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><g transform="translate(-4)" clip-path="url(#clip0_1245_66653)">
<path d="m18 64h172v72h-172z" fill="#fff"/>
<path d="m18 64h172v72h-172z" fill="#fff" tb:tag="pipe-background"/>
<path d="m18 64h172v72h-172z" fill="url(#paint0_linear_1245_66653)" style="fill:url(#paint0_linear_1245_66653)"/>
<path d="m19.5 65.5h169v69h-169z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m140 14v51l-36 35-36-35v-51z" fill="#fff"/>
<path d="m140 14v51l-36 35-36-35v-51z" fill="#fff" tb:tag="pipe-background"/>
<path d="m140 14v51l-36 35-36-35v-51z" fill="url(#paint1_linear_1245_66653)" style="fill:url(#paint1_linear_1245_66653)"/>
<path d="m138.5 15.5v48.866l-34.5 33.542-34.5-33.542v-48.866z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect x="5.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 57 KiB

82
application/src/main/data/json/system/scada_symbols/vertical-ball-valve.svg

@ -37,13 +37,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.opened}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "open",
@ -52,13 +72,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": true,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "close",
@ -67,13 +106,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": true,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
}
],
"properties": [

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

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

@ -1,4 +1,5 @@
<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[{
<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": "Vertical inline flow meter",
"description": "Vertical inline flow meter component used to display flow related value and render various states. Includes pipe fluid and leak visualizations.",
"searchTags": [
@ -50,6 +51,11 @@
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "var value = ctx.values.value;\nctx.api.text(element, value.toFixed(0));\n",
@ -77,13 +83,32 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 0,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": 0,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "flowRate"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
@ -92,13 +117,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
@ -107,13 +152,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
@ -122,13 +187,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "broken",
@ -137,13 +222,33 @@
"group": "{i18n:scada.symbol.display}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.broken}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "displayClick",
@ -152,13 +257,16 @@
"group": "{i18n:scada.symbol.display}",
"type": "widgetAction",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"openInSeparateDialog": false,
"openInPopover": false
}
},
{
"id": "fluid",
@ -167,13 +275,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
@ -182,13 +310,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
@ -197,13 +345,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
@ -212,13 +380,32 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
@ -227,13 +414,33 @@
"group": "{i18n:scada.symbol.pipe}",
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -508,10 +715,26 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata>
<rect transform="rotate(-90)" x="-386" y="64" width="372" height="72" fill="#fff"/><rect transform="rotate(-90)" x="-386" y="64" width="372" height="72" fill="url(#paint0_linear_1595_101503)" style="fill:url(#paint0_linear_1595_101503)"/><rect transform="rotate(-90)" x="-384.5" y="65.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(-90)" x="-12.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(-90)" x="-398.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(90)" x="14" y="-136" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect transform="rotate(90)" x="14" y="-136" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><defs>
<rect transform="rotate(-90)" x="-386" y="64" width="372" height="72" fill="#fff" tb:tag="pipe-background"/><rect transform="rotate(-90)" x="-386" y="64" width="372" height="72" fill="url(#paint0_linear_1595_101503)" style="fill:url(#paint0_linear_1595_101503)"/><rect transform="rotate(-90)" x="-384.5" y="65.5" width="369" height="69" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(-90)" x="-12.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(-90)" x="-398.5" y="51.5" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(90)" x="14" y="-136" width="372" height="72" fill="#1ec1f4" stroke-width="0" style="display: none;" tb:tag="fluid-background"/><rect transform="rotate(90)" x="14" y="-136" width="372" height="72" fill="url(#liquid)" stroke-width="0" style="display: none;" tb:tag="fluid"/><defs>
<mask id="path-5-inside-1_1595_102361" fill="#ffffff">
<path d="m300 100c0 55.228-44.772 100-100 100s-100-44.772-100-100 44.772-100 100-100 100 44.772 100 100zm-188 0c0 48.603 39.401 88.004 88.004 88.004s88.004-39.401 88.004-88.004c0-48.604-39.401-88.004-88.004-88.004s-88.004 39.401-88.004 88.004z"/>
</mask>

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 76 KiB

168
application/src/main/data/json/system/scada_symbols/vertical-pipe.svg

@ -23,6 +23,11 @@
"tag": "leak",
"stateRenderFunction": "var leak = ctx.values.leak;\nif (leak) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "pipe-background",
"stateRenderFunction": "var color = ctx.properties.pipeColor;\nelement.attr({fill: color});",
"actions": null
}
],
"behavior": [
@ -30,71 +35,175 @@
"id": "fluid",
"name": "{i18n:scada.symbol.fluid-presence}",
"hint": "{i18n:scada.symbol.fluid-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.fluid-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flow",
"name": "{i18n:scada.symbol.flow-presence}",
"hint": "{i18n:scada.symbol.flow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.flow-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowDirection",
"name": "{i18n:scada.symbol.flow-direction}",
"hint": "{i18n:scada.symbol.flow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": true,
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "flowAnimationSpeed",
"name": "{i18n:scada.symbol.flow-animation-speed}",
"hint": "{i18n:scada.symbol.flow-animation-speed-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"defaultValue": 1,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 1,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "leak",
"name": "{i18n:scada.symbol.leak}",
"hint": "{i18n:scada.symbol.leak-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.leak-present}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -113,9 +222,26 @@
"min": null,
"max": null,
"step": null
},
{
"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
}
]
}]]></tb:metadata><path d="m64 186v-172h72v172h-72z" fill="#fff"/><path d="m64 186v-172h72v172h-72z" fill="url(#paint0_linear_1245_55957)"/><g stroke-width="3">
}]]></tb:metadata>
<path d="m64 186v-172h72v172h-72z" fill="#fff" tb:tag="pipe-background"/><path d="m64 186v-172h72v172h-72z" fill="url(#paint0_linear_1245_55957)"/><g stroke-width="3">
<path d="m65.5 184.5v-169h69v169h-69z" stroke="#000" stroke-opacity=".12"/>
<rect transform="rotate(-90 51.5 198.5)" x="51.5" y="198.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171"/>
<rect transform="rotate(-90 51.5 12.5)" x="51.5" y="12.5" width="11" height="97" rx="5.5" fill="#D9D9D9" stroke="#727171"/>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

24
application/src/main/data/json/system/scada_symbols/vertical-tank-with-level.svg

@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="168" height="280" viewBox="0 0 168 280" fill="none">
<tb:metadata xmlns=""><![CDATA[{
"title": "Vertical tank with level",
"widgetSizeX": 3,
"widgetSizeY": 5,
"tags": [],
"behavior": [],
"properties": []
}]]></tb:metadata><path d="M0 55C-4.39865e-05 -7.62939e-06 56.5 0 56.5 0H84H112.5C112.5 0 168 0 168 55C168 98.4236 168 213.531 168 260.063C168 271.109 159.046 280 148 280H20C8.9543 280 2.24098e-06 271.109 4.61562e-06 260.063C1.46193e-05 213.531 3.47283e-05 98.4236 0 55Z" fill="url(#paint0_linear_828_77723)"/><path d="M0 55C-4.39865e-05 -7.62939e-06 56.5 0 56.5 0H84H112.5C112.5 0 168 0 168 55C168 98.4236 168 213.531 168 260.063C168 271.109 159.046 280 148 280H20C8.9543 280 2.24098e-06 271.109 4.61562e-06 260.063C1.46193e-05 213.531 3.47283e-05 98.4236 0 55Z" fill="url(#paint1_linear_828_77723)" fill-opacity="0.2"/><rect x="0.5" y="57.5" width="3" height="167" transform="rotate(-90 0.5 57.5)" fill="#D9D9D9" stroke="#727171"/><rect x="130.5" y="90.5" width="15" height="119" rx="1.5" fill="#4A4848" stroke="#7D7D7D"/><mask id="mask0_828_77723" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="131" y="90" width="14" height="120">
<rect x="131" y="90.8571" width="14" height="118.286" rx="1" fill="#D9D9D9"/>
</mask><g mask="url(#mask0_828_77723)">
<rect x="131" y="126.857" width="14" height="120" fill="#62A5F2"/>
</g><defs>
<linearGradient id="paint0_linear_828_77723" x1="168" y1="153.243" x2="-4.02657e-05" y2="153.243" gradientUnits="userSpaceOnUse">
<stop stop-color="#727171"/>
<stop offset="0.504644" stop-color="#CACACA"/>
<stop offset="1" stop-color="#727171"/>
</linearGradient>
<linearGradient id="paint1_linear_828_77723" x1="84" y1="228.919" x2="84" y2="280" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

1394
application/src/main/data/json/system/scada_symbols/vertical-tank.svg

File diff suppressed because it is too large

After

Width:  |  Height:  |  Size: 113 KiB

112
application/src/main/data/json/system/scada_symbols/vertical-wheel-valve.svg

@ -1,5 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="400" fill="none" version="1.1" viewBox="0 0 200 400">
<tb:metadata><![CDATA[{
<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": "Vertical wheel valve",
"description": "Vertical wheel valve with open/close animation and state colors.",
"searchTags": [
@ -37,13 +36,33 @@
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.opened}",
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "open",
@ -52,13 +71,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": true,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": true,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "close",
@ -67,13 +105,32 @@
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"defaultValue": false,
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"valueToDataType": "CONSTANT",
"constantValue": false,
"valueToDataFunction": ""
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "open"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
}
],
"properties": [
@ -143,16 +200,7 @@
}
]
}]]></tb:metadata>
<path d="m171.26 200c0-28.161-0.82311-44.15-5-72-4.2988-28.662-17-72-17-72h13.99c4.4182 0 8-3.5817 8-8v-26c0-4.4183-3.5818-8-8-8h-126c-4.418 0-8 3.5817-8 8v26c0 4.4183 3.582 8 8 8h14.01s-12.711 43.238-17 72c-4.151 27.834-5.195 43.859-5 72 0.19499 28.16 1.823 44.15 6 72 4.299 28.663 16 72 16 72h-14.01c-4.418 0-8 3.582-8 8v26c0 4.418 3.582 8 8 8h126c4.4182 0 8-3.582 8-8v-26c0-4.418-3.5818-8-8-8h-13.99s12.701-43.337 17-72c4.1769-27.85 5-43.839 5-72z" fill="#1c943e" tb:tag="background"/>
<rect transform="rotate(90)" x="1.5" y="-148.25" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<rect transform="rotate(90)" x="14" y="-170.75" width="42" height="142" rx="8" fill="url(#paint0_linear_1343_53957)" style="fill:url(#paint0_linear_1343_53957)"/>
<rect transform="rotate(90)" x="15.5" y="-169.25" width="39" height="139" rx="6.5" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<path d="m148.76 56s12.701 43.338 17 72c4.1769 27.85 5 43.839 5 72s-0.8231 44.15-5 72c-4.2988 28.663-17 72-17 72h-98s-11.701-43.337-16-72c-4.177-27.85-5.806-43.84-6-72-0.195-28.141 0.849-44.166 5-72 4.289-28.762 17-72 17-72z" fill="url(#paint1_linear_1343_53957)" style="fill:url(#paint1_linear_1343_53957)"/>
<path d="m147.87 341.67c-0.09 0.314-0.1699 0.591-0.239 0.831h-95.722c-0.062-0.233-0.133-0.501-0.212-0.802-0.328-1.238-0.801-3.04-1.382-5.292-1.163-4.505-2.758-10.809-4.487-18.008-3.46-14.409-7.448-32.357-9.587-46.62-4.169-27.798-5.789-43.715-5.983-71.788-0.194-28.068 0.844-44.015 4.983-71.769 2.133-14.3 6.366-32.237 10.078-46.632 1.854-7.1905 3.574-13.484 4.83-17.98 0.629-2.2476 1.141-4.0454 1.496-5.2807 0.09-0.3128 0.17-0.5896 0.239-0.8284h95.747c0.0691 0.2396 0.149 0.5174 0.239 0.8314 0.3548 1.2379 0.8668 3.0393 1.4947 5.2909 1.2558 4.5034 2.9751 10.806 4.8284 18.003 3.7103 14.408 7.9439 32.345 10.081 46.596 4.1635 27.761 4.9834 43.674 4.9834 71.778s-0.8199 44.017-4.9834 71.778c-2.1374 14.251-6.371 32.189-10.081 46.596-1.8533 7.197-3.5726 13.5-4.8284 18.004-0.6279 2.251-1.1399 4.053-1.4947 5.291z" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect transform="rotate(90)" x="344" y="-170.75" width="42" height="142" rx="8" fill="url(#paint2_linear_1343_53957)" style="fill:url(#paint2_linear_1343_53957)"/>
<rect transform="rotate(90)" x="345.5" y="-169.25" width="39" height="139" rx="6.5" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<rect transform="rotate(90)" x="387.5" y="-148.25" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/>
<defs>
<path d="m171.26 200c0-28.161-0.82311-44.15-5-72-4.2988-28.662-17-72-17-72h13.99c4.4182 0 8-3.5817 8-8v-26c0-4.4183-3.5818-8-8-8h-126c-4.418 0-8 3.5817-8 8v26c0 4.4183 3.582 8 8 8h14.01s-12.711 43.238-17 72c-4.151 27.834-5.195 43.859-5 72 0.19499 28.16 1.823 44.15 6 72 4.299 28.663 16 72 16 72h-14.01c-4.418 0-8 3.582-8 8v26c0 4.418 3.582 8 8 8h126c4.4182 0 8-3.582 8-8v-26c0-4.418-3.5818-8-8-8h-13.99s12.701-43.337 17-72c4.1769-27.85 5-43.839 5-72z" fill="#1c943e" tb:tag="background"/><rect transform="rotate(90)" x="1.5" y="-148.25" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><rect transform="rotate(90)" x="14" y="-170.75" width="42" height="142" rx="8" fill="url(#paint0_linear_1343_53957)" style="fill:url(#paint0_linear_1343_53957)"/><rect transform="rotate(90)" x="15.5" y="-169.25" width="39" height="139" rx="6.5" stroke="#000" stroke-opacity=".12" stroke-width="3"/><path d="m148.76 56s12.701 43.338 17 72c4.1769 27.85 5 43.839 5 72s-0.8231 44.15-5 72c-4.2988 28.663-17 72-17 72h-98s-11.701-43.337-16-72c-4.177-27.85-5.806-43.84-6-72-0.195-28.141 0.849-44.166 5-72 4.289-28.762 17-72 17-72z" fill="url(#paint1_linear_1343_53957)" style="fill:url(#paint1_linear_1343_53957)"/><path d="m147.87 341.67c-0.09 0.314-0.1699 0.591-0.239 0.831h-95.722c-0.062-0.233-0.133-0.501-0.212-0.802-0.328-1.238-0.801-3.04-1.382-5.292-1.163-4.505-2.758-10.809-4.487-18.008-3.46-14.409-7.448-32.357-9.587-46.62-4.169-27.798-5.789-43.715-5.983-71.788-0.194-28.068 0.844-44.015 4.983-71.769 2.133-14.3 6.366-32.237 10.078-46.632 1.854-7.1905 3.574-13.484 4.83-17.98 0.629-2.2476 1.141-4.0454 1.496-5.2807 0.09-0.3128 0.17-0.5896 0.239-0.8284h95.747c0.0691 0.2396 0.149 0.5174 0.239 0.8314 0.3548 1.2379 0.8668 3.0393 1.4947 5.2909 1.2558 4.5034 2.9751 10.806 4.8284 18.003 3.7103 14.408 7.9439 32.345 10.081 46.596 4.1635 27.761 4.9834 43.674 4.9834 71.778s-0.8199 44.017-4.9834 71.778c-2.1374 14.251-6.371 32.189-10.081 46.596-1.8533 7.197-3.5726 13.5-4.8284 18.004-0.6279 2.251-1.1399 4.053-1.4947 5.291z" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(90)" x="344" y="-170.75" width="42" height="142" rx="8" fill="url(#paint2_linear_1343_53957)" style="fill:url(#paint2_linear_1343_53957)"/><rect transform="rotate(90)" x="345.5" y="-169.25" width="39" height="139" rx="6.5" stroke="#000" stroke-opacity=".12" stroke-width="3"/><rect transform="rotate(90)" x="387.5" y="-148.25" width="11" height="97" rx="5.5" fill="#d9d9d9" stroke="#727171" stroke-width="3"/><defs>
<filter id="filter0_ii_1343_53957" x="166.88" y="67.863" width="66.25" height="66.25" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
@ -237,24 +285,16 @@
<stop stop-color="#fff" offset=".49829"/>
<stop stop-color="#727171" offset="1"/>
</linearGradient>
</defs>
<g tb:tag="wheel">
</defs><g tb:tag="wheel">
<path d="m200 200c0 55.229-44.772 100-100 100s-100-44.771-100-100c0-55.228 44.772-100 100-100s100 44.772 100 100zm-175.01 0c0 41.428 33.584 75.012 75.012 75.012s75.012-33.584 75.012-75.012-33.584-75.012-75.012-75.012-75.012 33.584-75.012 75.012z" fill="url(#paint3_radial_1343_53957)" style="fill:url(#paint3_radial_1343_53957)"/>
<path d="m94.12 175c-1.726 0-3.125-1.3991-3.125-3.125v-46.625c0-3.4517 2.798-6.2499 6.25-6.2499h5.5c3.452 0 6.25 2.7982 6.25 6.2499v46.625c0 1.7259-1.399 3.125-3.125 3.125z" fill="url(#paint4_linear_1343_53957)" style="fill:url(#paint4_linear_1343_53957)"/>
<path d="m122.98 188.92c-0.612-1.6137 0.2-3.418 1.814-4.0301l43.595-16.534c3.227-1.2241 6.836 0.3999 8.06 3.6273l2.217 5.8439c1.224 3.2274-0.4 6.8361-3.628 8.0602l-43.595 16.535c-1.613 0.612-3.418-0.2-4.03-1.814l-4.433-11.688z" fill="url(#paint5_linear_1343_53957)" style="fill:url(#paint5_linear_1343_53957)"/>
<path d="m90.37 225.55c1.37 1.05 1.63 3.012 0.58 4.382l-28.362 37.006c-2.1 2.74-6.023 3.259-8.762 1.159l-4.961-3.802c-2.74-2.099-3.259-6.022-1.159-8.762l28.362-37.007c1.05-1.37 3.011-1.629 4.381-0.579z" fill="url(#paint6_linear_1343_53957)" style="fill:url(#paint6_linear_1343_53957)"/>
<path d="m111.85 225.55c-1.37 1.05-1.63 3.012-0.58 4.382l28.362 37.006c2.1 2.74 6.023 3.259 8.762 1.159l4.961-3.802c2.74-2.099 3.259-6.022 1.159-8.762l-28.362-37.007c-1.05-1.37-3.011-1.629-4.381-0.579z" fill="url(#paint7_linear_1343_53957)" style="fill:url(#paint7_linear_1343_53957)"/>
<path d="m73.44 197.47c-0.506 1.6506-2.253 2.5776-3.903 2.0716l-44.578-13.664c-3.3-1.0116-5.156-4.507-4.144-7.8072l1.832-5.9756c1.011-3.3002 4.507-5.1555 7.807-4.1439l44.578 13.664c1.65 0.5058 2.577 2.2535 2.072 3.9036l-3.664 11.951z" fill="url(#paint8_linear_1343_53957)" style="fill:url(#paint8_linear_1343_53957)"/>
</g>
<g transform="translate(-100,100)" filter="url(#filter0_ii_1343_53957)">
</g><g transform="translate(-100,100)" filter="url(#filter0_ii_1343_53957)">
<circle cx="200" cy="100.99" r="30" fill="#d9d9d9"/>
</g>
<path d="m123 200.99c0 12.703-10.297 23-23 23s-23-10.297-23-23c0-12.702 10.297-23 23-23s23 10.297 23 23z" fill="#000" fill-opacity=".05"/>
<g transform="translate(-100,100)" filter="url(#filter1_i_1343_53957)">
</g><path d="m123 200.99c0 12.703-10.297 23-23 23s-23-10.297-23-23c0-12.702 10.297-23 23-23s23 10.297 23 23z" fill="#000" fill-opacity=".05"/><g transform="translate(-100,100)" filter="url(#filter1_i_1343_53957)">
<path d="m223 101c0 12.703-10.297 23-23 23s-23-10.297-23-23c0-12.702 10.297-23 23-23s23 10.298 23 23z" fill="#1c943e" tb:tag="background"/>
</g>
<path d="m122 201c0 12.15-9.85 22-22 22s-22-9.85-22-22 9.85-22 22-22 22 9.8497 22 22z" stroke="#fff" stroke-width="2"/>
<path d="m97.28 191.61c0-0.647-0.611-1.1214-1.216-0.8901-4.132 1.5826-7.067 5.5867-7.067 10.276 0 6.075 4.925 11 11 11s11-4.925 11-11c0-4.5645-2.78-8.4795-6.739-10.144-0.609-0.2563-1.244 0.2209-1.244 0.882 0 0.4223 0.269 0.7938 0.655 0.9649 3.181 1.4097 5.399 4.5944 5.399 8.2976 0 5.01-4.061 9.071-9.071 9.071s-9.071-4.061-9.071-9.071c0-3.8069 2.344-7.0659 5.668-8.4119 0.402-0.1627 0.686-0.5409 0.686-0.9745z" clip-rule="evenodd" fill="#fff" fill-rule="evenodd"/>
<path d="m99.51 188.64c0-0.3519 0.285-0.6372 0.637-0.6372s0.637 0.2853 0.637 0.6372v10.401c0 0.3519-0.285 0.6372-0.637 0.6372s-0.637-0.2853-0.637-0.6372z" fill="#fff"/>
<rect y="100" width="200" height="200" fill="#000" fill-opacity="0" stroke-width="0" tb:tag="clickArea"/>
</g><path d="m122 201c0 12.15-9.85 22-22 22s-22-9.85-22-22 9.85-22 22-22 22 9.8497 22 22z" stroke="#fff" stroke-width="2"/><path d="m97.28 191.61c0-0.647-0.611-1.1214-1.216-0.8901-4.132 1.5826-7.067 5.5867-7.067 10.276 0 6.075 4.925 11 11 11s11-4.925 11-11c0-4.5645-2.78-8.4795-6.739-10.144-0.609-0.2563-1.244 0.2209-1.244 0.882 0 0.4223 0.269 0.7938 0.655 0.9649 3.181 1.4097 5.399 4.5944 5.399 8.2976 0 5.01-4.061 9.071-9.071 9.071s-9.071-4.061-9.071-9.071c0-3.8069 2.344-7.0659 5.668-8.4119 0.402-0.1627 0.686-0.5409 0.686-0.9745z" clip-rule="evenodd" fill="#fff" fill-rule="evenodd"/><path d="m99.51 188.64c0-0.3519 0.285-0.6372 0.637-0.6372s0.637 0.2853 0.637 0.6372v10.401c0 0.3519-0.285 0.6372-0.637 0.6372s-0.637-0.2853-0.637-0.6372z" fill="#fff"/><rect y="100" width="200" height="200" fill="#000" fill-opacity="0" stroke-width="0" tb:tag="clickArea"/>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

10
application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json

@ -22,6 +22,12 @@
"bottom_tee_pipe",
"right_tee_pipe",
"top_tee_pipe",
"right_elbow_drain_pipe",
"left_elbow_drain_pipe",
"left_drain_pipe",
"right_drain_pipe",
"short_left_drain_pipe",
"short_right_drain_pipe",
"top_flow_meter",
"right_flow_meter",
"bottom_flow_meter",
@ -41,8 +47,6 @@
"vertical_wheel_valve",
"horizontal_ball_valve",
"vertical_ball_valve",
"horizontal_tank_with_screen",
"vertical_tank_with_level",
"level_and_fan"
"vertical_tank"
]
}

26
application/src/main/data/upgrade/3.7.0/schema_update.sql

@ -68,14 +68,34 @@ $$;
CREATE SEQUENCE IF NOT EXISTS attribute_kv_version_seq cache 1;
CREATE SEQUENCE IF NOT EXISTS ts_kv_latest_version_seq cache 1;
ALTER TABLE attribute_kv ADD COLUMN version bigint default 0;
ALTER TABLE ts_kv_latest ADD COLUMN version bigint default 0;
ALTER TABLE attribute_kv ADD COLUMN IF NOT EXISTS version bigint default 0;
ALTER TABLE ts_kv_latest ADD COLUMN IF NOT EXISTS version bigint default 0;
-- KV VERSIONING UPDATE END
-- RELATION VERSIONING UPDATE START
CREATE SEQUENCE IF NOT EXISTS relation_version_seq cache 1;
ALTER TABLE relation ADD COLUMN version bigint default 0;
ALTER TABLE relation ADD COLUMN IF NOT EXISTS version bigint default 0;
-- RELATION VERSIONING UPDATE END
-- ENTITIES VERSIONING UPDATE START
ALTER TABLE device ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE device_profile ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE device_credentials ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE asset ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE asset_profile ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE entity_view ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE tb_user ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE customer ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE edge ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE rule_chain ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE dashboard ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE widget_type ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE widgets_bundle ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
ALTER TABLE tenant ADD COLUMN IF NOT EXISTS version BIGINT DEFAULT 1;
-- ENTITIES VERSIONING UPDATE END

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

@ -0,0 +1,68 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http.converter.xml;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.Assert;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
/**
* RestTemplate firstly uses MappingJackson2XmlHttpMessageConverter converter instead of MappingJackson2HttpMessageConverter.
* It produces error UnsupportedMediaType, so this converter had to be shadowed for read and write operations to use the correct converter
*/
public class MappingJackson2XmlHttpMessageConverter extends AbstractJackson2HttpMessageConverter {
private static final List<MediaType> problemDetailMediaTypes;
public MappingJackson2XmlHttpMessageConverter() {
this(Jackson2ObjectMapperBuilder.xml().build());
}
public MappingJackson2XmlHttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, new MediaType[]{new MediaType("application", "xml", StandardCharsets.UTF_8), new MediaType("text", "xml", StandardCharsets.UTF_8), new MediaType("application", "*+xml", StandardCharsets.UTF_8)});
Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required");
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.isInstanceOf(XmlMapper.class, objectMapper, "XmlMapper required");
super.setObjectMapper(objectMapper);
}
protected List<MediaType> getMediaTypesForProblemDetail() {
return problemDetailMediaTypes;
}
static {
problemDetailMediaTypes = Collections.singletonList(MediaType.APPLICATION_PROBLEM_XML);
}
@Override
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
}

2
application/src/main/java/org/thingsboard/server/actors/shared/ComponentMsgProcessor.java

@ -78,7 +78,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
}
public void scheduleStatsPersistTick(TbActorCtx context, long statsPersistFrequency) {
schedulePeriodicMsgWithDelay(context, new StatsPersistTick(), statsPersistFrequency, statsPersistFrequency);
schedulePeriodicMsgWithDelay(context, StatsPersistTick.INSTANCE, statsPersistFrequency, statsPersistFrequency);
}
protected boolean checkMsgValid(TbMsg tbMsg) {

4
application/src/main/java/org/thingsboard/server/actors/stats/StatsPersistTick.java

@ -18,7 +18,9 @@ package org.thingsboard.server.actors.stats;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
public final class StatsPersistTick implements TbActorMsg {
public enum StatsPersistTick implements TbActorMsg {
INSTANCE;
@Override
public MsgType getMsgType() {
return MsgType.STATS_PERSIST_TICK_MSG;

3
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -65,6 +65,7 @@ import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.exception.EntityVersionMismatchException;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmCommentId;
@ -390,6 +391,8 @@ public abstract class BaseController {
} else {
return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL);
}
} else if (exception instanceof EntityVersionMismatchException) {
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.VERSION_CONFLICT);
}
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL);
}

26
application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java

@ -793,7 +793,7 @@ public class ControllerConstants {
" * 'SHARED_ATTRIBUTE' - used for shared attributes; \n" +
" * 'SERVER_ATTRIBUTE' - used for server attributes; \n" +
" * 'ATTRIBUTE' - used for any of the above; \n" +
" * 'TIME_SERIES' - used for time-series values; \n" +
" * 'TIME_SERIES' - used for time series values; \n" +
" * 'ENTITY_FIELD' - used for accessing entity fields like 'name', 'label', etc. The list of available fields depends on the entity type; \n" +
" * 'ALARM_FIELD' - similar to entity field, but is used in alarm queries only; \n" +
"\n\n Let's review the example:\n\n" +
@ -904,7 +904,7 @@ public class ControllerConstants {
protected static final String KEY_FILTERS =
"\n\n # Key Filters" +
"\nKey Filter allows you to define complex logical expressions over entity field, attribute or latest time-series value. The filter is defined using 'key', 'valueType' and 'predicate' objects. " +
"\nKey Filter allows you to define complex logical expressions over entity field, attribute or latest time series value. The filter is defined using 'key', 'valueType' and 'predicate' objects. " +
"Single Entity Query may have zero, one or multiple predicates. If multiple filters are defined, they are evaluated using logical 'AND'. " +
"The example below checks that temperature of the entity is above 20 degrees:" +
"\n\n" + MARKDOWN_CODE_BLOCK_START +
@ -935,7 +935,7 @@ public class ControllerConstants {
"For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" +
"\n\nOptional **key filters** allow to filter results of the entity filter by complex criteria against " +
"main entity fields (name, label, type, etc), attributes and telemetry. " +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"." +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and time series field 'batteryLevel' > 40\"." +
"\n\nLet's review the example:" +
"\n\n" + MARKDOWN_CODE_BLOCK_START +
"{\n" +
@ -970,13 +970,13 @@ public class ControllerConstants {
protected static final String ENTITY_DATA_QUERY_DESCRIPTION =
"Allows to run complex queries over platform entities (devices, assets, customers, etc) " +
"based on the combination of main entity filter and multiple key filters. " +
"Returns the paginated result of the query that contains requested entity fields and latest values of requested attributes and time-series data.\n\n" +
"Returns the paginated result of the query that contains requested entity fields and latest values of requested attributes and time series data.\n\n" +
"# Query Definition\n\n" +
"\n\nMain **entity filter** is mandatory and defines generic search criteria. " +
"For example, \"find all devices with profile 'Moisture Sensor'\" or \"Find all devices related to asset 'Building A'\"" +
"\n\nOptional **key filters** allow to filter results of the **entity filter** by complex criteria against " +
"main entity fields (name, label, type, etc), attributes and telemetry. " +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and timeseries field 'batteryLevel' > 40\"." +
"For example, \"temperature > 20 or temperature< 10\" or \"name starts with 'T', and attribute 'model' is 'T1000', and time series field 'batteryLevel' > 40\"." +
"\n\nThe **entity fields** and **latest values** contains list of entity fields and latest attribute/telemetry fields to fetch for each entity." +
"\n\nThe **page link** contains information about the page to fetch and the sort ordering." +
"\n\nLet's review the example:" +
@ -1054,7 +1054,7 @@ public class ControllerConstants {
protected static final String ALARM_DATA_QUERY_DESCRIPTION = "This method description defines how Alarm Data Query extends the Entity Data Query. " +
"See method 'Find Entity Data by Query' first to get the info about 'Entity Data Query'." +
"\n\n The platform will first search the entities that match the entity and key filters. Then, the platform will use 'Alarm Page Link' to filter the alarms related to those entities. " +
"Finally, platform fetch the properties of alarm that are defined in the **'alarmFields'** and combine them with the other entity, attribute and latest time-series fields to return the result. " +
"Finally, platform fetch the properties of alarm that are defined in the **'alarmFields'** and combine them with the other entity, attribute and latest time series fields to return the result. " +
"\n\n See example of the alarm query below. The query will search first 100 active alarms with type 'Temperature Alarm' or 'Fire Alarm' for any device with current temperature > 0. " +
"The query will return combination of the entity fields: name of the device, device model and latest temperature reading and alarms fields: createdTime, type, severity and status: " +
"\n\n" + MARKDOWN_CODE_BLOCK_START +
@ -1175,7 +1175,7 @@ public class ControllerConstants {
protected static final String ALARM_FILTER_KEY = "## Alarm Filter Key" + NEW_LINE +
"Filter Key defines either entity field, attribute, telemetry or constant. It is a JSON object that consists the key name and type. The following filter key types are supported:\n" +
" * 'ATTRIBUTE' - used for attributes values;\n" +
" * 'TIME_SERIES' - used for time-series values;\n" +
" * 'TIME_SERIES' - used for time series values;\n" +
" * 'ENTITY_FIELD' - used for accessing entity fields like 'name', 'label', etc. The list of available fields depends on the entity type;\n" +
" * 'CONSTANT' - constant value specified." + NEW_LINE + "Let's review the example:" + NEW_LINE +
MARKDOWN_CODE_BLOCK_START +
@ -1293,7 +1293,7 @@ public class ControllerConstants {
protected static final String KEY_FILTERS_DESCRIPTION = "# Key Filters" + NEW_LINE +
"Key filter objects are created under the **'condition'** array. They allow you to define complex logical expressions over entity field, " +
"attribute, latest time-series value or constant. The filter is defined using 'key', 'valueType', " +
"attribute, latest time series value or constant. The filter is defined using 'key', 'valueType', " +
"'value' (refers to the value of the 'CONSTANT' alarm filter key type) and 'predicate' objects. Let's review each object:" + NEW_LINE +
ALARM_FILTER_KEY + FILTER_VALUE_TYPE + NEW_LINE + DEVICE_PROFILE_FILTER_PREDICATE + NEW_LINE;
@ -1606,7 +1606,7 @@ public class ControllerConstants {
protected static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'. See API call description for more details.";
protected static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys.";
protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest timeseries. For example, 'temperature,humidity'.";
protected static final String TELEMETRY_KEYS_DESCRIPTION = TELEMETRY_KEYS_BASE_DESCRIPTION + " If keys are not selected, the result will return all latest time series. For example, 'temperature,humidity'.";
protected static final String TELEMETRY_SCOPE_DESCRIPTION = "Value is deprecated, reserved for backward compatibility and not used in the API call implementation. Specify any scope for compatibility";
protected static final String TELEMETRY_JSON_REQUEST_DESCRIPTION = "A JSON with the telemetry values. See API call description for more details.";
@ -1622,11 +1622,11 @@ public class ControllerConstants {
protected static final String SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED = "User is not authorized to save entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant.";
protected static final String SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_OK = "Timeseries from the request was created or updated. " +
"Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED'.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED = "User is not authorized to save entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_OK = "Time series from the request was created or updated. " +
"Platform creates an audit log event about entity time series updates with action type 'TIMESERIES_UPDATED'.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED = "User is not authorized to save entity time series for selected entity. Most likely, User belongs to different Customer or Tenant.";
protected static final String SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity timeseries updates with action type 'TIMESERIES_UPDATED' that includes an error stacktrace.";
"Platform creates an audit log event about entity time series updates with action type 'TIMESERIES_UPDATED' that includes an error stacktrace.";
protected static final String ENTITY_ATTRIBUTE_SCOPES_TEMPLATE = " List of possible attribute scopes depends on the entity type: " +
"\n\n * SERVER_SCOPE - supported for all entity types;" +

4
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -125,8 +125,8 @@ public class DeviceProfileController extends BaseController {
return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId()));
}
@ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
notes = "Get a set of unique time-series keys used by devices that belong to specified profile. " +
@ApiOperation(value = "Get time series keys (getTimeseriesKeys)",
notes = "Get a set of unique time series keys used by devices that belong to specified profile. " +
"If profile is not set returns a list of unique keys among all profiles. " +
"The call is used for auto-complete in the UI forms. " +
"The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " +

2
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -490,7 +490,7 @@ public class EdgeController extends BaseController {
if (fromEdgeSyncResponse.isSuccess()) {
response.setResult(new ResponseEntity<>(HttpStatus.OK));
} else {
response.setErrorResult(new ThingsboardException("Edge is not connected", ThingsboardErrorCode.GENERAL));
response.setErrorResult(new ThingsboardException(fromEdgeSyncResponse.getError(), ThingsboardErrorCode.GENERAL));
}
}

46
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -243,8 +243,8 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
}
@ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
notes = "Returns a set of unique time-series key names for the selected entity. " +
@ApiOperation(value = "Get time series keys (getTimeseriesKeys)",
notes = "Returns a set of unique time series key names for the selected entity. " +
"\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET)
@ -256,10 +256,10 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
}
@ApiOperation(value = "Get latest time-series value (getLatestTimeseries)",
notes = "Returns all time-series that belong to specified entity. Use optional 'keys' parameter to return specific time-series." +
@ApiOperation(value = "Get latest time series value (getLatestTimeseries)",
notes = "Returns all time series that belong to specified entity. Use optional 'keys' parameter to return specific time series." +
" The result is a JSON object. The format of the values depends on the 'useStrictDataTypes' parameter." +
" By default, all time-series values are converted to strings: \n\n"
" By default, all time series values are converted to strings: \n\n"
+ MARKDOWN_CODE_BLOCK_START
+ LATEST_TS_NON_STRICT_DATA_EXAMPLE
+ MARKDOWN_CODE_BLOCK_END
@ -282,8 +282,8 @@ public class TelemetryController extends BaseController {
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes));
}
@ApiOperation(value = "Get time-series data (getTimeseries)",
notes = "Returns a range of time-series values for specified entity. " +
@ApiOperation(value = "Get time series data (getTimeseries)",
notes = "Returns a range of time series values for specified entity. " +
"Returns not aggregated data by default. " +
"Use aggregation function ('agg') and aggregation interval ('interval') to enable aggregation of the results on the database / server side. " +
"The aggregation is generally more efficient then fetching all records. \n\n"
@ -308,7 +308,7 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "interval", defaultValue = "0") Long interval,
@Parameter(description = "A string value representing the timezone that will be used to calculate exact timestamps for 'WEEK', 'WEEK_ISO', 'MONTH' and 'QUARTER' interval types.")
@RequestParam(name = "timeZone", required = false) String timeZone,
@Parameter(description = "An integer value that represents a max number of timeseries data points to fetch." +
@Parameter(description = "An integer value that represents a max number of time series data points to fetch." +
" This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", schema = @Schema(defaultValue = "100"))
@RequestParam(name = "limit", defaultValue = "100") Integer limit,
@Parameter(description = "A string value representing the aggregation function. " +
@ -406,8 +406,8 @@ public class TelemetryController extends BaseController {
}
@ApiOperation(value = "Save or update time-series data (saveEntityTelemetry)",
notes = "Creates or updates the entity time-series data based on the Entity Id and request payload." +
@ApiOperation(value = "Save or update time series data (saveEntityTelemetry)",
notes = "Creates or updates the entity time series data based on the Entity Id and request payload." +
SAVE_TIMESERIES_REQUEST_PAYLOAD +
"\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. "
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ -429,8 +429,8 @@ public class TelemetryController extends BaseController {
return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
}
@ApiOperation(value = "Save or update time-series data with TTL (saveEntityTelemetryWithTTL)",
notes = "Creates or updates the entity time-series data based on the Entity Id and request payload." +
@ApiOperation(value = "Save or update time series data with TTL (saveEntityTelemetryWithTTL)",
notes = "Creates or updates the entity time series data based on the Entity Id and request payload." +
SAVE_TIMESERIES_REQUEST_PAYLOAD +
"\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. "
+ "\n\nThe ttl parameter takes affect only in case of Cassandra DB."
@ -454,21 +454,21 @@ public class TelemetryController extends BaseController {
return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
}
@ApiOperation(value = "Delete entity time-series data (deleteEntityTimeseries)",
notes = "Delete time-series for selected entity based on entity id, entity type and keys." +
" Use 'deleteAllDataForKeys' to delete all time-series data." +
@ApiOperation(value = "Delete entity time series data (deleteEntityTimeseries)",
notes = "Delete time series for selected entity based on entity id, entity type and keys." +
" Use 'deleteAllDataForKeys' to delete all time series data." +
" Use 'startTs' and 'endTs' to specify time-range instead. " +
" Use 'deleteLatest' to delete latest value (stored in separate table for performance) if the value's timestamp matches the time-range. " +
" Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." +
" The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " +
" The replacement value will be fetched from the 'time series' table, and its timestamp will be the most recent one before the defined time-range. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Timeseries for the selected keys in the request was removed. " +
"Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED'."),
@ApiResponse(responseCode = "200", description = "Time series for the selected keys in the request was removed. " +
"Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED'."),
@ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys list is empty or start and end timestamp values is empty when deleteAllDataForKeys is set to false."),
@ApiResponse(responseCode = "401", description = "User is not authorized to delete entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant."),
@ApiResponse(responseCode = "401", description = "User is not authorized to delete entity time series for selected entity. Most likely, User belongs to different Customer or Tenant."),
@ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."),
"Platform creates an audit log event about entity time series removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE)
@ -649,12 +649,12 @@ public class TelemetryController extends BaseController {
try {
telemetryJson = JsonParser.parseString(requestBody);
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("Unable to parse time series payload: Invalid JSON body!", HttpStatus.BAD_REQUEST);
}
try {
telemetryRequest = JsonConverter.convertToTelemetry(telemetryJson, System.currentTimeMillis());
} catch (Exception e) {
return getImmediateDeferredResult("Unable to parse timeseries payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("Unable to parse time series payload. Invalid JSON body: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
List<TsKvEntry> entries = new ArrayList<>();
for (Map.Entry<Long, List<KvEntry>> entry : telemetryRequest.entrySet()) {
@ -663,7 +663,7 @@ public class TelemetryController extends BaseController {
}
}
if (entries.isEmpty()) {
return getImmediateDeferredResult("No timeseries data found in request body!", HttpStatus.BAD_REQUEST);
return getImmediateDeferredResult("No time series data found in request body!", HttpStatus.BAD_REQUEST);
}
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_TELEMETRY, entityIdSrc, (result, tenantId, entityId) -> {

1
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java

@ -92,6 +92,7 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS);
errorCodeToStatusMap.put(ThingsboardErrorCode.TOO_MANY_UPDATES, HttpStatus.TOO_MANY_REQUESTS);
errorCodeToStatusMap.put(ThingsboardErrorCode.SUBSCRIPTION_VIOLATION, HttpStatus.FORBIDDEN);
errorCodeToStatusMap.put(ThingsboardErrorCode.VERSION_CONFLICT, HttpStatus.CONFLICT);
}
private static ThingsboardErrorCode statusToErrorCode(HttpStatus status) {

4
application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java

@ -182,7 +182,8 @@ public class EdgeEventSourcingListener {
return false;
}
if (oldEntity != null) {
User oldUser = (User) oldEntity;
user = JacksonUtil.clone(user);
User oldUser = JacksonUtil.clone((User) oldEntity);
cleanUpUserAdditionalInfo(oldUser);
cleanUpUserAdditionalInfo(user);
return !user.equals(oldUser);
@ -226,6 +227,7 @@ public class EdgeEventSourcingListener {
user.setAdditionalInfo(additionalInfo);
}
}
user.setVersion(null);
}
private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) {

29
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -287,22 +287,31 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
private void startSyncProcess(TenantId tenantId, EdgeId edgeId, UUID requestId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null) {
boolean success = false;
if (session.isConnected()) {
session.startSyncProcess(true);
success = true;
if (!session.isSyncCompleted()) {
clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, false, "Sync process is active at the moment"));
} else {
boolean success = false;
if (session.isConnected()) {
session.startSyncProcess(true);
success = true;
}
clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, success, ""));
}
clusterService.pushEdgeSyncResponseToCore(new FromEdgeSyncResponse(requestId, tenantId, edgeId, success));
}
}
@Override
public void processSyncRequest(ToEdgeSyncRequest request, Consumer<FromEdgeSyncResponse> responseConsumer) {
log.trace("[{}][{}] Processing sync edge request [{}]", request.getTenantId(), request.getId(), request.getEdgeId());
UUID requestId = request.getId();
localSyncEdgeRequests.put(requestId, responseConsumer);
clusterService.pushEdgeSyncRequestToCore(request);
scheduleSyncRequestTimeout(request, requestId);
EdgeGrpcSession session = sessions.get(request.getEdgeId());
if (session != null && !session.isSyncCompleted()) {
responseConsumer.accept(new FromEdgeSyncResponse(requestId, request.getTenantId(), request.getEdgeId(), false, "Sync process is active at the moment"));
} else {
log.trace("[{}][{}] Processing sync edge request [{}]", request.getTenantId(), request.getId(), request.getEdgeId());
localSyncEdgeRequests.put(requestId, responseConsumer);
clusterService.pushEdgeSyncRequestToCore(request);
scheduleSyncRequestTimeout(request, requestId);
}
}
private void scheduleSyncRequestTimeout(ToEdgeSyncRequest request, UUID requestId) {
@ -312,7 +321,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
Consumer<FromEdgeSyncResponse> consumer = localSyncEdgeRequests.remove(requestId);
if (consumer != null) {
log.trace("[{}] timeout for processing sync edge request.", requestId);
consumer.accept(new FromEdgeSyncResponse(requestId, request.getTenantId(), request.getEdgeId(), false));
consumer.accept(new FromEdgeSyncResponse(requestId, request.getTenantId(), request.getEdgeId(), false, "Edge is not connected"));
}
}, 20, TimeUnit.SECONDS);
}

17
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -247,6 +247,7 @@ public final class EdgeGrpcSession implements Closeable {
}
}, ctx.getGrpcCallbackExecutorService());
} else {
log.info("[{}][{}] sync process completed", this.tenantId, edge.getId());
DownlinkMsg syncCompleteDownlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.setSyncCompletedMsg(SyncCompletedMsg.newBuilder().build())
@ -325,7 +326,11 @@ public final class EdgeGrpcSession implements Closeable {
}
private void sendDownlinkMsg(ResponseMsg downlinkMsg) {
log.trace("[{}][{}] Sending downlink msg [{}]", this.tenantId, this.sessionId, downlinkMsg);
if (downlinkMsg.getDownlinkMsg().getWidgetTypeUpdateMsgCount() > 0) {
log.trace("[{}][{}] Sending downlink widgetTypeUpdateMsg, downlinkMsgId = {}", this.tenantId, this.sessionId, downlinkMsg.getDownlinkMsg().getDownlinkMsgId());
} else {
log.trace("[{}][{}] Sending downlink msg [{}]", this.tenantId, this.sessionId, downlinkMsg);
}
if (isConnected()) {
downlinkMsgLock.lock();
try {
@ -337,7 +342,7 @@ public final class EdgeGrpcSession implements Closeable {
} finally {
downlinkMsgLock.unlock();
}
log.trace("[{}][{}] Response msg successfully sent [{}]", this.tenantId, this.sessionId, downlinkMsg);
log.trace("[{}][{}] Response msg successfully sent. downlinkMsgId = {}", this.tenantId, this.sessionId, downlinkMsg.getDownlinkMsg().getDownlinkMsgId());
}
}
@ -551,9 +556,13 @@ public final class EdgeGrpcSession implements Closeable {
DownlinkMsg downlinkMsg = null;
try {
switch (edgeEvent.getAction()) {
case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE, CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, CREDENTIALS_REQUEST, RPC_CALL, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> {
case UPDATED, ADDED, DELETED, ASSIGNED_TO_EDGE, UNASSIGNED_FROM_EDGE, ALARM_ACK, ALARM_CLEAR, ALARM_DELETE, CREDENTIALS_UPDATED, RELATION_ADD_OR_UPDATE, RELATION_DELETED, RPC_CALL, ASSIGNED_TO_CUSTOMER, UNASSIGNED_FROM_CUSTOMER, ADDED_COMMENT, UPDATED_COMMENT, DELETED_COMMENT -> {
downlinkMsg = convertEntityEventToDownlink(edgeEvent);
log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg);
if (downlinkMsg != null && downlinkMsg.getWidgetTypeUpdateMsgCount() > 0) {
log.trace("[{}][{}] widgetTypeUpdateMsg message processed, downlinkMsgId = {}", this.tenantId, this.sessionId, downlinkMsg.getDownlinkMsgId());
} else {
log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg);
}
}
case ATTRIBUTES_UPDATED, POST_ATTRIBUTES, ATTRIBUTES_DELETED, TIMESERIES_UPDATED ->
downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edge, edgeEvent);

19
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.edge.rpc;
import lombok.Getter;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EntityId;
@ -53,6 +54,7 @@ public class EdgeSyncCursor {
private final List<EdgeEventFetcher> fetchers = new LinkedList<>();
@Getter
private int currentIdx = 0;
public EdgeSyncCursor(EdgeContextComponent ctx, Edge edge, boolean fullSync) {
@ -62,12 +64,12 @@ public class EdgeSyncCursor {
fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService()));
fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService()));
fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService()));
Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId());
fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId()));
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
fetchers.add(new CustomerEdgeEventFetcher(edge.getCustomerId()));
fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
}
}
Customer publicCustomer = ctx.getCustomerService().findOrCreatePublicCustomer(edge.getTenantId());
fetchers.add(new CustomerEdgeEventFetcher(publicCustomer.getId()));
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
fetchers.add(new CustomerEdgeEventFetcher(edge.getCustomerId()));
fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
}
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
fetchers.add(new DefaultProfilesEdgeEventFetcher(ctx.getDeviceProfileService(), ctx.getAssetProfileService()));
@ -102,9 +104,4 @@ public class EdgeSyncCursor {
currentIdx++;
return edgeEventFetcher;
}
public int getCurrentIdx() {
return currentIdx;
}
}

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java

@ -373,7 +373,7 @@ public abstract class BaseEdgeProcessor {
private boolean doSaveIfEdgeIsOffline(EdgeEventType type,
EdgeEventActionType action) {
return switch (action) {
case TIMESERIES_UPDATED, ALARM_ACK, ALARM_CLEAR, ALARM_ASSIGNED, ALARM_UNASSIGNED, CREDENTIALS_REQUEST, ADDED_COMMENT, UPDATED_COMMENT ->
case TIMESERIES_UPDATED, ALARM_ACK, ALARM_CLEAR, ALARM_ASSIGNED, ALARM_UNASSIGNED, ADDED_COMMENT, UPDATED_COMMENT ->
true;
default -> switch (type) {
case ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_CHAIN_METADATA, USER, CUSTOMER, TENANT, TENANT_PROFILE, WIDGETS_BUNDLE, WIDGET_TYPE,

24
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java

@ -43,7 +43,6 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
@ -70,7 +69,7 @@ public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
saveOrUpdateDevice(tenantId, deviceId, deviceUpdateMsg, edge);
return saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
return Futures.immediateFuture(null);
case ENTITY_DELETED_RPC_MESSAGE:
Device deviceToDelete = deviceService.findDeviceById(tenantId, deviceId);
if (deviceToDelete != null) {
@ -232,6 +231,12 @@ public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements
DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceUpdateMsg(deviceUpdateMsg);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(edgeEvent.getTenantId(), deviceId);
if (deviceCredentials != null) {
DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = ((DeviceMsgConstructor)
deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructDeviceCredentialsUpdatedMsg(deviceCredentials);
builder.addDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg).build();
}
if (UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(edgeEvent.getTenantId(), device.getDeviceProfileId());
deviceProfile = checkIfDeviceProfileDefaultFieldsAssignedToEdge(edgeEvent.getTenantId(), edgeId, deviceProfile, edgeVersion);
@ -269,22 +274,7 @@ public abstract class DeviceEdgeProcessor extends BaseDeviceProcessor implements
deviceMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion))
.constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody()))
.build();
case CREDENTIALS_REQUEST:
return convertCredentialsRequestEventToDownlink(edgeEvent);
}
return downlinkMsg;
}
private DownlinkMsg convertCredentialsRequestEventToDownlink(EdgeEvent edgeEvent) {
DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder()
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.build();
DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addDeviceCredentialsRequestMsg(deviceCredentialsRequestMsg);
return builder.build();
}
}

15
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/rule/RuleChainEdgeProcessor.java

@ -61,10 +61,19 @@ public class RuleChainEdgeProcessor extends BaseEdgeProcessor {
RuleChainUpdateMsg ruleChainUpdateMsg = ((RuleChainMsgConstructor)
ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion))
.constructRuleChainUpdatedMsg(msgType, ruleChain, isRoot);
downlinkMsg = DownlinkMsg.newBuilder()
DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addRuleChainUpdateMsg(ruleChainUpdateMsg)
.build();
.addRuleChainUpdateMsg(ruleChainUpdateMsg);
RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId);
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = ((RuleChainMsgConstructor)
ruleChainMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion))
.constructRuleChainMetadataUpdatedMsg(edgeEvent.getTenantId(), msgType, ruleChainMetaData, edgeVersion);
if (ruleChainMetadataUpdateMsg != null) {
builder.addRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg);
}
downlinkMsg = builder.build();
}
}
case DELETED, UNASSIGNED_FROM_EDGE -> downlinkMsg = DownlinkMsg.newBuilder()

12
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java

@ -43,10 +43,16 @@ public class UserEdgeProcessor extends BaseEdgeProcessor {
User user = userService.findUserById(edgeEvent.getTenantId(), userId);
if (user != null) {
UpdateMsgType msgType = getUpdateMsgType(edgeEvent.getAction());
downlinkMsg = DownlinkMsg.newBuilder()
DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addUserUpdateMsg(((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserUpdatedMsg(msgType, user))
.build();
.addUserUpdateMsg(((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserUpdatedMsg(msgType, user));
UserCredentials userCredentialsByUserId = userService.findUserCredentialsByUserId(edgeEvent.getTenantId(), userId);
if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {
UserCredentialsUpdateMsg userCredentialsUpdateMsg =
((UserMsgConstructor) userMsgConstructorFactory.getMsgConstructorByEdgeVersion(edgeVersion)).constructUserCredentialsUpdatedMsg(userCredentialsByUserId);
builder.addUserCredentialsUpdateMsg(userCredentialsUpdateMsg);
}
downlinkMsg = builder.build();
}
}
case DELETED -> downlinkMsg = DownlinkMsg.newBuilder()

17
application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -82,8 +82,6 @@ import java.util.UUID;
@Slf4j
public class DefaultEdgeRequestsService implements EdgeRequestsService {
private static final int DEFAULT_PAGE_SIZE = 1000;
@Autowired
private EdgeEventService edgeEventService;
@ -142,8 +140,10 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
private ListenableFuture<Void> processEntityAttributesAndAddToEdgeQueue(TenantId tenantId, EntityId entityId, Edge edge,
EdgeEventType entityType, String scope, List<AttributeKvEntry> ssAttributes,
AttributesRequestMsg attributesRequestMsg) {
Map<String, Object> entityData = null;
ObjectNode attributes = null;
ListenableFuture<Void> future;
try {
ListenableFuture<Void> future;
if (ssAttributes == null || ssAttributes.isEmpty()) {
log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
edge.getName(),
@ -151,8 +151,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
entityId.getId());
future = Futures.immediateFuture(null);
} else {
Map<String, Object> entityData = new HashMap<>();
ObjectNode attributes = JacksonUtil.newObjectNode();
entityData = new HashMap<>();
attributes = JacksonUtil.newObjectNode();
for (AttributeKvEntry attr : ssAttributes) {
if (DefaultDeviceStateService.PERSISTENT_ATTRIBUTES.contains(attr.getKey())
&& !DefaultDeviceStateService.INACTIVITY_TIMEOUT.equals(attr.getKey())) {
@ -170,7 +170,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
attributes.put(attr.getKey(), attr.getValueAsString());
}
}
if (attributes.size() > 0) {
if (!attributes.isEmpty()) {
entityData.put("kv", attributes);
entityData.put("scope", scope);
JsonNode body = JacksonUtil.valueToTree(entityData);
@ -182,12 +182,13 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
}
return Futures.transformAsync(future, v -> processLatestTimeseriesAndAddToEdgeQueue(tenantId, entityId, edge, entityType), dbCallbackExecutorService);
} catch (Exception e) {
String errMsg = String.format("[%s][%s] Failed to save attribute updates to the edge [%s]", tenantId, edge.getId(), attributesRequestMsg);
String errMsg = String.format("[%s][%s] Failed to save attribute updates to the edge [%s], scope = %s, entityData = %s, attributes = %s",
tenantId, edge.getId(), attributesRequestMsg, scope, entityData, attributes);
log.error(errMsg, e);
return Futures.immediateFailedFuture(new RuntimeException(errMsg, e));
}
}
private ListenableFuture<Void> processLatestTimeseriesAndAddToEdgeQueue(TenantId tenantId, EntityId entityId, Edge edge,
EdgeEventType entityType) {
ListenableFuture<List<TsKvEntry>> getAllLatestFuture = timeseriesService.findAllLatest(tenantId, entityId);

2
application/src/main/java/org/thingsboard/server/service/entitiy/device/DefaultTbDeviceService.java

@ -183,7 +183,7 @@ public class DefaultTbDeviceService extends AbstractTbEntityService implements T
try {
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials));
logEntityActionService.logEntityAction(tenantId, deviceId, device, device.getCustomerId(),
actionType, user, deviceCredentials);
actionType, user, result);
return result;
} catch (Exception e) {
logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DEVICE),

2
application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java

@ -147,7 +147,7 @@ public class ImagesUpdater {
try {
entity = dao.findById(TenantId.SYS_TENANT_ID, id.getId());
} catch (Exception e) {
log.error("Failed to update {} images: error fetching {} by id [{}]: {}", type, type, id.getId(), StringUtils.abbreviate(e.toString(), 1000));
log.error("Failed to update {} images: error fetching entity by id [{}]: {}", type, id.getId(), StringUtils.abbreviate(e.toString(), 1000));
continue;
}
try {

4
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.entity.EntityService;
@ -355,6 +356,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
private void handleWsCmdRuntimeException(String sessionId, RuntimeException e, EntityDataCmd cmd) {
log.debug("[{}] Failed to process ws cmd: {}", sessionId, cmd, e);
if (e instanceof TbRateLimitsException) {
return;
}
wsService.close(sessionId, CloseStatus.SERVICE_RESTARTED);
}

36
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java

@ -18,12 +18,15 @@ package org.thingsboard.server.service.subscription;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.DeduplicationUtil;
import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.cache.limits.RateLimitService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
@ -36,10 +39,12 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.limit.LimitedApi;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -48,6 +53,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.ws.WebSocketService;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate;
import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate;
@ -88,13 +94,20 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
private final TbClusterService clusterService;
private final SubscriptionManagerService subscriptionManagerService;
private final WebSocketService webSocketService;
private final RateLimitService rateLimitService;
private ExecutorService tsCallBackExecutor;
private ScheduledExecutorService staleSessionCleanupExecutor;
@Value("${server.ws.rate_limits.subscriptions_per_tenant:2000:60}")
private String subscriptionsPerTenantRateLimit;
@Value("${server.ws.rate_limits.subscriptions_per_user:500:60}")
private String subscriptionsPerUserRateLimit;
public DefaultTbLocalSubscriptionService(AttributesService attrService, TimeseriesService tsService, TbServiceInfoProvider serviceInfoProvider,
PartitionService partitionService, TbClusterService clusterService,
@Lazy SubscriptionManagerService subscriptionManagerService, @Lazy WebSocketService webSocketService) {
@Lazy SubscriptionManagerService subscriptionManagerService, @Lazy WebSocketService webSocketService,
RateLimitService rateLimitService) {
this.attrService = attrService;
this.tsService = tsService;
this.serviceInfoProvider = serviceInfoProvider;
@ -102,6 +115,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
this.clusterService = clusterService;
this.subscriptionManagerService = subscriptionManagerService;
this.webSocketService = webSocketService;
this.rateLimitService = rateLimitService;
}
private String serviceId;
@ -185,9 +199,18 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
}
@Override
public void addSubscription(TbSubscription<?> subscription) {
public void addSubscription(TbSubscription<?> subscription, WebSocketSessionRef sessionRef) {
TenantId tenantId = subscription.getTenantId();
EntityId entityId = subscription.getEntityId();
if (!rateLimitService.checkRateLimit(LimitedApi.WS_SUBSCRIPTIONS, (Object) tenantId, subscriptionsPerTenantRateLimit)) {
handleRateLimitError(subscription, sessionRef, "Exceeded rate limit for WS subscriptions per tenant");
return;
}
if (sessionRef.getSecurityCtx() != null && !rateLimitService.checkRateLimit(LimitedApi.WS_SUBSCRIPTIONS, sessionRef.getSecurityCtx().getId(), subscriptionsPerUserRateLimit)) {
handleRateLimitError(subscription, sessionRef, "Exceeded rate limit for WS subscriptions per user");
return;
}
log.debug("[{}][{}] Register subscription: {}", tenantId, entityId, subscription);
SubscriptionModificationResult result;
subsLock.lock();
@ -584,4 +607,13 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer
subscriptionsBySessionId.keySet().forEach(webSocketService::cleanupIfStale);
}
private void handleRateLimitError(TbSubscription<?> subscription, WebSocketSessionRef sessionRef, String message) {
String deduplicationKey = sessionRef.getSessionId() + message;
if (!DeduplicationUtil.alreadyProcessed(deduplicationKey, TimeUnit.SECONDS.toMillis(15))) {
log.info("{} {}", sessionRef, message);
webSocketService.sendError(sessionRef, subscription.getSubscriptionId(), SubscriptionErrorCode.BAD_REQUEST, message);
}
throw new TbRateLimitsException(message);
}
}

5
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractDataSubCtx.java

@ -132,7 +132,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
int subIdx = sessionRef.getSessionSubIdSeq().incrementAndGet();
subToEntityIdMap.put(subIdx, entityData.getEntityId());
localSubscriptionService.addSubscription(
createTsSub(entityData, subIdx, false, startTs, endTs, keyStates, resultToLatestValues));
createTsSub(entityData, subIdx, false, startTs, endTs, keyStates, resultToLatestValues), sessionRef);
});
}
@ -140,7 +140,7 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
Map<EntityKeyType, List<EntityKey>> keysByType = getEntityKeyByTypeMap(keys);
for (EntityData entityData : data.getData()) {
List<TbSubscription> entitySubscriptions = addSubscriptions(entityData, keysByType, latestValues, startTs, endTs);
entitySubscriptions.forEach(localSubscriptionService::addSubscription);
entitySubscriptions.forEach(subscription -> localSubscriptionService.addSubscription(subscription, sessionRef));
}
}
@ -254,4 +254,5 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends
abstract void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, EntityKeyType keyType, boolean resultToLatestValues);
protected abstract Aggregation getCurrentAggregation();
}

2
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java

@ -152,7 +152,7 @@ public abstract class TbAbstractSubCtx<T extends EntityCountQuery> {
.scope(TbAttributeSubscriptionScope.SERVER_SCOPE)
.build();
subToDynamicValueKeySet.add(subIdx);
localSubscriptionService.addSubscription(sub);
localSubscriptionService.addSubscription(sub, sessionRef);
}
} catch (InterruptedException | ExecutionException e) {
log.info("[{}][{}][{}] Failed to resolve dynamic values: {}", tenantId, customerId, userId, dynamicValues.keySet());

5
application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java

@ -177,7 +177,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
.updateProcessor((sub, update) -> sendWsMsg(sub.getSessionId(), update))
.ts(startTs)
.build();
localSubscriptionService.addSubscription(subscription);
localSubscriptionService.addSubscription(subscription, sessionRef);
}
@Override
@ -342,7 +342,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
newSubsList.forEach(entity -> createAlarmSubscriptionForEntity(query.getPageLink(), startTs, entity));
}
subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getSessionId(), subId));
subsToAdd.forEach(localSubscriptionService::addSubscription);
subsToAdd.forEach(subscription -> localSubscriptionService.addSubscription(subscription, sessionRef));
}
private void resetInvocationCounter() {
@ -361,4 +361,5 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
EntityDataPageLink edpl = new EntityDataPageLink(maxEntitiesPerAlarmSubscription, 0, null, entitiesSortOrder);
return new EntityDataQuery(query.getEntityFilter(), edpl, query.getEntityFields(), query.getLatestValues(), query.getKeyFilters());
}
}

3
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityDataSubCtx.java

@ -226,7 +226,7 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
}
}
subIdsToCancel.forEach(subId -> localSubscriptionService.cancelSubscription(getSessionId(), subId));
subsToAdd.forEach(localSubscriptionService::addSubscription);
subsToAdd.forEach(subscription -> localSubscriptionService.addSubscription(subscription, sessionRef));
sendWsMsg(new EntityDataUpdate(cmdId, data, null, maxEntitiesPerDataSubscription));
}
@ -239,4 +239,5 @@ public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
protected EntityDataQuery buildEntityDataQuery() {
return query;
}
}

3
application/src/main/java/org/thingsboard/server/service/subscription/TbLocalSubscriptionService.java

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent;
import org.thingsboard.server.service.ws.WebSocketSessionRef;
import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate;
import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate;
@ -29,7 +30,7 @@ import java.util.List;
public interface TbLocalSubscriptionService {
void addSubscription(TbSubscription<?> subscription);
void addSubscription(TbSubscription<?> subscription, WebSocketSessionRef sessionRef);
void onSubEventCallback(TransportProtos.TbEntitySubEventCallbackProto subEventCallback, TbCallback callback);

4
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java

@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -74,6 +75,9 @@ public class DefaultEntityExportService<I extends EntityId, E extends Exportable
exportData.setEntity(entity);
exportData.setEntityType(entityId.getEntityType());
setAdditionalExportData(ctx, entity, exportData);
if (entity instanceof HasVersion hasVersion) {
hasVersion.setVersion(null);
}
var externalId = entity.getExternalId() != null ? entity.getExternalId() : entity.getId();
ctx.putExternalId(entityId, externalId);

4
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasAdditionalInfo;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.audit.ActionType;
@ -147,6 +148,9 @@ public abstract class AbstractBulkImportService<E extends HasId<? extends Entity
if (entity.getId() != null) {
importedEntityInfo.setOldEntity((E) entity.getClass().getConstructor(entity.getClass()).newInstance(entity));
importedEntityInfo.setUpdated(true);
if (entity instanceof HasVersion versionedEntity) {
versionedEntity.setVersion(null); // to overwrite the entity regardless of concurrent changes
}
} else {
setOwners(entity, user);
}

4
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasDefaultOption;
import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -161,6 +162,9 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo
protected void cleanupForComparison(E e) {
e.setTenantId(null);
e.setCreatedTime(0);
if (e instanceof HasVersion hasVersion) {
hasVersion.setVersion(null);
}
}
protected abstract E saveOrUpdate(EntitiesImportCtx ctx, E entity, D exportData, IdProvider idProvider);

14
application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java

@ -49,6 +49,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
@ -223,9 +224,10 @@ public class DefaultWebSocketService implements WebSocketService {
try {
Optional.ofNullable(cmdsHandlers.get(cmd.getType()))
.ifPresent(cmdHandler -> cmdHandler.handle(sessionRef, cmd));
} catch (TbRateLimitsException e) {
log.debug("{} Failed to handle WS cmd: {}", sessionRef, cmd, e);
} catch (Exception e) {
log.error("[sessionId: {}, tenantId: {}, userId: {}] Failed to handle WS cmd: {}", sessionId,
sessionRef.getSecurityCtx().getTenantId(), sessionRef.getSecurityCtx().getId(), cmd, e);
log.error("{} Failed to handle WS cmd: {}", sessionRef, cmd, e);
}
}
}
@ -467,7 +469,7 @@ public class DefaultWebSocketService implements WebSocketService {
subLock.lock();
try {
oldSubService.addSubscription(sub);
oldSubService.addSubscription(sub, sessionRef);
sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), attributesData));
} finally {
subLock.unlock();
@ -580,7 +582,7 @@ public class DefaultWebSocketService implements WebSocketService {
subLock.lock();
try {
oldSubService.addSubscription(sub);
oldSubService.addSubscription(sub, sessionRef);
sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), attributesData));
} finally {
subLock.unlock();
@ -677,7 +679,7 @@ public class DefaultWebSocketService implements WebSocketService {
subLock.lock();
try {
oldSubService.addSubscription(sub);
oldSubService.addSubscription(sub, sessionRef);
sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data));
} finally {
subLock.unlock();
@ -732,7 +734,7 @@ public class DefaultWebSocketService implements WebSocketService {
subLock.lock();
try {
oldSubService.addSubscription(sub);
oldSubService.addSubscription(sub, sessionRef);
sendUpdate(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data));
} finally {
subLock.unlock();

4
application/src/main/java/org/thingsboard/server/service/ws/notification/DefaultNotificationCommandsHandler.java

@ -81,7 +81,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH
.limit(cmd.getLimit())
.notificationTypes(cmd.getTypes())
.build();
localSubscriptionService.addSubscription(subscription);
localSubscriptionService.addSubscription(subscription, sessionRef);
fetchUnreadNotifications(subscription);
sendUpdate(sessionRef.getSessionId(), subscription.createFullUpdate());
@ -99,7 +99,7 @@ public class DefaultNotificationCommandsHandler implements NotificationCommandsH
.entityId(securityCtx.getId())
.updateProcessor(this::handleNotificationsCountSubscriptionUpdate)
.build();
localSubscriptionService.addSubscription(subscription);
localSubscriptionService.addSubscription(subscription, sessionRef);
fetchUnreadNotificationsCount(subscription);
sendUpdate(sessionRef.getSessionId(), subscription.createUpdate());

30
application/src/main/resources/thingsboard.yml

@ -78,6 +78,11 @@ server:
max_queue_messages_per_session: "${TB_SERVER_WS_DEFAULT_QUEUE_MESSAGES_PER_SESSION:1000}"
# Maximum time between WS session opening and sending auth command
auth_timeout_ms: "${TB_SERVER_WS_AUTH_TIMEOUT_MS:10000}"
rate_limits:
# Per-tenant rate limit for WS subscriptions
subscriptions_per_tenant: "${TB_SERVER_WS_SUBSCRIPTIONS_PER_TENANT_RATE_LIMIT:2000:60}"
# Per-user rate limit for WS subscriptions
subscriptions_per_user: "${TB_SERVER_WS_SUBSCRIPTIONS_PER_USER_RATE_LIMIT:500:60}"
rest:
server_side_rpc:
# Minimum value of the server-side RPC timeout. May override value provided in the REST API call.
@ -485,7 +490,7 @@ actors:
# Cache settings parameters
cache:
# caffeine or redis
# caffeine or redis(7.2 - latest compatible version)
type: "${CACHE_TYPE:caffeine}"
maximumPoolSize: "${CACHE_MAXIMUM_POOL_SIZE:16}" # max pool size to process futures that call the external cache
attributes:
@ -768,7 +773,28 @@ spring:
leakDetectionThreshold: "${SPRING_DATASOURCE_HIKARI_LEAK_DETECTION_THRESHOLD:0}"
# This property increases the number of connections in the pool as demand increases. At the same time, the property ensures that the pool doesn't grow to the point of exhausting a system's resources, which ultimately affects an application's performance and availability
maximumPoolSize: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:16}"
registerMbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}" # true - enable MBean to diagnose pools state via JMX
# Enable MBean to diagnose pools state via JMX
registerMbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}"
events:
# Enable dedicated datasource (a separate database) for events and audit logs.
# Before enabling this, make sure you have set up the following tables in the new DB:
# error_event, lc_event, rule_chain_debug_event, rule_node_debug_event, stats_event, audit_log
enabled: "${SPRING_DEDICATED_EVENTS_DATASOURCE_ENABLED:false}"
# Database driver for Spring JPA for events datasource
driverClassName: "${SPRING_EVENTS_DATASOURCE_DRIVER_CLASS_NAME:org.postgresql.Driver}"
# Database connection URL for events datasource
url: "${SPRING_EVENTS_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard_events}"
# Database username for events datasource
username: "${SPRING_EVENTS_DATASOURCE_USERNAME:postgres}"
# Database user password for events datasource
password: "${SPRING_EVENTS_DATASOURCE_PASSWORD:postgres}"
hikari:
# This property controls the amount of time that a connection can be out of the pool before a message is logged indicating a possible connection leak for events datasource. A value of 0 means leak detection is disabled
leakDetectionThreshold: "${SPRING_EVENTS_DATASOURCE_HIKARI_LEAK_DETECTION_THRESHOLD:0}"
# This property increases the number of connections in the pool as demand increases for events datasource. At the same time, the property ensures that the pool doesn't grow to the point of exhausting a system's resources, which ultimately affects an application's performance and availability
maximumPoolSize: "${SPRING_EVENTS_DATASOURCE_MAXIMUM_POOL_SIZE:16}"
# Enable MBean to diagnose pools state via JMX for events datasource
registerMbeans: "${SPRING_EVENTS_DATASOURCE_HIKARI_REGISTER_MBEANS:false}"
# Audit log parameters
audit-log:

2
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -526,7 +526,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
JsonNode activateRequest = getActivateRequest(password);
ResultActions resultActions = doPost("/api/noauth/activate", activateRequest);
resultActions.andExpect(status().isOk());
return savedUser;
return doGet("/api/user/" + savedUser.getId(), User.class);
}
private JsonNode getActivateRequest(String password) throws Exception {

30
application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest.java

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
@ -64,6 +65,7 @@ public class AuditLogControllerTest extends AbstractControllerTest {
@Autowired
private AuditLogDao auditLogDao;
@Getter
@SpyBean
private SqlPartitioningRepository partitioningRepository;
@SpyBean
@ -161,7 +163,7 @@ public class AuditLogControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class);
for (int i = 0; i < 11; i++) {
savedDevice.setName("Device name" + i);
doPost("/api/device", savedDevice, Device.class);
savedDevice = doPost("/api/device", savedDevice, Device.class);
}
List<AuditLog> loadedAuditLogs = new ArrayList<>();
@ -183,12 +185,12 @@ public class AuditLogControllerTest extends AbstractControllerTest {
@Test
public void whenSavingNewAuditLog_thenCheckAndCreatePartitionIfNotExists() throws ParseException {
long entityTs = ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2024-01-01T01:43:11Z").getTime();
reset(partitioningRepository);
reset(getPartitioningRepository());
AuditLog auditLog = createAuditLog(ActionType.LOGIN, tenantAdminUserId, entityTs);
verify(partitioningRepository).createPartitionIfNotExists(eq("audit_log"), eq(auditLog.getCreatedTime()), eq(partitionDurationInMs));
verify(getPartitioningRepository()).createPartitionIfNotExists(eq("audit_log"), eq(auditLog.getCreatedTime()), eq(partitionDurationInMs));
List<Long> partitions = partitioningRepository.fetchPartitions("audit_log");
assertThat(partitions).contains(partitioningRepository.calculatePartitionStartTime(auditLog.getCreatedTime(), partitionDurationInMs));
List<Long> partitions = getPartitioningRepository().fetchPartitions("audit_log");
assertThat(partitions).contains(getPartitioningRepository().calculatePartitionStartTime(auditLog.getCreatedTime(), partitionDurationInMs));
}
@Test
@ -197,15 +199,15 @@ public class AuditLogControllerTest extends AbstractControllerTest {
final long oldAuditLogTs = ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2020-10-01T00:00:00Z").getTime();
final long currentTimeMillis = oldAuditLogTs + TimeUnit.SECONDS.toMillis(auditLogsTtlInSec) * 2;
final long partitionStartTs = partitioningRepository.calculatePartitionStartTime(oldAuditLogTs, partitionDurationInMs);
partitioningRepository.createPartitionIfNotExists("audit_log", oldAuditLogTs, partitionDurationInMs);
List<Long> partitions = partitioningRepository.fetchPartitions("audit_log");
final long partitionStartTs = getPartitioningRepository().calculatePartitionStartTime(oldAuditLogTs, partitionDurationInMs);
getPartitioningRepository().createPartitionIfNotExists("audit_log", oldAuditLogTs, partitionDurationInMs);
List<Long> partitions = getPartitioningRepository().fetchPartitions("audit_log");
assertThat(partitions).contains(partitionStartTs);
willReturn(currentTimeMillis).given(auditLogsCleanUpService).getCurrentTimeMillis();
auditLogsCleanUpService.cleanUp();
partitions = partitioningRepository.fetchPartitions("audit_log");
partitions = getPartitioningRepository().fetchPartitions("audit_log");
assertThat(partitions).as("partitions cleared").doesNotContain(partitionStartTs);
assertThat(partitions).as("only newer partitions left").allSatisfy(partitionsStart -> {
long partitionEndTs = partitionsStart + partitionDurationInMs;
@ -218,18 +220,18 @@ public class AuditLogControllerTest extends AbstractControllerTest {
// creating partition bigger than sql.audit_logs.partition_size
long entityTs = ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2022-04-29T07:43:11Z").getTime();
//the partition 7 days is overlapping default partition size 1 day, use in the far past to not affect other tests
partitioningRepository.createPartitionIfNotExists("audit_log", entityTs, TimeUnit.DAYS.toMillis(7));
List<Long> partitions = partitioningRepository.fetchPartitions("audit_log");
getPartitioningRepository().createPartitionIfNotExists("audit_log", entityTs, TimeUnit.DAYS.toMillis(7));
List<Long> partitions = getPartitioningRepository().fetchPartitions("audit_log");
log.warn("entityTs [{}], fetched partitions {}", entityTs, partitions);
assertThat(partitions).contains(ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2022-04-28T00:00:00Z").getTime());
partitioningRepository.cleanupPartitionsCache("audit_log", entityTs, 0);
getPartitioningRepository().cleanupPartitionsCache("audit_log", entityTs, 0);
assertDoesNotThrow(() -> {
// expecting partition overlap error on partition save
createAuditLog(ActionType.LOGIN, tenantAdminUserId, entityTs);
});
assertThat(partitioningRepository.fetchPartitions("audit_log"))
.contains(ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2022-04-28T00:00:00Z").getTime());;
assertThat(getPartitioningRepository().fetchPartitions("audit_log"))
.contains(ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse("2022-04-28T00:00:00Z").getTime());
}
private AuditLog createAuditLog(ActionType actionType, EntityId entityId, long entityTs) {

36
application/src/test/java/org/thingsboard/server/controller/AuditLogControllerTest_DedicatedEventsDataSource.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import lombok.Getter;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.dao.sqlts.insert.sql.DedicatedEventsSqlPartitioningRepository;
@DaoSqlTest
@TestPropertySource(properties = {
"spring.datasource.events.enabled=true",
"spring.datasource.events.url=${spring.datasource.url}",
"spring.datasource.events.driverClassName=${spring.datasource.driverClassName}",
})
public class AuditLogControllerTest_DedicatedEventsDataSource extends AuditLogControllerTest {
@Getter
@SpyBean
private DedicatedEventsSqlPartitioningRepository partitioningRepository;
}

30
application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java

@ -192,7 +192,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
savedDevice = doPost("/api/device", savedDevice, Device.class);
testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED);
@ -247,7 +247,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
savedDevice = doPost("/api/device", savedDevice, Device.class);
testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED);
@ -749,8 +749,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isOk());
deviceCredentials = doPost("/api/device/credentials", deviceCredentials, DeviceCredentials.class);
testNotifyEntityMsgToEdgePushMsgToCoreOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(),
tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, deviceCredentials);
@ -1586,10 +1585,33 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(newAttributeValue, actualAttribute.get("value"));
}
@Test
public void testSaveDeviceWithOutdatedVersion() throws Exception {
Device device = createDevice("Device v1.0");
assertThat(device.getVersion()).isOne();
device.setName("Device v2.0");
device = doPost("/api/device", device, Device.class);
assertThat(device.getVersion()).isEqualTo(2);
device.setName("Device v1.1");
device.setVersion(1L);
String response = doPost("/api/device", device).andExpect(status().isConflict())
.andReturn().getResponse().getContentAsString();
assertThat(JacksonUtil.toJsonNode(response).get("message").asText())
.containsIgnoringCase("already changed by someone else");
device.setVersion(null); // overriding entity
device = doPost("/api/device", device, Device.class);
assertThat(device.getName()).isEqualTo("Device v1.1");
assertThat(device.getVersion()).isEqualTo(3);
}
private Device createDevice(String name) {
Device device = new Device();
device.setName(name);
device.setType("default");
return doPost("/api/device", device, Device.class);
}
}

66
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -55,14 +55,18 @@ import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.model.JwtSettings;
import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -73,11 +77,13 @@ import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.gen.edge.v1.QueueUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
@ -93,6 +99,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.containsString;
@ -887,23 +894,24 @@ public class EdgeControllerTest extends AbstractControllerTest {
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.ignoreType(OAuth2UpdateMsg.class);
edgeImitator.expectMessageAmount(24);
edgeImitator.expectMessageAmount(27);
edgeImitator.connect();
waitForMessages(edgeImitator);
verifyFetchersMsgs(edgeImitator);
verifyFetchersMsgs(edgeImitator, savedDevice);
// verify queue msgs
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
edgeImitator.expectMessageAmount(20);
edgeImitator.expectMessageAmount(22);
doPost("/api/edge/sync/" + edge.getId());
waitForMessages(edgeImitator);
verifyFetchersMsgs(edgeImitator);
verifyFetchersMsgs(edgeImitator, savedDevice);
Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
edgeImitator.allowIgnoredTypes();
@ -920,6 +928,23 @@ public class EdgeControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
private RuleChainId getEdgeRootRuleChainId(EdgeImitator edgeImitator) {
try {
EdgeId edgeId = new EdgeId(new UUID(edgeImitator.getConfiguration().getEdgeIdMSB(), edgeImitator.getConfiguration().getEdgeIdLSB()));
List<RuleChain> edgeRuleChains = doGetTypedWithPageLink("/api/edge/" + edgeId.getId() + "/ruleChains?",
new TypeReference<PageData<RuleChain>>() {
}, new PageLink(100)).getData();
for (RuleChain edgeRuleChain : edgeRuleChains) {
if (edgeRuleChain.isRoot()) {
return edgeRuleChain.getId();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
throw new RuntimeException("Root rule chain not found");
}
private void simulateEdgeActivation(Edge edge) throws Exception {
ObjectNode attributes = JacksonUtil.newObjectNode();
attributes.put("active", true);
@ -949,9 +974,10 @@ public class EdgeControllerTest extends AbstractControllerTest {
}
}
private void verifyFetchersMsgs(EdgeImitator edgeImitator) {
private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) {
Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity"));
@ -965,6 +991,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId));
@ -1002,6 +1029,21 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popRuleChainMetadataMsg(List<AbstractMessage> messages, UpdateMsgType msgType, RuleChainId ruleChainId) {
for (AbstractMessage message : messages) {
if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) {
RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true);
Assert.assertNotNull(ruleChainMetaData);
if (msgType.equals(ruleChainMetadataUpdateMsg.getMsgType())
&& ruleChainId.equals(ruleChainMetaData.getRuleChainId())) {
messages.remove(message);
return true;
}
}
}
return false;
}
private boolean popAdminSettingsMsg(List<AbstractMessage> messages, String key) {
for (AbstractMessage message : messages) {
if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) {
@ -1046,6 +1088,20 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popDeviceCredentialsMsg(List<AbstractMessage> messages, DeviceId deviceId) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true);
Assert.assertNotNull(deviceCredentials);
if (deviceId.equals(deviceCredentials.getDeviceId())) {
messages.remove(message);
return true;
}
}
}
return false;
}
private boolean popAssetProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) {
if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) {

2
application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java

@ -176,7 +176,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
savedView.setName("New test entity view");
doPost("/api/entityView", savedView, EntityView.class);
savedView = doPost("/api/entityView", savedView, EntityView.class);
foundEntityView = doGet("/api/entityView/" + savedView.getId().getId().toString(), EntityView.class);
assertEquals(savedView, foundEntityView);

2
application/src/test/java/org/thingsboard/server/controller/RuleChainControllerTest.java

@ -120,7 +120,7 @@ public class RuleChainControllerTest extends AbstractControllerTest {
ActionType.ADDED);
savedRuleChain.setName("New RuleChain");
doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
savedRuleChain = doPost("/api/ruleChain", savedRuleChain, RuleChain.class);
RuleChain foundRuleChain = doGet("/api/ruleChain/" + savedRuleChain.getId().getId().toString(), RuleChain.class);
Assert.assertEquals(savedRuleChain.getName(), foundRuleChain.getName());

8
application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java

@ -149,7 +149,7 @@ public class TenantControllerTest extends AbstractControllerTest {
testBroadcastEntityStateChangeEventTimeManyTimeTenant(savedTenant, ComponentLifecycleEvent.CREATED, 1);
savedTenant.setTitle("My new tenant");
saveTenant(savedTenant);
savedTenant = saveTenant(savedTenant);
Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class);
Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle());
@ -470,7 +470,7 @@ public class TenantControllerTest extends AbstractControllerTest {
tenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
tenant.setTenantProfileId(tenantProfile.getId());
saveTenant(tenant);
tenant = saveTenant(tenant);
login(username, password);
@ -500,7 +500,7 @@ public class TenantControllerTest extends AbstractControllerTest {
tenantProfile2 = doPost("/api/tenantProfile", tenantProfile2, TenantProfile.class);
tenant.setTenantProfileId(tenantProfile2.getId());
saveTenant(tenant);
tenant = saveTenant(tenant);
login(username, password);
@ -542,7 +542,7 @@ public class TenantControllerTest extends AbstractControllerTest {
loginSysAdmin();
tenant.setTenantProfileId(null);
saveTenant(tenant);
tenant = saveTenant(tenant);
login(username, password);
for (Queue queue : foundTenantQueues) {

10
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -83,6 +83,7 @@ import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
@ -96,6 +97,7 @@ import org.thingsboard.server.gen.edge.v1.TenantProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.TenantUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import java.util.ArrayList;
@ -143,7 +145,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(OAuth2UpdateMsg.class);
edgeImitator.expectMessageAmount(21);
edgeImitator.expectMessageAmount(24);
edgeImitator.connect();
requestEdgeRuleChainMetadata();
@ -250,7 +252,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
UUID ruleChainUUID = validateRuleChains();
// 1 from request message
validateMsgsCnt(RuleChainMetadataUpdateMsg.class, 1);
validateMsgsCnt(RuleChainMetadataUpdateMsg.class, 2);
validateRuleChainMetadataUpdates(ruleChainUUID);
// 4 messages ('general', 'mail', 'connectivity', 'jwt')
@ -275,6 +277,8 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
validateMsgsCnt(DeviceUpdateMsg.class, 1);
validateDevices();
validateMsgsCnt(DeviceCredentialsUpdateMsg.class, 1);
// 1 from asset fetcher
validateMsgsCnt(AssetUpdateMsg.class, 1);
validateAssets();
@ -287,6 +291,8 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
validateMsgsCnt(UserUpdateMsg.class, 1);
validateUsers();
validateMsgsCnt(UserCredentialsUpdateMsg.class, 1);
// 1 from tenant fetcher
validateMsgsCnt(TenantUpdateMsg.class, 1);
validateTenant();

58
application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java

@ -150,25 +150,25 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
+ "/edge/" + edge.getUuidId(), Edge.class);
Assert.assertTrue(edgeImitator.waitForMessages());
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
doPost("/api/customer/" + savedCustomer.getUuidId()
+ "/device/" + savedDevice.getUuidId(), Device.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
deviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class);
Assert.assertTrue(deviceUpdateMsgOpt.isPresent());
deviceUpdateMsg = deviceUpdateMsgOpt.get();
deviceFromMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
Assert.assertNotNull(deviceFromMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType());
Assert.assertEquals(savedCustomer.getId(), deviceFromMsg.getCustomerId());
// unassign device #2 from customer
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
doDelete("/api/customer/device/" + savedDevice.getUuidId(), Device.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
deviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class);
Assert.assertTrue(deviceUpdateMsgOpt.isPresent());
deviceUpdateMsg = deviceUpdateMsgOpt.get();
deviceFromMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
Assert.assertNotNull(deviceFromMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType());
@ -204,8 +204,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId("access_token");
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isOk());
deviceCredentials = doPost("/api/device/credentials", deviceCredentials, DeviceCredentials.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg);
@ -218,8 +217,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
deviceCredentials.setCredentialsType(DeviceCredentialsType.X509_CERTIFICATE);
deviceCredentials.setCredentialsId(null);
deviceCredentials.setCredentialsValue("-----BEGIN RSA PRIVATE KEY-----");
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isOk());
deviceCredentials = doPost("/api/device/credentials", deviceCredentials, DeviceCredentials.class);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceCredentialsUpdateMsg);
@ -243,7 +241,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
Assert.assertTrue(edgeImitator.waitForMessages());
// update device
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
savedDevice.setFirmwareId(firmwareOtaPackageInfo.getId());
savedDevice.setSoftwareId(softwareOtaPackageInfo.getId());
@ -256,9 +254,9 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
savedDevice = doPost("/api/device", savedDevice, Device.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
DeviceUpdateMsg deviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
Optional<DeviceUpdateMsg> deviceUpdateMsgOpt = edgeImitator.findMessageByType(DeviceUpdateMsg.class);
Assert.assertTrue(deviceUpdateMsgOpt.isPresent());
DeviceUpdateMsg deviceUpdateMsg = deviceUpdateMsgOpt.get();
Device deviceMsg = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
Assert.assertNotNull(deviceMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, deviceUpdateMsg.getMsgType());
@ -504,7 +502,7 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build());
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(2);
edgeImitator.expectMessageAmount(1);
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
@ -526,18 +524,6 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
Device device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
Assert.assertNotEquals(deviceOnCloudName, device.getName());
Optional<DeviceCredentialsRequestMsg> deviceCredentialsUpdateMsgOpt = edgeImitator.findMessageByType(DeviceCredentialsRequestMsg.class);
Assert.assertTrue(deviceCredentialsUpdateMsgOpt.isPresent());
DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = deviceCredentialsUpdateMsgOpt.get();
Assert.assertEquals(deviceMsg.getUuidId().getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB());
Assert.assertEquals(device.getUuidId().getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
device = doGet("/api/device/" + newDeviceId, Device.class);
Assert.assertNotNull(device);
Assert.assertNotEquals(deviceOnCloudName, device.getName());
}
@Test
@ -553,22 +539,10 @@ public class DeviceEdgeTest extends AbstractEdgeTest {
uplinkMsgBuilder.addDeviceUpdateMsg(deviceUpdateMsgBuilder.build());
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceCredentialsRequestMsg);
DeviceCredentialsRequestMsg latestDeviceCredentialsRequestMsg = (DeviceCredentialsRequestMsg) latestMessage;
Assert.assertEquals(deviceMsg.getUuidId().getMostSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdMSB());
Assert.assertEquals(deviceMsg.getUuidId().getLeastSignificantBits(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
UUID newDeviceId = new UUID(latestDeviceCredentialsRequestMsg.getDeviceIdMSB(), latestDeviceCredentialsRequestMsg.getDeviceIdLSB());
Device device = doGet("/api/device/" + newDeviceId, Device.class);
Device device = doGet("/api/device/" + deviceMsg.getId().getId(), Device.class);
Assert.assertNotNull(device);
Assert.assertEquals("Edge Device 2", device.getName());
}

4
application/src/test/java/org/thingsboard/server/edge/RuleChainEdgeTest.java

@ -193,7 +193,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest {
ruleChain.setType(RuleChainType.EDGE);
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class);
edgeImitator.expectMessageAmount(2);
edgeImitator.expectMessageAmount(4);
doPost("/api/edge/" + edge.getUuidId()
+ "/ruleChain/" + savedRuleChain.getUuidId(), RuleChain.class);
RuleChainMetaData metaData = createRuleChainMetadata(savedRuleChain);
@ -201,7 +201,7 @@ public class RuleChainEdgeTest extends AbstractEdgeTest {
// set new rule chain as root
RuleChainId currentRootRuleChainId = edge.getRootRuleChainId();
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
doPost("/api/edge/" + edge.getUuidId()
+ "/" + savedRuleChain.getUuidId() + "/root", Edge.class);
Assert.assertTrue(edgeImitator.waitForMessages());

6
application/src/test/java/org/thingsboard/server/edge/TelemetryEdgeTest.java

@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
@ -183,7 +184,7 @@ public class TelemetryEdgeTest extends AbstractEdgeTest {
edgeImitator.setRandomFailuresOnTimeseriesDownlink(true);
// imitator will generate failure in 100% of timeseries cases
edgeImitator.setFailureProbability(100);
edgeImitator.expectMessageAmount(numberOfMsgsToSend);
edgeImitator.expectMessageAmount(numberOfMsgsToSend * 2);
for (int idx = 1; idx <= numberOfMsgsToSend; idx++) {
String timeseriesData = "{\"data\":{\"idx\":" + idx + "},\"ts\":" + System.currentTimeMillis() + "}";
JsonNode timeseriesEntityData = JacksonUtil.toJsonNode(timeseriesData);
@ -204,6 +205,9 @@ public class TelemetryEdgeTest extends AbstractEdgeTest {
List<DeviceUpdateMsg> deviceUpdateMsgs = edgeImitator.findAllMessagesByType(DeviceUpdateMsg.class);
Assert.assertEquals(numberOfMsgsToSend, deviceUpdateMsgs.size());
List<DeviceCredentialsUpdateMsg> deviceCredentialsUpdateMsgs = edgeImitator.findAllMessagesByType(DeviceCredentialsUpdateMsg.class);
Assert.assertEquals(numberOfMsgsToSend, deviceCredentialsUpdateMsgs.size());
edgeImitator.setRandomFailuresOnTimeseriesDownlink(false);
}

28
application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java

@ -47,7 +47,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
@Test
public void testCreateUpdateDeleteTenantUser() throws Exception {
// create user
edgeImitator.expectMessageAmount(3);
edgeImitator.expectMessageAmount(4);
User newTenantAdmin = new User();
newTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
newTenantAdmin.setTenantId(tenantId);
@ -55,7 +55,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
newTenantAdmin.setFirstName("Boris");
newTenantAdmin.setLastName("Johnson");
User savedTenantAdmin = createUser(newTenantAdmin, "tenant");
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - user update msg and x2 user credentials update msgs
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x2 user update msg and x2 user credentials update msgs
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
@ -71,13 +71,13 @@ public class UserEdgeTest extends AbstractEdgeTest {
Assert.assertTrue(userCredentialsUpdateMsgOpt.isPresent());
// update user
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
savedTenantAdmin.setLastName("Borisov");
savedTenantAdmin = doPost("/api/user", savedTenantAdmin, User.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserUpdateMsg);
userUpdateMsg = (UserUpdateMsg) latestMessage;
userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
userUpdateMsg = userUpdateMsgOpt.get();
userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
@ -92,7 +92,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
changePasswordRequest.setNewPassword("newTenant");
doPost("/api/auth/changePassword", changePasswordRequest);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
@ -131,7 +131,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
Assert.assertTrue(edgeImitator.waitForMessages());
// create user
edgeImitator.expectMessageAmount(3);
edgeImitator.expectMessageAmount(4);
User customerUser = new User();
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(tenantId);
@ -140,7 +140,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
customerUser.setFirstName("John");
customerUser.setLastName("Edwards");
User savedCustomerUser = createUser(customerUser, "customer");
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - user update msg and x2 user credentials update msgs
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x2 user update msg and x2 user credentials update msgs
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
@ -155,13 +155,13 @@ public class UserEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName());
// update user
edgeImitator.expectMessageAmount(1);
edgeImitator.expectMessageAmount(2);
savedCustomerUser.setLastName("Addams");
savedCustomerUser = doPost("/api/user", savedCustomerUser, User.class);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserUpdateMsg);
userUpdateMsg = (UserUpdateMsg) latestMessage;
userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
userUpdateMsg = userUpdateMsgOpt.get();
userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
@ -176,7 +176,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
changePasswordRequest.setNewPassword("newCustomer");
doPost("/api/auth/changePassword", changePasswordRequest);
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);

6
application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/device/AbstractDeviceProcessorTest.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessorTest;
@ -61,6 +62,9 @@ public abstract class AbstractDeviceProcessorTest extends BaseEdgeProcessorTest
deviceProfile.setProfileData(deviceProfileData);
deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(deviceId);
Device device = new Device();
device.setDeviceProfileId(deviceProfileId);
device.setId(deviceId);
@ -71,9 +75,9 @@ public abstract class AbstractDeviceProcessorTest extends BaseEdgeProcessorTest
edgeEvent.setTenantId(tenantId);
edgeEvent.setAction(EdgeEventActionType.ADDED);
willReturn(device).given(deviceService).findDeviceById(tenantId, deviceId);
willReturn(deviceProfile).given(deviceProfileService).findDeviceProfileById(tenantId, deviceProfileId);
willReturn(deviceCredentials).given(deviceCredentialsService).findDeviceCredentialsByDeviceId(tenantId, deviceId);
}
protected void updateDeviceProfileDefaultFields(long expectedDashboardIdMSB, long expectedDashboardIdLSB,

33
application/src/test/java/org/thingsboard/server/system/RestTemplateConvertersTest.java

@ -17,20 +17,45 @@ package org.thingsboard.server.system;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.springframework.http.MediaType;
import org.springframework.mock.http.client.MockClientHttpRequest;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.util.ClassUtils;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@Slf4j
public class RestTemplateConvertersTest {
@Test
public void testJacksonXmlConverter() {
public void testMappingJackson2HttpMessageConverterIsUsedInsteadOfMappingJackson2XmlHttpMessageConverter() {
ClassLoader classLoader = RestTemplate.class.getClassLoader();
boolean jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
Assertions.assertFalse(jackson2XmlPresent, "XmlMapper must not be present in classpath, please, exclude \"jackson-dataformat-xml\" dependency!");
//If this xml mapper will be present in classpath then we will get "Unsupported Media Type" in RestTemplate
assertThat(jackson2XmlPresent).isTrue();
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer.expect(requestTo("/test"))
.andExpect(request -> {
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
byte[] body = mockRequest.getBodyAsBytes();
String requestBody = new String(body, StandardCharsets.UTF_8);
assertThat(requestBody).contains("{\"name\":\"test\",\"value\":1}");
})
.andRespond(withSuccess("{\"name\":\"test\",\"value\":1}", MediaType.APPLICATION_JSON));
TestObject requestObject = new TestObject("test", 1);
TestObject actualObject = restTemplate.postForObject("/test", requestObject, TestObject.class);
assertThat(actualObject).isEqualTo(requestObject);
mockServer.verify();
}
record TestObject(String name, int value) {}
}

10
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java

@ -59,7 +59,7 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
createDeviceProfile(transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO));
final Device device = createDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
awaitObserveReadAll(0, device.getId().getId().toString());
device.setFirmwareId(createFirmware().getId());
@ -145,12 +145,14 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
return tsKvEntries;
}
private boolean predicateForStatuses (List<TsKvEntry> ts) {
List<OtaPackageUpdateStatus> statuses = ts.stream().sorted(Comparator
.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString)
private boolean predicateForStatuses(List<TsKvEntry> ts) {
List<OtaPackageUpdateStatus> statuses = ts.stream()
.sorted(Comparator.comparingLong(TsKvEntry::getTs))
.map(KvEntry::getValueAsString)
.map(OtaPackageUpdateStatus::valueOf)
.collect(Collectors.toList());
log.warn("{}", statuses);
return statuses.containsAll(expectedStatuses);
}
}

10
common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java

@ -39,14 +39,14 @@ public abstract class VersionedCaffeineTbCache<K extends Serializable, V extends
@Override
public void put(K key, V value) {
Long version = value != null ? value.getVersion() : 0;
Long version = getVersion(value);
if (version == null) {
return;
}
doPut(key, value, version);
}
private void doPut(K key, V value, Long version) {
if (version == null) {
return;
}
lock.lock();
try {
TbPair<Long, V> versionValuePair = doGet(key);
@ -85,7 +85,7 @@ public abstract class VersionedCaffeineTbCache<K extends Serializable, V extends
@Override
void doPutIfAbsent(K key, V value) {
cache.putIfAbsent(key, wrapValue(value, value != null ? value.getVersion() : 0));
cache.putIfAbsent(key, wrapValue(value, getVersion(value)));
}
private TbPair<Long, V> wrapValue(V value, Long version) {

23
common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java

@ -46,20 +46,11 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
redis.call('SET', key, newValueWithVersion, 'EX', expiration)
end
local function bytes_to_number(bytes)
local n = 0
for i = 1, 8 do
n = n * 256 + string.byte(bytes, i)
end
return n
end
-- Get the current version (first 8 bytes) of the current value
local currentVersionBytes = redis.call('GETRANGE', key, 0, 7)
if currentVersionBytes and #currentVersionBytes == 8 then
local currentVersion = bytes_to_number(currentVersionBytes)
local currentVersion = struct.unpack(">I8", currentVersionBytes)
if newVersion > currentVersion then
setNewValue()
end
@ -68,7 +59,7 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
setNewValue()
end
""");
static final byte[] SET_VERSIONED_VALUE_SHA = StringRedisSerializer.UTF_8.serialize("80e56cbbbb4bd9cb150d6537f1e7d8df4fddb252");
static final byte[] SET_VERSIONED_VALUE_SHA = StringRedisSerializer.UTF_8.serialize("0453cb1814135b706b4198b09a09f43c9f67bbfe");
public VersionedRedisTbCache(String cacheName, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory, TBRedisCacheConfiguration configuration, TbRedisSerializer<K, V> valueSerializer) {
super(cacheName, cacheSpecsMap, connectionFactory, configuration, valueSerializer);
@ -168,14 +159,4 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
throw new NotImplementedException("evictOrPut is not supported by versioned cache");
}
private Long getVersion(V value) {
if (value == null) {
return 0L;
} else if (value.getVersion() != null) {
return value.getVersion();
} else {
return null;
}
}
}

10
common/cache/src/main/java/org/thingsboard/server/cache/VersionedTbCache.java

@ -50,4 +50,14 @@ public interface VersionedTbCache<K extends Serializable, V extends Serializable
void evict(K key, Long version);
default Long getVersion(V value) {
if (value == null) {
return 0L;
} else if (value.getVersion() != null) {
return value.getVersion();
} else {
return null;
}
}
}

2
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheEvictEvent.java

@ -16,6 +16,7 @@
package org.thingsboard.server.cache.device;
import lombok.Data;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -26,5 +27,6 @@ public class DeviceCacheEvictEvent {
private final DeviceId deviceId;
private final String newName;
private final String oldName;
private Device savedDevice;
}

4
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCaffeineCache.java

@ -18,13 +18,13 @@ package org.thingsboard.server.cache.device;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.cache.VersionedCaffeineTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Device;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("DeviceCache")
public class DeviceCaffeineCache extends CaffeineTbTransactionalCache<DeviceCacheKey, Device> {
public class DeviceCaffeineCache extends VersionedCaffeineTbCache<DeviceCacheKey, Device> {
public DeviceCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.DEVICE_CACHE);

4
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceRedisCache.java

@ -21,9 +21,9 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CacheSpecsMap;
import org.thingsboard.server.cache.RedisTbTransactionalCache;
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
import org.thingsboard.server.cache.TbRedisSerializer;
import org.thingsboard.server.cache.VersionedRedisTbCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.util.ProtoUtils;
@ -31,7 +31,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("DeviceCache")
public class DeviceRedisCache extends RedisTbTransactionalCache<DeviceCacheKey, Device> {
public class DeviceRedisCache extends VersionedRedisTbCache<DeviceCacheKey, Device> {
public DeviceRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.DEVICE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbRedisSerializer<>() {

5
common/data/src/main/java/org/thingsboard/server/common/data/Customer.java

@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true)
public class Customer extends ContactBased<CustomerId> implements HasTenantId, ExportableEntity<CustomerId>, HasTitle {
public class Customer extends ContactBased<CustomerId> implements HasTenantId, ExportableEntity<CustomerId>, HasTitle, HasVersion {
private static final long serialVersionUID = -1599722990298929275L;
@ -42,6 +42,8 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
@Getter @Setter
private CustomerId externalId;
@Getter @Setter
private Long version;
public Customer() {
super();
@ -56,6 +58,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId, E
this.tenantId = customer.getTenantId();
this.title = customer.getTitle();
this.externalId = customer.getExternalId();
this.version = customer.getVersion();
}
public TenantId getTenantId() {

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

Loading…
Cancel
Save