diff --git a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg index e59a2c8375..5a886556f8 100644 --- a/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/bottom-flow-meter.svg @@ -926,7 +926,7 @@ - 0m³/hr + 0m³/hr diff --git a/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg index 3b07c31186..fa23dbbe93 100644 --- a/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/cylindrical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -633,7 +633,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/elevated-tank.svg b/application/src/main/data/json/system/scada_symbols/elevated-tank.svg index 9502136bf1..e3731ab979 100644 --- a/application/src/main/data/json/system/scada_symbols/elevated-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/elevated-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 265;\n var majorIntervalLength = 895 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(825, y, 857, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 815, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(837, minorY, 857, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 265;\n var majorIntervalLength = 895 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(825, y, 857, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 815, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(837, minorY, 857, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -626,7 +626,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg index c20584288a..3e13dc1126 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-inline-flow-meter.svg @@ -906,7 +906,7 @@ - 0m³/hr + 0m³/hr diff --git a/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg b/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg index db0906677e..646957a6b4 100644 --- a/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/horizontal-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -637,7 +637,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg index 08ea8a4533..eaea959550 100644 --- a/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-cylindrical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -633,7 +633,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg index c805ec3de3..3d2708ef11 100644 --- a/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-stand-cylindrical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 60;\n var majorIntervalLength = 910 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(656, y, 688, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 646, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(668, minorY, 688, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -634,7 +634,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg index 46fb273d2c..89d3095953 100644 --- a/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-stand-vertical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -634,7 +634,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg index 935e43f7a5..7eebad7d7a 100644 --- a/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/large-vertical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 203;\n var majorIntervalLength = 763 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(676, y, 708, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 666, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(688, minorY, 708, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -633,7 +633,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg b/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg index b3defcd1bf..9b7a15bf7c 100644 --- a/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-analog-water-level-meter.svg @@ -664,7 +664,7 @@ }]]> - Water + Water diff --git a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg index d558e81df1..faa5153531 100644 --- a/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/left-flow-meter.svg @@ -920,7 +920,7 @@ - 0m³/hr + 0m³/hr diff --git a/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg b/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg index b682fe2063..251dfe6729 100644 --- a/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/left-heat-pump.svg @@ -680,7 +680,7 @@ - 27 + 27 diff --git a/application/src/main/data/json/system/scada_symbols/pool.svg b/application/src/main/data/json/system/scada_symbols/pool.svg index 57086a7a71..f5d0bd7ad7 100644 --- a/application/src/main/data/json/system/scada_symbols/pool.svg +++ b/application/src/main/data/json/system/scada_symbols/pool.svg @@ -291,7 +291,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg b/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg index 7c942b6fe5..592339003f 100644 --- a/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-analog-water-level-meter.svg @@ -664,7 +664,7 @@ }]]> - Water + Water diff --git a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg index 9fc751851a..71ade4c774 100644 --- a/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/right-flow-meter.svg @@ -939,7 +939,7 @@ - 0m³/hr + 0m³/hr diff --git a/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg b/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg index 32007b18f7..c2b4042671 100644 --- a/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg +++ b/application/src/main/data/json/system/scada_symbols/right-heat-pump.svg @@ -680,7 +680,7 @@ - 27 + 27 diff --git a/application/src/main/data/json/system/scada_symbols/sand-filter.svg b/application/src/main/data/json/system/scada_symbols/sand-filter.svg index 1f9c0293d9..59aa228a6f 100644 --- a/application/src/main/data/json/system/scada_symbols/sand-filter.svg +++ b/application/src/main/data/json/system/scada_symbols/sand-filter.svg @@ -8,7 +8,7 @@ ], "widgetSizeX": 3, "widgetSizeY": 5, - "stateRenderFunction": "var running = ctx.values.running;\nif (running) {\n ctx.api.enable(svg.children());\n} else {\n ctx.api.disable(svg.children());\n}", + "stateRenderFunction": "var running = ctx.values.running;\nif (running) {\n ctx.api.enable(ctx.tags.filterMode);\n} else {\n ctx.api.disable(ctx.tags.filterMode);\n}", "tags": [ { "tag": "background", @@ -354,37 +354,37 @@ - Filter + Filter - Backwash + Backwash - Rinse + Rinse - Waste + Waste - Recirculate + Recirculate - Closed + Closed diff --git a/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg b/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg index fd0370db2f..48e6d755f2 100644 --- a/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/small-spherical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 560 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(268, y, 300, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 258, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(280, minorY, 300, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 560 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(268, y, 300, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 258, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(280, minorY, 300, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -604,7 +604,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/spherical-tank.svg b/application/src/main/data/json/system/scada_symbols/spherical-tank.svg index 191be8dcec..7cb36206a4 100644 --- a/application/src/main/data/json/system/scada_symbols/spherical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/spherical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 960 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(458, y, 490, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 448, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(470, minorY, 490, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 23;\n var majorIntervalLength = 960 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(458, y, 490, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 448, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(470, minorY, 490, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -634,7 +634,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg index 127f6bbd84..a1b8ad7072 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-cylindrical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -634,7 +634,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg index df7ac82ffc..a47d208f41 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-horizontal-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 17;\n var majorIntervalLength = 568 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(715, y, 747, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 705, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(727, minorY, 747, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -638,7 +638,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg index e14833c18b..92a1c6b8d5 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-vertical-short-tank.svg @@ -35,7 +35,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -607,7 +607,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg index 2e6831b8ca..8b8038fde0 100644 --- a/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/stand-vertical-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -636,7 +636,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg index 907b0c5211..2162610cfc 100644 --- a/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/top-flow-meter.svg @@ -920,7 +920,7 @@ - 0m³/hr + 0m³/hr diff --git a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg index a002ca57a4..4d5017a6c4 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-inline-flow-meter.svg @@ -906,7 +906,7 @@ - 0m³/hr + 0m³/hr diff --git a/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg b/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg index 332a138e2c..82d33a2e2c 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-short-tank.svg @@ -34,7 +34,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 137;\n var majorIntervalLength = 442 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(523, y, 555, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 513, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(535, minorY, 555, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -606,7 +606,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/main/data/json/system/scada_symbols/vertical-tank.svg b/application/src/main/data/json/system/scada_symbols/vertical-tank.svg index bb0cc8f2b6..207a406a93 100644 --- a/application/src/main/data/json/system/scada_symbols/vertical-tank.svg +++ b/application/src/main/data/json/system/scada_symbols/vertical-tank.svg @@ -33,7 +33,7 @@ }, { "tag": "scale", - "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', 'dominant-baseline': 'middle', class: 'majorTickText'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", + "stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 205;\n var majorIntervalLength = 760 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(340, y, 372, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 330, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n if (ctx.values.critical) {\n majorColor = ctx.properties.majorCriticalColor;\n minorColor = ctx.properties.minorCriticalColor;\n } else if (ctx.values.warning) {\n majorColor = ctx.properties.minorWarningColor;\n minorColor = ctx.properties.minorWarningColor;\n }\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n \n var elementCriticalAnimation = element.remember('criticalAnimation');\n var criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\n if (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n }\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(352, minorY, 372, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}", "actions": null }, { @@ -635,7 +635,7 @@ - 1660 gal + 1660 gal diff --git a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java index 2bf10f742a..9b9da4e797 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java @@ -355,8 +355,8 @@ public class ImageControllerTest extends AbstractControllerTest { assertThat(previewDescriptor.getMediaType()).isEqualTo("image/png"); assertThat(previewDescriptor.getWidth()).isEqualTo(225); assertThat(previewDescriptor.getHeight()).isEqualTo(225); - assertThat(previewDescriptor.getSize()).isEqualTo(53498); - assertThat(previewDescriptor.getEtag()).isEqualTo("c909e20ba942f95f4ed1ddfcf4307ce846b4a689195c629cd85f2517f46e84f9"); + assertThat(previewDescriptor.getSize()).isEqualTo(52365); + assertThat(previewDescriptor.getEtag()).isEqualTo("f231854ceb5cfce4371c246f7738d35d61770bac106ad3b9ee9b74d19817d09e"); } private void checkSvgImageDescriptor(ImageDescriptor imageDescriptor) { diff --git a/dao/pom.xml b/dao/pom.xml index 6f5ec41672..a5a0719ae7 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -246,12 +246,8 @@ hypersistence-utils-hibernate-63 - org.apache.xmlgraphics - batik-transcoder - - - org.apache.xmlgraphics - batik-codec + com.github.weisj + jsvg com.drewnoakes diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java b/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java index 19649655a3..e37e9cef48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/ImageUtils.java @@ -21,37 +21,37 @@ import com.drew.metadata.Metadata; import com.drew.metadata.Tag; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.github.weisj.jsvg.SVGDocument; +import com.github.weisj.jsvg.SVGRenderingHints; +import com.github.weisj.jsvg.attributes.ViewBox; +import com.github.weisj.jsvg.geometry.size.FloatSize; +import com.github.weisj.jsvg.parser.DefaultParserProvider; +import com.github.weisj.jsvg.parser.LoaderContext; +import com.github.weisj.jsvg.parser.SVGLoader; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.With; import lombok.extern.slf4j.Slf4j; -import org.apache.batik.anim.dom.SAXSVGDocumentFactory; -import org.apache.batik.bridge.BridgeContext; -import org.apache.batik.bridge.DocumentLoader; -import org.apache.batik.bridge.GVTBuilder; -import org.apache.batik.bridge.UserAgent; -import org.apache.batik.bridge.UserAgentAdapter; -import org.apache.batik.gvt.GraphicsNode; -import org.apache.batik.transcoder.TranscoderInput; -import org.apache.batik.transcoder.TranscoderOutput; -import org.apache.batik.transcoder.image.PNGTranscoder; -import org.apache.batik.util.XMLResourceDescriptor; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.StringUtils; -import org.w3c.dom.Document; +import javax.imageio.IIOImage; import javax.imageio.ImageIO; -import java.awt.Color; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.ImageWriteParam; +import javax.imageio.ImageWriter; +import java.awt.*; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Map; +import java.util.regex.Pattern; @NoArgsConstructor(access = AccessLevel.PRIVATE) @Slf4j @@ -144,52 +144,32 @@ public class ImageUtils { BufferedImage thumbnail = new BufferedImage(preview.getWidth(), preview.getHeight(), BufferedImage.TYPE_INT_ARGB); thumbnail.getGraphics().drawImage(bufferedImage, 0, 0, preview.getWidth(), preview.getHeight(), null); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ImageIO.write(thumbnail, "png", out); + + byte[] pngThumbnail = toCompressedPngData(thumbnail); preview.setMediaType("image/png"); - preview.setData(out.toByteArray()); - preview.setSize(preview.getData().length); + preview.setData(pngThumbnail); + preview.setSize(pngThumbnail.length); image.setPreview(preview); return image; } public static ProcessedImage processSvgImage(byte[] data, String mediaType, int thumbnailMaxDimension) throws Exception { - SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName()); - Document document = factory.createDocument(null, new ByteArrayInputStream(data)); + var imageData = removeScadaSymbolMetadata(data); + + SVGLoader loader = new SVGLoader(); + SVGDocument svgDocument = loader.load(new ByteArrayInputStream(imageData), null, LoaderContext.builder() + .parserProvider(new DefaultParserProvider()) + .build()); + Integer width = null; Integer height = null; - String strWidth = document.getDocumentElement().getAttribute("width"); - String strHeight = document.getDocumentElement().getAttribute("height"); - if (StringUtils.isNotEmpty(strWidth) && StringUtils.isNotEmpty(strHeight)) { - try { - width = (int) Double.parseDouble(strWidth); - height = (int) Double.parseDouble(strHeight); - } catch (NumberFormatException ignored) {} // in case width and height are in %, mm, etc. - } - if (width == null || height == null) { - String viewBox = document.getDocumentElement().getAttribute("viewBox"); - if (StringUtils.isNotEmpty(viewBox)) { - String[] viewBoxValues = viewBox.split(" "); - if (viewBoxValues.length > 3) { - width = (int) Double.parseDouble(viewBoxValues[2]); - height = (int) Double.parseDouble(viewBoxValues[3]); - } - } - } - if (width == null) { - UserAgent agent = new UserAgentAdapter(); - DocumentLoader loader = new DocumentLoader(agent); - BridgeContext context = new BridgeContext(agent, loader); - context.setDynamic(true); - GVTBuilder builder = new GVTBuilder(); - GraphicsNode root = builder.build(context, document); - var bounds = root.getPrimitiveBounds(); - if (bounds != null) { - width = (int) bounds.getWidth(); - height = (int) bounds.getHeight(); - } + if (svgDocument != null) { + FloatSize imageSize = svgDocument.size(); + width = (int) imageSize.width; + height = (int) imageSize.height; } + ProcessedImage image = new ProcessedImage(); image.setMediaType(mediaType); image.setWidth(width == null ? 0 : width); @@ -197,26 +177,31 @@ public class ImageUtils { image.setData(data); image.setSize(data.length); - PNGTranscoder transcoder = new PNGTranscoder(); - if (image.getSize() < 10240) { // if SVG is smaller than 10kB (average 250x250 PNG preview size) - return withPreviewAsOriginalImage(image); + if (imageData.length < 10240 || svgDocument == null) { // if SVG is smaller than 10kB (average 250x250 PNG preview size) + return withPreviewAsOriginalImage(image, imageData); } else if (image.getSize() > 102400 && image.getWidth() != 0) { // considering SVG image detailed after 100kB - // increasing preview dimensions thumbnailMaxDimension = 512; - int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension, false); - transcoder.addTranscodingHint(PNGTranscoder.KEY_WIDTH, (float) thumbnailDimensions[0]); - transcoder.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, (float) thumbnailDimensions[1]); - } else { - transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_WIDTH, (float) thumbnailMaxDimension); - transcoder.addTranscodingHint(PNGTranscoder.KEY_MAX_HEIGHT, (float) thumbnailMaxDimension); } - ByteArrayOutputStream out = new ByteArrayOutputStream(); - transcoder.transcode(new TranscoderInput(new ByteArrayInputStream(data)), new TranscoderOutput(out)); - byte[] pngThumbnail = out.toByteArray(); - ProcessedImage preview = new ProcessedImage(); - preview.setWidth(thumbnailMaxDimension); - preview.setHeight(thumbnailMaxDimension); + int[] thumbnailDimensions = getThumbnailDimensions(image.getWidth(), image.getHeight(), thumbnailMaxDimension, false); + preview.setWidth(thumbnailDimensions[0]); + preview.setHeight(thumbnailDimensions[1]); + + BufferedImage thumbnail = new BufferedImage(preview.getWidth(), preview.getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = thumbnail.createGraphics(); + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); + graphics.setRenderingHint(SVGRenderingHints.KEY_IMAGE_ANTIALIASING, SVGRenderingHints.VALUE_IMAGE_ANTIALIASING_ON); + graphics.setRenderingHint(SVGRenderingHints.KEY_SOFT_CLIPPING, SVGRenderingHints.VALUE_SOFT_CLIPPING_ON); + svgDocument.render((Component)null,graphics, new ViewBox(0, 0, preview.getWidth(), preview.getHeight())); + graphics.dispose(); + + byte[] pngThumbnail = toCompressedPngData(thumbnail); + + if (imageData.length < pngThumbnail.length) { // set preview as original SVG if PNG thumbnail size is greater + return withPreviewAsOriginalImage(image, imageData); + } + preview.setMediaType("image/png"); preview.setData(pngThumbnail); preview.setSize(pngThumbnail.length); @@ -224,6 +209,27 @@ public class ImageUtils { return image; } + public static byte[] toCompressedPngData(BufferedImage image) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(image); + ImageWriter writer = ImageIO.getImageWriters(type, "png").next(); + ImageWriteParam param = writer.getDefaultWriteParam(); + if (param.canWriteCompressed()) { + param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + param.setCompressionQuality(0.0f); + } + var output = ImageIO.createImageOutputStream(out); + writer.setOutput(output); + try { + writer.write(null, new IIOImage(image, null, null), param); + } finally { + writer.dispose(); + output.flush(); + } + return out.toByteArray(); + } + + private static ProcessedImage previewAsOriginalImage(byte[] data, String mediaType) { ProcessedImage image = new ProcessedImage(); image.setMediaType(mediaType); @@ -235,21 +241,34 @@ public class ImageUtils { } public static ProcessedImage withPreviewAsOriginalImage(ProcessedImage originalImage) { - originalImage.setPreview(originalImage.withData(null)); + return withPreviewAsOriginalImage(originalImage, null); + } + + public static ProcessedImage withPreviewAsOriginalImage(ProcessedImage originalImage, byte[] previewData) { + originalImage.setPreview(originalImage.withData(previewData)); + if (previewData != null) { + originalImage.getPreview().setSize(previewData.length); + } return originalImage; } public static ScadaSymbolMetadataInfo processScadaSymbolMetadata(String fileName, byte[] data) throws Exception { - SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName()); - Document document = factory.createDocument(null, new ByteArrayInputStream(data)); - var metaElements = document.getElementsByTagName("tb:metadata"); JsonNode metaData = null; - if (metaElements.getLength() > 0) { - metaData = JacksonUtil.toJsonNode(metaElements.item(0).getTextContent()); + String contents = new String(data, StandardCharsets.UTF_8); + var matcher = Pattern.compile("(?s)]*><\\/tb:metadata>").matcher(contents); + if (matcher.find()) { + var metadataContent = matcher.group(1); + metaData = JacksonUtil.toJsonNode(metadataContent); } return new ScadaSymbolMetadataInfo(fileName, metaData); } + public static byte[] removeScadaSymbolMetadata(byte[] data) { + String contents = new String(data, StandardCharsets.UTF_8); + contents = contents.replaceFirst("(?s)]*>.*<\\/tb:metadata>", ""); + return contents.getBytes(StandardCharsets.UTF_8); + } + private static int[] getThumbnailDimensions(int originalWidth, int originalHeight, int maxDimension, boolean originalIfSmaller) { if (originalWidth <= maxDimension && originalHeight <= maxDimension && originalIfSmaller) { return new int[]{originalWidth, originalHeight}; diff --git a/pom.xml b/pom.xml index 6d0fa6f090..4ea6491eb2 100755 --- a/pom.xml +++ b/pom.xml @@ -163,7 +163,7 @@ 1.39.0 6.6.0 1.35.0 - 1.17 + 1.6.1 2.19.0 9.2.0 @@ -2262,20 +2262,9 @@ ${google-oauth-client.version} - org.apache.xmlgraphics - batik-transcoder - ${apache-xmlgraphics.version} - - - commons-logging - commons-logging - - - - - org.apache.xmlgraphics - batik-codec - ${apache-xmlgraphics.version} + com.github.weisj + jsvg + ${weisj-jsvg.version} com.drewnoakes diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 74c29bf00e..c37706b5e0 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -59,7 +59,7 @@ "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master", "font-awesome": "^4.7.0", "html2canvas": "^1.4.1", - "jquery": "^3.6.3", + "jquery": "^3.7.1", "jquery.terminal": "^2.35.3", "js-beautify": "1.14.7", "json-schema-defaults": "^0.4.0", @@ -127,7 +127,7 @@ "@types/flowjs": "^2.13.9", "@types/jasmine": "~3.10.2", "@types/jasminewd2": "^2.0.10", - "@types/jquery": "^3.5.16", + "@types/jquery": "^3.5.30", "@types/js-beautify": "^1.13.3", "@types/leaflet": "1.8.0", "@types/leaflet-polylinedecorator": "1.6.4", diff --git a/ui-ngx/src/app/app.component.ts b/ui-ngx/src/app/app.component.ts index 9ac5bc79a1..dcc33dcbd9 100644 --- a/ui-ngx/src/app/app.component.ts +++ b/ui-ngx/src/app/app.component.ts @@ -32,6 +32,7 @@ import { AuthService } from '@core/auth/auth.service'; import { svgIcons, svgIconsUrl } from '@shared/models/icon.models'; import { ActionSettingsChangeLanguage } from '@core/settings/settings.actions'; import { SETTINGS_KEY } from '@core/settings/settings.effects'; +import { initCustomJQueryEvents } from '@shared/models/jquery-event.models'; @Component({ selector: 'tb-root', @@ -74,6 +75,8 @@ export class AppComponent implements OnInit { this.setupTranslate(); this.setupAuth(); + + initCustomJQueryEvents(); } setupTranslate() { diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html index 33c8d659f6..16498100ae 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html @@ -27,7 +27,7 @@ [class.center-vertical]="centerVertical" [class.center-horizontal]="centerHorizontal" (mousedown)="onDashboardMouseDown($event)" - (contextmenu)="openDashboardContextMenu($event)"> + (tbcontextmenu)="openDashboardContextMenu($event)">
((_, item) => item), @@ -395,7 +396,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo } } - openDashboardContextMenu($event: MouseEvent) { + openDashboardContextMenu($event: TbContextMenuEvent) { if (this.callbacks && this.callbacks.prepareDashboardContextMenu) { const items = this.callbacks.prepareDashboardContextMenu($event); if (items && items.length) { @@ -410,7 +411,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo } } - private openWidgetContextMenu($event: MouseEvent, widget: DashboardWidget) { + private openWidgetContextMenu($event: TbContextMenuEvent, widget: DashboardWidget) { if (this.callbacks && this.callbacks.prepareWidgetContextMenu) { const items = this.callbacks.prepareWidgetContextMenu($event, widget.widget, widget.isReference); if (items && items.length) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html similarity index 77% rename from ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component.html rename to ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html index c2dead1abc..7122bcd52d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html @@ -16,20 +16,9 @@ --> -
- - {{ 'gateway.key' | translate }} - - - warning - - +
+ {{ 'gateway.rpc.hint.modbus-response-reading' | translate }}
+ {{ 'gateway.rpc.hint.modbus-writing-functions' | translate }}
@@ -45,21 +34,6 @@
-
- - {{ 'gateway.rpc.value' | translate }} - - - warning - - -
{{ 'gateway.rpc.address' | translate }} @@ -88,5 +62,20 @@ />
+
+ + {{ 'gateway.rpc.value' | translate }} + + + warning + + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss new file mode 100644 index 0000000000..62eaca664f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss @@ -0,0 +1,20 @@ +/** + * 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. + */ +:host { + .hint-container { + margin-bottom: 12px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts similarity index 94% rename from ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts index 17c48cc595..ae110b9418 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-rpc-parameters/modbus-rpc-parameters.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts @@ -39,13 +39,14 @@ import { ModbusEditableDataTypes, ModbusFunctionCodeTranslationsMap, ModbusObjectCountByDataType, - ModbusValue, noLeadTrailSpacesRegex, + RPCTemplateConfigModbus, } from '@home/components/widget/lib/gateway/gateway-widget.models'; @Component({ selector: 'tb-modbus-rpc-parameters', templateUrl: './modbus-rpc-parameters.component.html', + styleUrls: ['./modbus-rpc-parameters.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { @@ -80,14 +81,13 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid private readonly readFunctionCodes = [1, 2, 3, 4]; private readonly bitsFunctionCodes = [...this.readFunctionCodes, ...this.writeFunctionCodes]; - private onChange: (value: ModbusValue) => void; + private onChange: (value: RPCTemplateConfigModbus) => void; private onTouched: () => void; private destroy$ = new Subject(); constructor(private fb: FormBuilder) { this.rpcParametersFormGroup = this.fb.group({ - tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], type: [ModbusDataType.BYTES, [Validators.required]], functionCode: [this.defaultFunctionCodes[0], [Validators.required]], value: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], @@ -106,7 +106,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid this.destroy$.complete(); } - registerOnChange(fn: (value: ModbusValue) => void): void { + registerOnChange(fn: (value: RPCTemplateConfigModbus) => void): void { this.onChange = fn; } @@ -120,7 +120,7 @@ export class ModbusRpcParametersComponent implements ControlValueAccessor, Valid }; } - writeValue(value: ModbusValue): void { + writeValue(value: RPCTemplateConfigModbus): void { this.rpcParametersFormGroup.patchValue(value, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html new file mode 100644 index 0000000000..eb66a6df8c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html @@ -0,0 +1,48 @@ + + + + {{ 'gateway.rpc.method-name' | translate }} + + + + {{ 'gateway.rpc.requestTopicExpression' | translate }} + + + + {{ 'gateway.rpc.withResponse' | translate }} + + + {{ 'gateway.rpc.responseTopicExpression' | translate }} + + + + {{ 'gateway.rpc.responseTimeout' | translate }} + + + + {{ 'gateway.rpc.valueExpression' | translate }} + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss new file mode 100644 index 0000000000..a2dddebc47 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss @@ -0,0 +1,24 @@ +/** + * 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. + */ +:host { + display: flex; + flex-direction: column; + + .mat-mdc-slide-toggle.margin { + margin-bottom: 10px; + margin-left: 10px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts new file mode 100644 index 0000000000..56d9510e7d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts @@ -0,0 +1,139 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectionStrategy, + Component, + forwardRef, + OnDestroy, +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + ValidationErrors, + Validator, Validators, +} from '@angular/forms'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { Subject } from 'rxjs'; +import { takeUntil, tap } from 'rxjs/operators'; +import { + integerRegex, + noLeadTrailSpacesRegex, + RPCTemplateConfigMQTT +} from '@home/components/widget/lib/gateway/gateway-widget.models'; + +@Component({ + selector: 'tb-mqtt-rpc-parameters', + templateUrl: './mqtt-rpc-parameters.component.html', + styleUrls: ['./mqtt-rpc-parameters.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MqttRpcParametersComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => MqttRpcParametersComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + ], +}) +export class MqttRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy { + + rpcParametersFormGroup: UntypedFormGroup; + + private onChange: (value: RPCTemplateConfigMQTT) => void = (_) => {}; + private onTouched: () => void = () => {}; + + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder) { + this.rpcParametersFormGroup = this.fb.group({ + methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(integerRegex)]], + valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + withResponse: [false, []], + }); + + this.observeValueChanges(); + this.observeWithResponse(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: (value: RPCTemplateConfigMQTT) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + validate(): ValidationErrors | null { + return this.rpcParametersFormGroup.valid ? null : { + rpcParametersFormGroup: { valid: false } + }; + } + + writeValue(value: RPCTemplateConfigMQTT): void { + this.rpcParametersFormGroup.patchValue(value, {emitEvent: false}); + this.toggleResponseFields(value.withResponse); + } + + private observeValueChanges(): void { + this.rpcParametersFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + this.onChange(value); + this.onTouched(); + }); + } + + private observeWithResponse(): void { + this.rpcParametersFormGroup.get('withResponse').valueChanges.pipe( + tap((isActive: boolean) => this.toggleResponseFields(isActive)), + takeUntil(this.destroy$), + ).subscribe(); + } + + private toggleResponseFields(enabled: boolean): void { + const responseTopicControl = this.rpcParametersFormGroup.get('responseTopicExpression'); + const responseTimeoutControl = this.rpcParametersFormGroup.get('responseTimeout'); + if (enabled) { + responseTopicControl.enable(); + responseTimeoutControl.enable(); + } else { + responseTopicControl.disable(); + responseTimeoutControl.disable(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html new file mode 100644 index 0000000000..ec5c20cfb1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html @@ -0,0 +1,93 @@ + + +
+ {{ 'gateway.rpc.hint.opc-method' | translate }} +
+ + {{ 'gateway.rpc.method' | translate }} + + +
+ + {{ 'gateway.rpc.arguments' | translate }} + +
+
+
gateway.type
+
+ + + +
+ + + {{ valueTypes.get(argumentFormGroup.get('type').value)?.name | translate }} +
+
+ + + + {{ valueTypes.get(valueType).name | translate }} + +
+
+
+
+
+
gateway.value
+ + + + + + + true + false + + + + warning + + +
+ +
+ +
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss new file mode 100644 index 0000000000..5108cc70b1 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .arguments-container { + margin-bottom: 10px; + } + + .type-container { + width: 40%; + } + + .value-container { + width: 50%; + } + + .hint-container { + margin-bottom: 12px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts new file mode 100644 index 0000000000..0c0dbea3bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts @@ -0,0 +1,169 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + forwardRef, + OnDestroy, +} from '@angular/core'; +import { + ControlValueAccessor, + FormArray, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + ValidationErrors, + Validator, Validators, +} from '@angular/forms'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { + integerRegex, + MappingValueType, + mappingValueTypesMap, + noLeadTrailSpacesRegex, + OPCTypeValue, + RPCTemplateConfigOPC +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { isDefinedAndNotNull, isEqual } from '@core/utils'; + +@Component({ + selector: 'tb-opc-rpc-parameters', + templateUrl: './opc-rpc-parameters.component.html', + styleUrls: ['./opc-rpc-parameters.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => OpcRpcParametersComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => OpcRpcParametersComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + ], +}) +export class OpcRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy { + + rpcParametersFormGroup: UntypedFormGroup; + + readonly valueTypeKeys: MappingValueType[] = Object.values(MappingValueType); + readonly MappingValueType = MappingValueType; + readonly valueTypes = mappingValueTypesMap; + + private onChange: (value: RPCTemplateConfigOPC) => void = (_) => {} ; + private onTouched: () => void = () => {}; + + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) { + this.rpcParametersFormGroup = this.fb.group({ + method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + arguments: this.fb.array([]), + }); + + this.observeValueChanges(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: (value: RPCTemplateConfigOPC) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + validate(): ValidationErrors | null { + return this.rpcParametersFormGroup.valid ? null : { + rpcParametersFormGroup: { valid: false } + }; + } + + writeValue(params: RPCTemplateConfigOPC): void { + this.clearArguments(); + params.arguments?.map(({type, value}) => ({type, [type]: value })) + .forEach(argument => this.addArgument(argument as OPCTypeValue)); + this.cdr.markForCheck(); + this.rpcParametersFormGroup.get('method').patchValue(params.method); + } + + private observeValueChanges(): void { + this.rpcParametersFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(params => { + const updatedArguments = params.arguments.map(({type, ...config}) => ({type, value: config[type]})); + this.onChange({method: params.method, arguments: updatedArguments}); + this.onTouched(); + }); + } + + removeArgument(index: number): void { + (this.rpcParametersFormGroup.get('arguments') as FormArray).removeAt(index); + } + + addArgument(value: OPCTypeValue = {} as OPCTypeValue): void { + const fromGroup = this.fb.group({ + type: [value.type ?? MappingValueType.STRING], + string: [ + value.string ?? { value: '', disabled: !(isEqual(value, {}) || value.string)}, + [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)] + ], + integer: [ + {value: value.integer ?? 0, disabled: !isDefinedAndNotNull(value.integer)}, + [Validators.required, Validators.pattern(integerRegex)] + ], + double: [{value: value.double ?? 0, disabled: !isDefinedAndNotNull(value.double)}, [Validators.required]], + boolean: [{value: value.boolean ?? false, disabled: !isDefinedAndNotNull(value.boolean)}, [Validators.required]], + }); + this.observeTypeChange(fromGroup); + (this.rpcParametersFormGroup.get('arguments') as FormArray).push(fromGroup, {emitEvent: false}); + } + + clearArguments(): void { + const formArray = this.rpcParametersFormGroup.get('arguments') as FormArray; + while (formArray.length !== 0) { + formArray.removeAt(0); + } + } + + private observeTypeChange(dataKeyFormGroup: FormGroup): void { + dataKeyFormGroup.get('type').valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(type => { + dataKeyFormGroup.disable({emitEvent: false}); + dataKeyFormGroup.get('type').enable({emitEvent: false}); + dataKeyFormGroup.get(type).enable({emitEvent: false}); + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html index f72a2c18af..f40914f3d0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html @@ -70,8 +70,8 @@ matTooltipPosition="above" matTooltipClass="tb-error-tooltip" [matTooltip]="('gateway.value-required') | translate" - *ngIf="keyControl.get(keyControl.get('type').value).hasError('required') - && keyControl.get(keyControl.get('type').value).touched" + *ngIf="keyControl.get(keyControl.get('value').value).hasError('required') + && keyControl.get(keyControl.get('value').value).touched" class="tb-error"> warning diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts index 8e1c6fdb57..27096ea25a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts @@ -104,7 +104,7 @@ export class TypeValuePanelComponent implements ControlValueAccessor, Validator, dataKeyFormGroup.disable({emitEvent: false}); dataKeyFormGroup.get('type').enable({emitEvent: false}); dataKeyFormGroup.get(type).enable({emitEvent: false}); - }) + }); } deleteKey($event: Event, index: number): void { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html index ee644c54c7..0d01a1c1d7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html @@ -44,7 +44,11 @@
{{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}}
-
+ {{ config.value | getRpcTemplateArrayView }} +
+ +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss index 27ff7843ee..bf037a7249 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss @@ -104,6 +104,10 @@ flex: 1; margin: 0; } + + .array-value { + margin-left: 10px; + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts index 466ab5fdb5..9c2e551a22 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts @@ -49,7 +49,8 @@ export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit { rpcTemplates: Array; public readonly originalOrder = (): number => 0; - public readonly isObject = (value: any) => isLiteralObject(value); + public readonly isObject = (value: unknown) => isLiteralObject(value); + public readonly isArray = (value: unknown) => Array.isArray(value); public readonly SNMPMethodsTranslations = SNMPMethodsTranslations; constructor(private attributeService: AttributeService) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html index 3e94ae96d7..fde4ab28ac 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html @@ -20,36 +20,6 @@ class="mat-subtitle-1 title">{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}
- - - {{ 'gateway.rpc.methodFilter' | translate }} - - - - {{ 'gateway.rpc.requestTopicExpression' | translate }} - - - - {{ 'gateway.rpc.withResponse' | translate }} - - - {{ 'gateway.rpc.responseTopicExpression' | translate }} - - - - {{ 'gateway.rpc.responseTimeout' | translate }} - - - - {{ 'gateway.rpc.valueExpression' | translate }} - - - {{ 'gateway.rpc.methodRPC' | translate }} @@ -407,31 +377,6 @@ - - - {{ 'gateway.rpc.method' | translate }} - - -
- {{ 'gateway.rpc.arguments' | translate }} -
- - - - delete - -
- -
-
{{ 'gateway.statistics.command' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index 989f9daeed..f77e877aea 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -51,7 +51,6 @@ import { } from '@shared/components/dialog/json-object-edit-dialog.component'; import { jsonRequired } from '@shared/components/json-object-edit.component'; import { deepClone } from '@core/utils'; -import { takeUntil, tap } from "rxjs/operators"; import { Subject } from "rxjs"; @Component({ @@ -129,7 +128,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C this.propagateChange({...this.commandForm.value, ...value}); } }); - this.observeMQTTWithResponse(); } ngOnDestroy(): void { @@ -141,16 +139,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C let formGroup: FormGroup; switch (type) { - case ConnectorType.MQTT: - formGroup = this.fb.group({ - methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], - valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - withResponse: [false, []], - }); - break; case ConnectorType.BACNET: formGroup = this.fb.group({ method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], @@ -246,12 +234,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C httpHeaders: this.fb.array([]), }) break; - case ConnectorType.OPCUA: - formGroup = this.fb.group({ - method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - arguments: this.fb.array([]), - }) - break; default: formGroup = this.fb.group({ command: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], @@ -293,18 +275,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C return (this.commandForm.get(path) as FormArray).controls as FormControl[]; } - addOCPUAArguments(value: string = null) { - const oidsFA = this.commandForm.get('arguments') as FormArray; - if (oidsFA) { - oidsFA.push(this.fb.control(value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]), {emitEvent: false}); - } - } - - removeOCPUAArguments(index: number) { - const oidsFA = this.commandForm.get('arguments') as FormArray; - oidsFA.removeAt(index); - } - openEditJSONDialog($event: Event) { if ($event) { $event.stopPropagation(); @@ -368,34 +338,8 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C }) delete value.httpHeaders; break; - case ConnectorType.OPCUA: - this.clearFromArrayByName("arguments"); - value.arguments.forEach(value => { - this.addOCPUAArguments(value) - }) - delete value.arguments; - break; } this.commandForm.patchValue(value, {onlySelf: false}); } } - - private observeMQTTWithResponse(): void { - if (this.connectorType === ConnectorType.MQTT) { - this.commandForm.get('withResponse').valueChanges.pipe( - tap((isActive: boolean) => { - const responseTopicControl = this.commandForm.get('responseTopicExpression'); - const responseTimeoutControl = this.commandForm.get('responseTimeout'); - if (isActive) { - responseTopicControl.enable(); - responseTimeoutControl.enable(); - } else { - responseTopicControl.disable(); - responseTimeoutControl.disable(); - } - }), - takeUntil(this.destroy$), - ).subscribe(); - } - } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html index 41dea0b656..41a655d93e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html @@ -42,16 +42,20 @@
- +
{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}
- + + + + +
-
+
SVG.Svg', - description: 'A root svg node. Instance of SVG.Svg.' + type: 'SVG.Svg', + description: 'A root svg node. Instance of SVG.Svg.' } }); @@ -1114,8 +1115,9 @@ export const elementStateRenderFunctionCompletions = (ctxCompletion: TbEditorCom meta: 'argument', type: 'Element', description: 'SVG element.
' + - 'See Manipulating section to manipulate the element.
' + - 'See Animating section to animate the element.' + 'See Manipulating section to manipulate the element.
' + + 'See Animating section to animate the element.' } }); @@ -1131,6 +1133,10 @@ export const clickActionFunctionCompletions = (ctxCompletion: TbEditorCompletion export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags: string[], customTranslate: CustomTranslatePipe): TbEditorCompletion => { + + const scadaSymbolAnimationLink = HelpLinks.linksMap.scadaSymbolDevAnimation; + const scadaSymbolAnimation = `ScadaSymbolAnimation`; + const properties: TbEditorCompletion = { meta: 'object', type: 'object', @@ -1191,9 +1197,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags } ], return: { - description: 'Instance of ScadaSymbolAnimation which has generally similar methods as ' + - 'SVG.Runner to control the animation.', - type: 'ScadaSymbolAnimation' + description: `Instance of ${scadaSymbolAnimation} class with API to setup and control the animation.`, + type: scadaSymbolAnimation } }, cssAnimation: { @@ -1207,9 +1212,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags } ], return: { - description: 'Instance of ScadaSymbolAnimation which has generally similar methods as ' + - 'SVG.Runner to control the animation.', - type: 'ScadaSymbolAnimation' + description: `Instance of ${scadaSymbolAnimation} class with API to setup and control the animation.`, + type: scadaSymbolAnimation } }, resetCssAnimation: { @@ -1280,8 +1284,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags text: { meta: 'function', description: 'Set text to element(s). Only applicable for elements of type ' + - 'SVG.Text or ' + - 'SVG.Tspan.', + 'SVG.Text or ' + + 'SVG.Tspan.', args: [ { name: 'element', @@ -1298,8 +1302,8 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags font: { meta: 'function', description: 'Set element(s) text font and color. Only applicable for elements of type ' + - 'SVG.Text or ' + - 'SVG.Tspan.', + 'SVG.Text or ' + + 'SVG.Tspan.', args: [ { name: 'element', @@ -1321,7 +1325,7 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags icon: { meta: 'function', description: 'Draws icon inside element(s). Only applicable for elements of type ' + - 'SVG.G.', + 'SVG.G.', args: [ { name: 'element', @@ -1429,8 +1433,9 @@ export const scadaSymbolContextCompletion = (metadata: ScadaSymbolMetadata, tags tags: tagsCompletions, svg: { meta: 'argument', - type: 'SVG.Svg', - description: 'A root svg node. Instance of SVG.Svg.' + type: 'SVG.Svg', + description: 'A root svg node. Instance of SVG.Svg.' } } }; diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts index e4d2af97af..cb4a3805f3 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.models.ts @@ -21,7 +21,7 @@ import { serviceCompletions } from '@shared/models/ace/service-completion.models const widgetEditorCompletions: TbEditorCompletions = { ... {self: { description: 'Built-in variable self that is a reference to the widget instance', - type: 'WidgetTypeInstance', + type: 'WidgetTypeInstance', meta: 'object', children: { ...{ @@ -31,19 +31,19 @@ const widgetEditorCompletions: TbEditorCompletions = { }, onDataUpdated: { description: 'Called when the new data is available from the widget subscription.
Latest data can be accessed from ' + - 'the defaultSubscription property of widget context (ctx).', + 'the defaultSubscription property of widget context (ctx).', meta: 'function' }, onResize: { - description: 'Called when widget container is resized. Latest width and height can be obtained from widget context (ctx).', + description: 'Called when widget container is resized. Latest width and height can be obtained from widget context (ctx).', meta: 'function' }, onEditModeChanged: { - description: 'Called when dashboard editing mode is changed. Latest mode is handled by isEdit property of widget context (ctx).', + description: 'Called when dashboard editing mode is changed. Latest mode is handled by isEdit property of widget context (ctx).', meta: 'function' }, onMobileModeChanged: { - description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by isMobile property of widget context (ctx).', + description: 'Called when dashboard view width crosses mobile breakpoint. Latest state is handled by isMobile property of widget context (ctx).', meta: 'function' }, onDestroy: { @@ -51,7 +51,7 @@ const widgetEditorCompletions: TbEditorCompletions = { meta: 'function' }, getSettingsSchema: { - description: 'Optional function returning widget settings schema json as alternative to Settings tab of Settings schema section.', + description: 'Optional function returning widget settings schema json as alternative to Settings tab of Settings schema section.', meta: 'function', return: { description: 'An widget settings schema json', @@ -59,7 +59,7 @@ const widgetEditorCompletions: TbEditorCompletions = { } }, getDataKeySettingsSchema: { - description: 'Optional function returning particular data key settings schema json as alternative to Data key settings schema of Settings schema section.', + description: 'Optional function returning particular data key settings schema json as alternative to Data key settings schema of Settings schema section.', meta: 'function', return: { description: 'A particular data key settings schema json', @@ -71,7 +71,7 @@ const widgetEditorCompletions: TbEditorCompletions = { meta: 'function', return: { description: 'An object describing widget datasource parameters.', - type: 'WidgetTypeParameters' + type: 'WidgetTypeParameters' } }, actionSources: { @@ -79,7 +79,7 @@ const widgetEditorCompletions: TbEditorCompletions = { meta: 'function', return: { description: 'A map of action sources by action source id.', - type: '{[actionSourceId: string]: WidgetActionSource}' + type: '{[actionSourceId: string]: WidgetActionSource}' } } }, diff --git a/ui-ngx/src/app/shared/directives/context-menu.directive.ts b/ui-ngx/src/app/shared/directives/context-menu.directive.ts new file mode 100644 index 0000000000..ada727cfe2 --- /dev/null +++ b/ui-ngx/src/app/shared/directives/context-menu.directive.ts @@ -0,0 +1,35 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Directive, ElementRef, EventEmitter, OnDestroy, Output } from '@angular/core'; +import { TbContextMenuEvent } from '@shared/models/jquery-event.models'; + +@Directive({ + selector: '[tbcontextmenu]' +}) +export class ContextMenuDirective implements OnDestroy { + + @Output() + tbcontextmenu = new EventEmitter(); + + constructor(private el: ElementRef) { + $(this.el.nativeElement).on('tbcontextmenu', (e: TbContextMenuEvent) => this.tbcontextmenu.emit(e)); + } + + ngOnDestroy() { + $(this.el.nativeElement).off('tbcontextmenu'); + } +} diff --git a/ui-ngx/src/app/shared/directives/public-api.ts b/ui-ngx/src/app/shared/directives/public-api.ts index 6f25320d90..7e0902e223 100644 --- a/ui-ngx/src/app/shared/directives/public-api.ts +++ b/ui-ngx/src/app/shared/directives/public-api.ts @@ -16,3 +16,4 @@ export * from './truncate-with-tooltip.directive'; export * from './ellipsis-chip-list.directive'; +export * from './context-menu.directive'; diff --git a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts index dd7fb74dc8..5efc428194 100644 --- a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts +++ b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts @@ -16,101 +16,101 @@ import { FunctionArg, FunctionArgType, TbEditorCompletions } from '@shared/models/ace/completion.models'; -export const entityIdHref = 'EntityId'; +export const entityIdHref = 'EntityId'; -export const baseDataHref = 'Base data'; +export const baseDataHref = 'Base data'; -export const alarmDataHref = 'Alarm data'; +export const alarmDataHref = 'Alarm data'; -export const alarmDataQueryHref = 'Alarm data query'; +export const alarmDataQueryHref = 'Alarm data query'; -export const attributeScopeHref = 'Attribute scope'; +export const attributeScopeHref = 'Attribute scope'; -export const entityTypeHref = 'EntityType'; +export const entityTypeHref = 'EntityType'; -export const pageDataHref = 'PageData'; +export const pageDataHref = 'PageData'; -export const deviceInfoHref = 'DeviceInfo'; +export const deviceInfoHref = 'DeviceInfo'; -export const assetInfoHref = 'AssetInfo'; +export const assetInfoHref = 'AssetInfo'; -export const entityViewInfoHref = 'EntityViewInfo'; +export const entityViewInfoHref = 'EntityViewInfo'; -export const entityRelationsQueryHref = 'EntityRelationsQuery'; +export const entityRelationsQueryHref = 'EntityRelationsQuery'; -export const entityRelationInfoHref = 'EntityRelationInfo'; +export const entityRelationInfoHref = 'EntityRelationInfo'; -export const dashboardInfoHref = 'DashboardInfo'; +export const dashboardInfoHref = 'DashboardInfo'; -export const deviceHref = 'Device'; +export const deviceHref = 'Device'; -export const assetHref = 'Asset'; +export const assetHref = 'Asset'; -export const entityViewHref = 'entityView'; +export const entityViewHref = 'entityView'; -export const entityRelationHref = 'Entity relation'; +export const entityRelationHref = 'Entity relation'; -export const dashboardHref = 'Dashboard'; +export const dashboardHref = 'Dashboard'; -export const customerHref = 'Customer'; +export const customerHref = 'Customer'; -export const attributeDataHref = 'Attribute Data'; +export const attributeDataHref = 'Attribute Data'; -export const timeseriesDataHref = 'Timeseries Data'; +export const timeseriesDataHref = 'Timeseries Data'; -export const aggregationTypeHref = 'Aggregation Type'; +export const aggregationTypeHref = 'Aggregation Type'; -export const dataSortOrderHref = 'Data Sort Order'; +export const dataSortOrderHref = 'Data Sort Order'; -export const userHref = 'User'; +export const userHref = 'User'; -export const entityDataHref = 'Entity data'; +export const entityDataHref = 'Entity data'; -export const entityDataQueryHref = 'Entity Data Query'; +export const entityDataQueryHref = 'Entity Data Query'; -export const deviceCredentialsHref = 'DeviceCredentials'; +export const deviceCredentialsHref = 'DeviceCredentials'; -export const entityFilterHref = 'Entity filter'; +export const entityFilterHref = 'Entity filter'; -export const entityInfoHref = 'Entity info'; +export const entityInfoHref = 'Entity info'; -export const aliasEntityTypeHref = 'Alias Entity Type'; +export const aliasEntityTypeHref = 'Alias Entity Type'; -export const aliasFilterTypeHref = 'Alias filter type'; +export const aliasFilterTypeHref = 'Alias filter type'; -export const entityAliasHref = 'Entity alias'; +export const entityAliasHref = 'Entity alias'; -export const dataKeyTypeHref = 'Data key type'; +export const dataKeyTypeHref = 'Data key type'; -export const subscriptionInfoHref = 'Subscription info'; +export const subscriptionInfoHref = 'Subscription info'; -export const dataSourceHref = 'Datasource'; +export const dataSourceHref = 'Datasource'; -export const stateParamsHref = 'State params'; +export const stateParamsHref = 'State params'; -export const aliasInfoHref = 'Alias info'; +export const aliasInfoHref = 'Alias info'; -export const entityAliasFilterHref = 'Entity alias filter'; +export const entityAliasFilterHref = 'Entity alias filter'; -export const entityAliasFilterResultHref = 'Entity alias filter result'; +export const entityAliasFilterResultHref = 'Entity alias filter result'; -export const importEntityDataHref = 'Import entity data'; +export const importEntityDataHref = 'Import entity data'; -export const importEntitiesResultInfoHref = 'Import entities result info'; +export const importEntitiesResultInfoHref = 'Import entities result info'; -export const customDialogComponentHref = 'CustomDialogComponent'; +export const customDialogComponentHref = 'CustomDialogComponent'; -export const resourceInfoHref = 'Resource info'; +export const resourceInfoHref = 'Resource info'; export const pageLinkArg: FunctionArg = { name: 'pageLink', - type: 'PageLink', + type: 'PageLink', description: 'Page link object used to perform paginated request.' }; export const requestConfigArg: FunctionArg = { name: 'config', - type: 'RequestConfig', + type: 'RequestConfig', description: 'HTTP request configuration.', optional: true }; @@ -167,9 +167,9 @@ export function observablePageDataReturnType(objectType: string): FunctionArgTyp export const serviceCompletions: TbEditorCompletions = { deviceService: { description: 'Device Service API
' + - 'See DeviceService for API reference.', + 'See DeviceService for API reference.', meta: 'service', - type: 'DeviceService', + type: 'DeviceService', children: { getTenantDeviceInfos: { description: 'Get tenant devices', @@ -243,7 +243,7 @@ export const serviceCompletions: TbEditorCompletions = { args: [ requestConfigArg ], - return: observableArrayReturnType('EntitySubtype') + return: observableArrayReturnType('EntitySubtype') }, getDeviceCredentials: { description: 'Get device credentials by device id', @@ -322,7 +322,7 @@ export const serviceCompletions: TbEditorCompletions = { description: 'Find devices by search query', meta: 'function', args: [ - { name: 'query', type: 'DeviceSearchQuery', + { name: 'query', type: 'DeviceSearchQuery', description: 'Device search query object'}, requestConfigArg ], @@ -346,7 +346,7 @@ export const serviceCompletions: TbEditorCompletions = { description: 'Claiming device name'}, requestConfigArg ], - return: observableReturnType('ClaimResult') + return: observableReturnType('ClaimResult') }, unclaimDevice: { description: 'Send un-claim device request', @@ -362,9 +362,9 @@ export const serviceCompletions: TbEditorCompletions = { }, assetService: { description: 'Asset Service API
' + - 'See AssetService for API reference.', + 'See AssetService for API reference.', meta: 'service', - type: 'AssetService', + type: 'AssetService', children: { getTenantAssetInfos: { description: 'Get tenant assets', @@ -438,7 +438,7 @@ export const serviceCompletions: TbEditorCompletions = { args: [ requestConfigArg ], - return: observableArrayReturnType('EntitySubtype') + return: observableArrayReturnType('EntitySubtype') }, makeAssetPublic: { description: 'Make asset public (available from public dashboard)', @@ -474,7 +474,7 @@ export const serviceCompletions: TbEditorCompletions = { args: [ { name: 'query', - type: 'AssetSearchQuery', + type: 'AssetSearchQuery', description: 'Asset search query object' }, requestConfigArg @@ -497,9 +497,9 @@ export const serviceCompletions: TbEditorCompletions = { }, entityViewService: { description: 'EntityView Service API
' + - 'See EntityViewService for API reference.', + 'See EntityViewService for API reference.', meta: 'service', - type: 'EntityViewService', + type: 'EntityViewService', children: { getTenantEntityViewInfos: { description: 'Get tenant entity view infos', @@ -564,7 +564,7 @@ export const serviceCompletions: TbEditorCompletions = { args: [ requestConfigArg ], - return: observableArrayReturnType('EntitySubtype') + return: observableArrayReturnType('EntitySubtype') }, makeEntityViewPublic: { description: 'Make entity view public (available from public dashboard)', @@ -600,7 +600,7 @@ export const serviceCompletions: TbEditorCompletions = { args: [ { name: 'query', - type: 'AssetSearchQuery', + type: 'AssetSearchQuery', description: 'Entity view search query object' }, requestConfigArg @@ -611,9 +611,9 @@ export const serviceCompletions: TbEditorCompletions = { }, customerService: { description: 'Customer Service API
' + - 'See CustomerService for API reference.', + 'See CustomerService for API reference.', meta: 'service', - type: 'CustomerService', + type: 'CustomerService', children: { getCustomer: { description: 'Get customer by id', @@ -655,9 +655,9 @@ export const serviceCompletions: TbEditorCompletions = { }, dashboardService: { description: 'Dashboard Service API
' + - 'See DashboardService for API reference.', + 'See DashboardService for API reference.', meta: 'service', - type: 'DashboardService', + type: 'DashboardService', children: { getTenantDashboards: { description: 'Get tenant dashboards', @@ -814,9 +814,9 @@ export const serviceCompletions: TbEditorCompletions = { }, userService: { description: 'User Service API
' + - 'See UserService for API reference.', + 'See UserService for API reference.', meta: 'service', - type: 'UserService', + type: 'UserService', children: { getUsers: { description: 'Get users', @@ -907,9 +907,9 @@ export const serviceCompletions: TbEditorCompletions = { }, entityRelationService: { description: 'Entity Relation Service API
' + - 'See EntityRelationService for API reference.', + 'See EntityRelationService for API reference.', meta: 'service', - type: 'EntityRelationService', + type: 'EntityRelationService', children: { saveRelation: { description: 'Save relation', @@ -1029,9 +1029,9 @@ export const serviceCompletions: TbEditorCompletions = { }, attributeService: { description: 'Attribute Service API
' + - 'See AttributeService for API reference.', + 'See AttributeService for API reference.', meta: 'service', - type: 'AttributeService', + type: 'AttributeService', children: { getEntityAttributes: { description: 'Get entity attributes by id', @@ -1109,9 +1109,9 @@ export const serviceCompletions: TbEditorCompletions = { }, entityService: { description: 'Entity Service API
' + - 'See EntityService for API reference.', + 'See EntityService for API reference.', meta: 'service', - type: 'EntityService', + type: 'EntityService', children: { getEntity: { description: 'Get entity by id', @@ -1304,9 +1304,9 @@ export const serviceCompletions: TbEditorCompletions = { }, resourceService: { description: 'Resource Service API
' + - 'See ResourceService for API reference.', + 'See ResourceService for API reference.', meta: 'service', - type: 'ResourceService', + type: 'ResourceService', children: { getResources: { description: 'Find resources by search text', @@ -1321,9 +1321,9 @@ export const serviceCompletions: TbEditorCompletions = { }, dialogs: { description: 'Dialogs Service API
' + - 'See DialogService for API reference.', + 'See DialogService for API reference.', meta: 'service', - type: 'DialogService', + type: 'DialogService', children: { confirm: { description: 'Confirm', @@ -1382,9 +1382,9 @@ export const serviceCompletions: TbEditorCompletions = { }, customDialog: { description: 'Custom Dialog Service API
' + - 'See CustomDialogService for API reference.', + 'See CustomDialogService for API reference.', meta: 'service', - type: 'CustomDialogService', + type: 'CustomDialogService', children: { customDialog: { description: 'Custom Dialog', @@ -1400,32 +1400,32 @@ export const serviceCompletions: TbEditorCompletions = { }, date: { description: 'Date Pipe
Formats a date value according to locale rules.
' + - 'See DatePipe for API reference.', + 'See DatePipe for API reference.', meta: 'service', - type: 'DatePipe' + type: 'DatePipe' }, translate: { description: 'Translate Service API
' + - 'See TranslateService for API reference.', + 'See TranslateService for API reference.', meta: 'service', - type: 'TranslateService' + type: 'TranslateService' }, http: { description: 'HTTP Client Service
' + - 'See HttpClient for API reference.', + 'See HttpClient for API reference.', meta: 'service', - type: 'HttpClient' + type: 'HttpClient' }, sanitizer: { description: 'DomSanitizer Service
' + - 'See DomSanitizer for API reference.', + 'See DomSanitizer for API reference.', meta: 'service', - type: 'DomSanitizer' + type: 'DomSanitizer' }, router: { description: 'Router Service
' + - 'See Router for API reference.', + 'See Router for API reference.', meta: 'service', - type: 'Router' + type: 'Router' } }; diff --git a/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts b/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts index e54e52bdbc..b2b0b6235d 100644 --- a/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts +++ b/ui-ngx/src/app/shared/models/ace/widget-completion.models.ts @@ -20,7 +20,7 @@ import { entityIdHref, serviceCompletions } from '@shared/models/ace/service-com export const timewindowCompletion: TbEditorCompletion = { description: 'Timewindow configuration object', meta: 'property', - type: 'Timewindow', + type: 'Timewindow', children: { displayValue: { description: 'Current timewindow display value.', @@ -50,7 +50,7 @@ export const timewindowCompletion: TbEditorCompletion = { realtime: { description: 'Realtime timewindow configuration object.', meta: 'property', - type: 'IntervalWindow', + type: 'IntervalWindow', children: { interval: { description: 'Timewindow aggregation interval in milliseconds', @@ -67,7 +67,7 @@ export const timewindowCompletion: TbEditorCompletion = { history: { description: 'History timewindow configuration object.', meta: 'property', - type: 'HistoryWindow', + type: 'HistoryWindow', children: { historyType: { description: 'History timewindow type (0 - last interval, 1 - fixed)', @@ -87,7 +87,7 @@ export const timewindowCompletion: TbEditorCompletion = { fixedTimewindow: { description: 'Fixed history timewindow configuration object', meta: 'property', - type: 'FixedWindow', + type: 'FixedWindow', children: { startTimeMs: { description: 'Timewindow start time in UTC milliseconds', @@ -106,7 +106,7 @@ export const timewindowCompletion: TbEditorCompletion = { aggregation: { description: 'Timewindow aggregation configuration object.', meta: 'property', - type: 'Aggregation', + type: 'Aggregation', children: { interval: { description: 'Aggregation interval in milliseconds', @@ -116,7 +116,7 @@ export const timewindowCompletion: TbEditorCompletion = { type: { description: 'Aggregation type', meta: 'property', - type: 'AggregationType' + type: 'AggregationType' }, limit: { description: 'Maximum allowed datapoints when aggregation is disabled (AggregationType == \'NONE\')', @@ -132,7 +132,7 @@ export const widgetContextCompletions: TbEditorCompletions = { ctx: { description: 'A reference to widget context that has all necessary API
and data used by widget instance.', meta: 'object', - type: 'WidgetContext', + type: 'WidgetContext', children: { ...{ $container: { @@ -143,7 +143,7 @@ export const widgetContextCompletions: TbEditorCompletions = { $scope: { description: 'Reference to the current widget component.
Can be used to access/modify component properties when widget is built using Angular approach.', meta: 'property', - type: 'IDynamicWidgetComponent' + type: 'IDynamicWidgetComponent' }, width: { description: 'Current width of widget container in pixels.', @@ -168,7 +168,7 @@ export const widgetContextCompletions: TbEditorCompletions = { widgetConfig: { description: 'Common widget configuration containing properties such as color (text color), backgroundColor (widget background color), etc.', meta: 'property', - type: 'WidgetConfig', + type: 'WidgetConfig', children: { title: { description: 'Widget title.', @@ -233,17 +233,17 @@ export const widgetContextCompletions: TbEditorCompletions = { legendConfig: { description: 'Legend configuration.', meta: 'property', - type: 'LegendConfig', + type: 'LegendConfig', children: { position: { description: 'Legend position. Possible values: \'top\', \'bottom\', \'left\', \'right\'', meta: 'property', - type: 'LegendPosition', + type: 'LegendPosition', }, direction: { description: 'Legend direction. Possible values: \'column\', \'row\'', meta: 'property', - type: 'LegendDirection', + type: 'LegendDirection', }, showMin: { description: 'Whether to display aggregated min values.', @@ -331,12 +331,12 @@ export const widgetContextCompletions: TbEditorCompletions = { alarmSource: { description: 'Configured alarm source for alarm widget type.', meta: 'property', - type: 'Datasource' + type: 'Datasource' }, alarmSearchStatus: { description: 'Configured default alarm search status for alarm widget type.', meta: 'property', - type: 'AlarmSearchStatus' + type: 'AlarmSearchStatus' }, alarmsPollingInterval: { description: 'Configured alarms polling interval for alarm widget type.', @@ -356,29 +356,29 @@ export const widgetContextCompletions: TbEditorCompletions = { datasources: { description: 'Array of configured widget datasources.', meta: 'property', - type: 'Array<Datasource>' + type: 'Array<Datasource>' } } }, settings: { - description: 'Widget settings containing widget specific properties according to the defined settings json schema', + description: 'Widget settings containing widget specific properties according to the defined settings json schema', meta: 'property', type: 'object' }, datasources: { description: 'Array of resolved widget datasources.', meta: 'property', - type: 'Array<Datasource>' + type: 'Array<Datasource>' }, data: { description: 'Array of latest datasources data.', meta: 'property', - type: 'Array<DatasourceData>' + type: 'Array<DatasourceData>' }, timeWindow: { description: 'Current widget timewindow (applicable for timeseries widgets).', meta: 'property', - type: 'WidgetTimewindow' + type: 'WidgetTimewindow' }, units: { description: 'Optional property defining units text of values displayed by widget. Useful for simple widgets like cards or gauges.', @@ -393,7 +393,7 @@ export const widgetContextCompletions: TbEditorCompletions = { currentUser: { description: 'Current user object.', meta: 'property', - type: 'AuthUser', + type: 'AuthUser', children: { sub: { description: 'User subject (email).', @@ -443,7 +443,7 @@ export const widgetContextCompletions: TbEditorCompletions = { authority: { description: 'User authority. Possible values: SYS_ADMIN, TENANT_ADMIN, CUSTOMER_USER', meta: 'property', - type: 'Authority' + type: 'Authority' } } }, @@ -468,12 +468,12 @@ export const widgetContextCompletions: TbEditorCompletions = { defaultSubscription: { description: 'Default widget subscription object contains all subscription information,
including current data, according to the widget type.', meta: 'property', - type: 'IWidgetSubscription' + type: 'IWidgetSubscription' }, timewindowFunctions: { description: 'Object with timewindow functions used to manage widget data time frame. Can by used by Time-series or Alarm widgets.', meta: 'property', - type: 'TimewindowFunctions', + type: 'TimewindowFunctions', children: { onUpdateTimewindow: { description: 'This function can be used to update current subscription time frame
to historical one identified by startTimeMs and endTimeMs arguments.', @@ -500,7 +500,7 @@ export const widgetContextCompletions: TbEditorCompletions = { controlApi: { description: 'Object that provides API functions for RPC (Control) widgets.', meta: 'property', - type: 'RpcApi', + type: 'RpcApi', children: { sendOneWayCommand: { description: 'Sends one way (without response) RPC command to the device.', @@ -585,7 +585,7 @@ export const widgetContextCompletions: TbEditorCompletions = { actionsApi: { description: 'Set of API functions to work with user defined actions.', meta: 'property', - type: 'WidgetActionsApi', + type: 'WidgetActionsApi', children: { getActionDescriptors: { description: 'Get list of action descriptors for provided actionSourceId.', @@ -599,7 +599,7 @@ export const widgetContextCompletions: TbEditorCompletions = { ], return: { description: 'The list of action descriptors', - type: 'Array<WidgetActionDescriptor>' + type: 'Array<WidgetActionDescriptor>' } }, handleWidgetAction: { @@ -614,7 +614,7 @@ export const widgetContextCompletions: TbEditorCompletions = { { name: 'descriptor', description: 'An action descriptor.', - type: 'WidgetActionDescriptor' + type: 'WidgetActionDescriptor' }, { name: 'entityId', @@ -635,7 +635,7 @@ export const widgetContextCompletions: TbEditorCompletions = { stateController: { description: 'Reference to Dashboard state controller, providing API to manage current dashboard state.', meta: 'property', - type: 'IStateController', + type: 'IStateController', children: { openState: { description: 'Navigate to new dashboard state.', @@ -649,7 +649,7 @@ export const widgetContextCompletions: TbEditorCompletions = { { name: 'params', description: 'An object with state parameters to use by the new state.', - type: 'StateParams', + type: 'StateParams', optional: true }, { @@ -667,7 +667,7 @@ export const widgetContextCompletions: TbEditorCompletions = { { name: 'id', description: 'An array state object of the target dashboard state.', - type: 'Array StateObject', + type: 'Array StateObject', }, { name: 'openRightLayout', @@ -690,7 +690,7 @@ export const widgetContextCompletions: TbEditorCompletions = { { name: 'params', description: 'An object with state parameters to update current state parameters.', - type: 'StateParams', + type: 'StateParams', optional: true }, { @@ -714,7 +714,7 @@ export const widgetContextCompletions: TbEditorCompletions = { meta: 'function', return: { description: 'current dashboard state parameters.', - type: 'StateParams' + type: 'StateParams' } }, getStateParamsByStateId: { @@ -729,7 +729,7 @@ export const widgetContextCompletions: TbEditorCompletions = { ], return: { description: 'current dashboard state parameters.', - type: 'StateParams' + type: 'StateParams' } } } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 36600eec72..1a681972bf 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -187,6 +187,7 @@ export const HelpLinks = { gatewayInstall: `${helpBaseUrl}/docs/iot-gateway/install/docker-installation`, scada: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada`, scadaSymbolDev: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/symbols-dev-guide`, + scadaSymbolDevAnimation: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/scada/scada-symbols-dev-guide/#scadasymbolanimation` } }; /* eslint-enable max-len */ diff --git a/ui-ngx/src/app/shared/models/jquery-event.models.ts b/ui-ngx/src/app/shared/models/jquery-event.models.ts new file mode 100644 index 0000000000..5657beb60b --- /dev/null +++ b/ui-ngx/src/app/shared/models/jquery-event.models.ts @@ -0,0 +1,80 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import Timeout = NodeJS.Timeout; + +export interface TbContextMenuEvent extends Event { + clientX: number; + clientY: number; + ctrlKey: boolean; + metaKey: boolean; +} + +const isIOSDevice = (): boolean => + /iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.userAgent.includes('Mac') && 'ontouchend' in document); + +export const initCustomJQueryEvents = () => { + $.event.special.tbcontextmenu = { + setup(this: HTMLElement) { + const el = $(this); + if (isIOSDevice()) { + let timeoutId: Timeout; + + el.on('touchstart', (e) => { + e.stopPropagation(); + timeoutId = setTimeout(() => { + timeoutId = null; + e.stopPropagation(); + const touch = e.originalEvent.changedTouches[0]; + const event = $.Event('tbcontextmenu', { + clientX: touch.clientX, + clientY: touch.clientY, + ctrlKey: false, + metaKey: false + }); + el.trigger(event, e); + }, 500); + }); + + el.on('touchend touchmove', () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }); + } else { + el.on('contextmenu', (e) => { + e.preventDefault(); + e.stopPropagation(); + const event = $.Event('tbcontextmenu', { + clientX: e.originalEvent.clientX, + clientY: e.originalEvent.clientY, + ctrlKey: e.originalEvent.ctrlKey, + metaKey: e.originalEvent.metaKey, + }); + el.trigger(event, e); + }); + } + }, + teardown(this: HTMLElement) { + const el = $(this); + if (isIOSDevice()) { + el.off('touchstart touchend touchmove'); + } else { + el.off('contextmenu'); + } + } + }; +}; diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 130e272c21..309a3e2bf3 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -68,6 +68,7 @@ import { NgxHmCarouselModule } from 'ngx-hm-carousel'; import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular'; import { UserMenuComponent } from '@shared/components/user-menu.component'; import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive'; +import { ContextMenuDirective } from '@shared/directives/context-menu.directive'; import { NospacePipe } from '@shared/pipe/nospace.pipe'; import { TranslateModule } from '@ngx-translate/core'; import { TbCheckboxComponent } from '@shared/components/tb-checkbox.component'; @@ -371,6 +372,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) LedLightComponent, MarkdownEditorComponent, TruncateWithTooltipDirective, + ContextMenuDirective, NospacePipe, MillisecondsToTimeStringPipe, EnumToArrayPipe, @@ -633,6 +635,7 @@ export function MarkedOptionsFactory(markedOptionsService: MarkedOptionsService) LedLightComponent, MarkdownEditorComponent, TruncateWithTooltipDirective, + ContextMenuDirective, NospacePipe, MillisecondsToTimeStringPipe, EnumToArrayPipe, diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json index e3277ac88a..59eb61a935 100644 --- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -1208,7 +1208,7 @@ "dropShadow": false, "enableFullscreen": false, "widgetStyle": {}, - "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-height: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}", + "widgetCss": " .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding: 0;\n}\n\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 8px;\n font-weight: 600;\n font-size: 20px;\n line-height: 24px;\n letter-spacing: 0.1px;\n color: rgba(0, 0, 0, 0.76);\n}\n\n.tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n padding: 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content .tb-widget-title > .title-row > .title {\n padding-bottom: 0;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1182px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel {\n gap: 0;\n }\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n min-width: 150px;\n }\n}\n\n@media screen and (max-width: 960px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 120px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 1819px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode {\n max-width: 125px;\n }\n \n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-badges {\n max-width: 160px;\n }\n}\n\n@media screen and (min-width: 960px) and (max-height: 965px) {\n .tb-widget-container > .tb-widget > .tb-widget-content > .tb-widget .tb-mobile-app-qrcode-panel .tb-qrcode-label {\n display: none;\n }\n}", "showTitleIcon": false, "titleTooltip": "", "titleStyle": null, diff --git a/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md b/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md index bdb0d45234..9a2c811817 100644 --- a/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md +++ b/ui-ngx/src/assets/help/en_US/scada/symbol_state_render_fn.md @@ -10,9 +10,9 @@ A JavaScript function used to render SCADA symbol state. **Parameters:**
    -
  • ctx: ScadaSymbolContext - Context of the SCADA symbol. +
  • ctx: ScadaSymbolContext - Context of the SCADA symbol.
  • -
  • svg: SVG.Svg - A root svg node. Instance of SVG.Svg. +
  • svg: SVG.Svg - A root svg node. Instance of SVG.Svg.
@@ -20,8 +20,44 @@ A JavaScript function used to render SCADA symbol state. ##### Examples -
- -TODO - - +This JavaScript snippet manages the enablement and appearance of SVG buttons used to control volume settings based on device connectivity and value thresholds. +The 'Up' button is disabled when the volume reaches its maximum allowable value (`maxValue`), and the 'Down' button is disabled when the volume is at its minimum allowable value (`minValue`). +Both buttons are disabled when the device is offline. The visual cues for enabled/disabled states are represented by color changes. + +```javascript + +var levelUpButton = ctx.tags.levelUpButton; // Button for increasing volume +var levelDownButton = ctx.tags.levelDownButton; // Button for decreasing volume +var levelArrowUp = ctx.tags.levelArrowUp; // Visual arrow for 'Up' button +var levelArrowDown = ctx.tags.levelArrowDown; // Visual arrow for 'Down' button + +var active = ctx.values.active; // Device connectivity status +var value = ctx.values.value; // Current volume level +var minValue = ctx.properties.minValue; // Minimum volume level +var maxValue = ctx.properties.maxValue; // Maximum volume level + +var enabledColor = '#4CAF50'; // Color for enabled state +var disabledColor = '#A1ADB1'; // Color for disabled state + +// Determine if the 'Up' button should be enabled +var levelUpEnabled = active && value < maxValue; +if (levelUpEnabled) { + ctx.api.enable(levelUpButton); // Enable 'Up' button + levelArrowUp[0].attr({fill: enabledColor}); // Set arrow color to indicate enabled state +} else { + ctx.api.disable(levelUpButton); // Disable 'Up' button + levelArrowUp[0].attr({fill: disabledColor}); // Set arrow color to indicate disabled state +} + +// Determine if the 'Down' button should be enabled +var levelDownEnabled = active && value > minValue; +if (levelDownEnabled) { + ctx.api.enable(levelDownButton); // Enable 'Down' button + levelArrowDown[0].attr({fill: enabledColor}); // Set arrow color to indicate enabled state +} else { + ctx.api.disable(levelDownButton); // Disable 'Down' button + levelArrowDown[0].attr({fill: disabledColor}); // Set arrow color to indicate disabled state +} + +{:copy-code} +``` diff --git a/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md b/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md index 26ec4f9bb3..8675419803 100644 --- a/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md +++ b/ui-ngx/src/assets/help/en_US/scada/tag_click_action_fn.md @@ -10,11 +10,10 @@ A JavaScript function invoked when user clicks on SVG element with specific tag. **Parameters:**
    -
  • ctx: ScadaSymbolContext - Context of the SCADA symbol. +
  • ctx: ScadaSymbolContext - Context of the SCADA symbol.
  • -
  • element: Element - SVG element.
    - See Manipulating section to manipulate the element.
    - See Animating section to animate the element. +
  • element: Element - SVG.js element.
    + See the examples below to learn how to manipulate and animate elements.
  • event: Event - DOM event.
  • @@ -24,6 +23,28 @@ A JavaScript function invoked when user clicks on SVG element with specific tag. ##### Examples -
    +The example demonstrates how to dynamically call the 'turnOn' or 'turnOff' actions based on the 'active' status from the context. The actions are implemented using the following methods from the Scada Symbol API: -TODO +- **callAction**: *(event: Event, behaviorId: string, value?: any, observer?: Partial\\>): void* - Triggers a specific behavior action identified by its ID, allowing for the optional passing of values and observer callbacks. + +- **setValue**: *(valueId: string, value: any): void* - Updates a specific value within the `ctx.values` object and initiates all related rendering functions. + +For more detailed guidelines on device interaction, consider reviewing the best practices. + +```javascript +var active = ctx.values.active; // Current active status from context +var action = active ? 'turnOn' : 'turnOff'; // Determine action based on active status +var parameter = "any object or primitive"; // Parameter to pass with the action + +// Call the action with observer callbacks for next and error handling +ctx.api.callAction(event, action, parameter, { + next: () => { + // Action succeeded; toggle the 'activate' status for debugging + ctx.api.setValue('activate', !active); + }, + error: () => { + // Action failed; reset the 'activate' status for debugging + ctx.api.setValue('activate', active); + } +}); +``` diff --git a/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md b/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md index caed8060d4..c16ad96c58 100644 --- a/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md +++ b/ui-ngx/src/assets/help/en_US/scada/tag_state_render_fn.md @@ -10,11 +10,10 @@ A JavaScript function used to render SCADA symbol element with specific tag. **Parameters:**
      -
    • ctx: ScadaSymbolContext - Context of the SCADA symbol. +
    • ctx: ScadaSymbolContext - Context of the SCADA symbol.
    • -
    • element: Element - SVG element.
      - See Manipulating section to manipulate the element.
      - See Animating section to animate the element. +
    • element: Element - SVG.js element.
      + See the examples below to learn how to manipulate and animate elements.
    @@ -22,6 +21,46 @@ A JavaScript function used to render SCADA symbol element with specific tag. ##### Examples -
    +* Change the background of the element based on the value of the “active” -TODO +```javascript +if(ctx.values.active){ + element.attr({fill: ctx.properties.activeColor}); +} else { + element.attr({fill: ctx.properties.inactiveColor}); +} +{:copy-code} +``` + +* Enable and disable the “On” button based on the state of the "active" (avoid or prevent click action) + +```javascript +if (ctx.values.active) { + ctx.api.disable(element); +} else { + ctx.api.enable(element); +} +{:copy-code} +``` + +* Smooth infinite rotation animation based on the value of the “active” with speed based on the value of the “speed” + +```javascript +var on = ctx.values.active; +var speed = ctx.values.speed ? ctx.values.speed / 60 : 1; +var animation = ctx.api.cssAnimation(element); + +if (on) { + if (!animation) { + animation = ctx.api.cssAnimate(element, 2000) + .rotate(360).loop().speed(speed); + } else { + animation.speed(speed).play(); + } +} else { + if (animation) { + animation.pause(); + } +} +{:copy-code} +``` diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b66457cc3d..23a60fec14 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3143,10 +3143,11 @@ "title": "{{type}} Connector RPC parameters", "templates-title": "Connector RPC Templates", "methodFilter": "Method filter", + "method-name": "Method name", "requestTopicExpression": "Request topic expression", "responseTopicExpression": "Response topic expression", - "responseTimeout": "Response Time", - "valueExpression": "Value Expression", + "responseTimeout": "Response timeout", + "valueExpression": "Value expression", "tag": "Tag", "type": "Type", "functionCode": "Function Code", @@ -3185,6 +3186,11 @@ "tries": "Tries", "httpHeaders": "HTTP Headers", "header-name": "Header name", + "hint": { + "modbus-response-reading": "RPC response will return all subtracted values from all connected devices when the reading functions are selected.", + "modbus-writing-functions": "RPC will write a filled value to all connected devices when the writing functions are selected.", + "opc-method": "A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)." + }, "security-name": "Security name", "value": "Value", "security": "Security", diff --git a/ui-ngx/src/typings/jquery.typings.d.ts b/ui-ngx/src/typings/jquery.typings.d.ts index d457729ace..a058373022 100644 --- a/ui-ngx/src/typings/jquery.typings.d.ts +++ b/ui-ngx/src/typings/jquery.typings.d.ts @@ -14,6 +14,9 @@ /// limitations under the License. /// +import { TbContextMenuEvent } from '@shared/models/jquery-event.models'; + interface JQuery { terminal(options?: any): any; + on(events: 'tbcontextmenu', handler: (e: TbContextMenuEvent) => void): this; } diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 51bfe8f56e..cc6762eb57 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -3052,10 +3052,10 @@ dependencies: "@types/jasmine" "*" -"@types/jquery@*", "@types/jquery@^3.5.16", "@types/jquery@^3.5.29": - version "3.5.29" - resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.29.tgz#3c06a1f519cd5fc3a7a108971436c00685b5dcea" - integrity sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg== +"@types/jquery@*", "@types/jquery@^3.5.29", "@types/jquery@^3.5.30": + version "3.5.30" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.30.tgz#888d584cbf844d3df56834b69925085038fd80f7" + integrity sha512-nbWKkkyb919DOUxjmRVk8vwtDb0/k8FKncmUKFi+NY+QXqWltooxTrswvz4LspQwxvLdvzBN1TImr6cw3aQx2A== dependencies: "@types/sizzle" "*" @@ -7377,7 +7377,7 @@ jquery.terminal@^2.35.3: optionalDependencies: fsevents "^2.3.2" -jquery@>=1.9.1, jquery@^3.5.0, jquery@^3.6.3, jquery@^3.7.1: +jquery@>=1.9.1, jquery@^3.5.0, jquery@^3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==