@frangoteam/fuxa-min 1.2.1 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/_widgets/README.md +25 -0
  2. package/_widgets/controls/draggingIndicatorExample.svg +243 -0
  3. package/_widgets/controls/timePickerHHmmSS.svg +396 -0
  4. package/_widgets/controls/timePickerHHmmSSsss.svg +428 -0
  5. package/_widgets/dynamicSVG/blower3.svg +282 -0
  6. package/_widgets/dynamicSVG/blower3_flipped.svg +282 -0
  7. package/_widgets/dynamicSVG/blower4.svg +304 -0
  8. package/_widgets/dynamicSVG/blower4_flipped.svg +304 -0
  9. package/_widgets/dynamicSVG/checkvalve_closed.svg +333 -0
  10. package/_widgets/dynamicSVG/checkvalve_closed_flipped.svg +333 -0
  11. package/_widgets/dynamicSVG/checkvalve_open.svg +316 -0
  12. package/_widgets/dynamicSVG/checkvalve_open_flipped.svg +316 -0
  13. package/_widgets/dynamicSVG/compressor3.svg +361 -0
  14. package/_widgets/dynamicSVG/compressor6.svg +299 -0
  15. package/_widgets/dynamicSVG/condensor_3d_h1.svg +300 -0
  16. package/_widgets/dynamicSVG/conveyor.svg +316 -0
  17. package/_widgets/dynamicSVG/conveyor4.svg +314 -0
  18. package/_widgets/dynamicSVG/conveyor_20degdecline.svg +341 -0
  19. package/_widgets/dynamicSVG/conveyor_20degincline.svg +340 -0
  20. package/_widgets/dynamicSVG/cyclonicseparator.svg +317 -0
  21. package/_widgets/dynamicSVG/fanblades.svg +280 -0
  22. package/_widgets/dynamicSVG/flange_3d_1.svg +286 -0
  23. package/_widgets/dynamicSVG/flowmeter.svg +305 -0
  24. package/_widgets/dynamicSVG/flowmeter1.svg +281 -0
  25. package/_widgets/dynamicSVG/gaugedial_3d_h1.svg +288 -0
  26. package/_widgets/dynamicSVG/generator_1.svg +534 -0
  27. package/_widgets/dynamicSVG/heatexchanger_pipetopipe.svg +329 -0
  28. package/_widgets/dynamicSVG/heatexchanger_shelltube.svg +329 -0
  29. package/_widgets/dynamicSVG/heatexchanger_singlepipetopipe.svg +299 -0
  30. package/_widgets/dynamicSVG/meter_3d_h1.svg +299 -0
  31. package/_widgets/dynamicSVG/meter_3d_h2.svg +293 -0
  32. package/_widgets/dynamicSVG/mixer1.svg +369 -0
  33. package/_widgets/dynamicSVG/motor.svg +285 -0
  34. package/_widgets/dynamicSVG/motor1.svg +300 -0
  35. package/_widgets/dynamicSVG/motor1_flipped.svg +300 -0
  36. package/_widgets/dynamicSVG/motor_wshaft.svg +325 -0
  37. package/_widgets/dynamicSVG/pipe_3d_elbow.svg +287 -0
  38. package/_widgets/dynamicSVG/pipe_3d_elbow2.svg +285 -0
  39. package/_widgets/dynamicSVG/pipe_3d_h1.svg +286 -0
  40. package/_widgets/dynamicSVG/pipe_3d_h2.svg +287 -0
  41. package/_widgets/dynamicSVG/pipe_3d_h3.svg +286 -0
  42. package/_widgets/dynamicSVG/pipe_3d_heatexchange1.svg +298 -0
  43. package/_widgets/dynamicSVG/pipe_3d_heatexchange2.svg +293 -0
  44. package/_widgets/dynamicSVG/pipe_3d_heatexchange3.svg +289 -0
  45. package/_widgets/dynamicSVG/pipe_3d_tjunction.svg +286 -0
  46. package/_widgets/dynamicSVG/piperedirect1.svg +294 -0
  47. package/_widgets/dynamicSVG/piperedirect1_flipped.svg +294 -0
  48. package/_widgets/dynamicSVG/pressure_regulator.svg +374 -0
  49. package/_widgets/dynamicSVG/pulsationdampener.svg +300 -0
  50. package/_widgets/dynamicSVG/pump1.svg +328 -0
  51. package/_widgets/dynamicSVG/pump2.svg +310 -0
  52. package/_widgets/dynamicSVG/pump2_flipped.svg +310 -0
  53. package/_widgets/dynamicSVG/pump_3d_180.svg +323 -0
  54. package/_widgets/dynamicSVG/pump_3d_180_flipped.svg +323 -0
  55. package/_widgets/dynamicSVG/pump_3d_90_1.svg +318 -0
  56. package/_widgets/dynamicSVG/pump_3d_90_1_flipped.svg +318 -0
  57. package/_widgets/dynamicSVG/pump_3d_example.svg +292 -0
  58. package/_widgets/dynamicSVG/pump_3d_straight1.svg +321 -0
  59. package/_widgets/dynamicSVG/pump_3d_straight1_flipped.svg +321 -0
  60. package/_widgets/dynamicSVG/pumphead_profile_3d.svg +303 -0
  61. package/_widgets/dynamicSVG/reducer_3d_1.svg +290 -0
  62. package/_widgets/dynamicSVG/reducer_3d_2.svg +287 -0
  63. package/_widgets/dynamicSVG/sensor_rtd.svg +391 -0
  64. package/_widgets/dynamicSVG/sensor_rtd_silhouette.svg +286 -0
  65. package/_widgets/dynamicSVG/strainer_basket.svg +376 -0
  66. package/_widgets/dynamicSVG/strainer_basket_r.svg +377 -0
  67. package/_widgets/dynamicSVG/strainer_wye.svg +401 -0
  68. package/_widgets/dynamicSVG/strainer_wye_r.svg +403 -0
  69. package/_widgets/dynamicSVG/tank_3d_medium_rtop1.svg +286 -0
  70. package/_widgets/dynamicSVG/tank_3d_v1.svg +291 -0
  71. package/_widgets/dynamicSVG/tank_3d_v2.svg +295 -0
  72. package/_widgets/dynamicSVG/tank_3d_v4.svg +290 -0
  73. package/_widgets/dynamicSVG/tank_3d_v5.svg +295 -0
  74. package/_widgets/dynamicSVG/tank_3d_v6.svg +290 -0
  75. package/_widgets/dynamicSVG/tank_3d_v7.svg +292 -0
  76. package/_widgets/dynamicSVG/trucking.svg +297 -0
  77. package/_widgets/dynamicSVG/turbine1.svg +284 -0
  78. package/_widgets/dynamicSVG/turbine2.svg +329 -0
  79. package/_widgets/dynamicSVG/turbinerounded.svg +291 -0
  80. package/_widgets/dynamicSVG/valve_1path_down.svg +291 -0
  81. package/_widgets/dynamicSVG/valve_3d_3way1.svg +329 -0
  82. package/_widgets/dynamicSVG/valve_3d_3way1_nopipe.svg +310 -0
  83. package/_widgets/dynamicSVG/valve_3d_4way1.svg +358 -0
  84. package/_widgets/dynamicSVG/valve_3d_4way1_nopipe.svg +328 -0
  85. package/_widgets/dynamicSVG/valve_3d_angled1.svg +325 -0
  86. package/_widgets/dynamicSVG/valve_3d_angled1_nopipe.svg +305 -0
  87. package/_widgets/dynamicSVG/valve_3d_angled2.svg +323 -0
  88. package/_widgets/dynamicSVG/valve_3d_angled2_nopipe.svg +301 -0
  89. package/_widgets/dynamicSVG/valve_3d_common1.svg +321 -0
  90. package/_widgets/dynamicSVG/valve_3d_common2.svg +327 -0
  91. package/_widgets/dynamicSVG/valve_3d_common2_nopipe.svg +298 -0
  92. package/_widgets/dynamicSVG/valve_3d_common3.svg +327 -0
  93. package/_widgets/dynamicSVG/valve_3d_common4.svg +329 -0
  94. package/_widgets/dynamicSVG/valve_3d_common4_nopipe.svg +299 -0
  95. package/_widgets/dynamicSVG/valve_3d_h1.svg +286 -0
  96. package/_widgets/dynamicSVG/valve_3d_h2_closed.svg +299 -0
  97. package/_widgets/dynamicSVG/valve_3d_h2_open.svg +291 -0
  98. package/_widgets/dynamicSVG/valve_circle.svg +295 -0
  99. package/_widgets/indicators/analogIndicatorExample.svg +272 -0
  100. package/_widgets/indicators/timeDisplayHHmmSS.svg +160 -0
  101. package/_widgets/indicators/timeDisplayHHmmSSsss.svg +164 -0
  102. package/api/alarms/index.js +23 -23
  103. package/api/command/index.js +6 -35
  104. package/api/diagnose/index.js +9 -9
  105. package/api/index.js +16 -4
  106. package/api/jwt-helper.js +11 -0
  107. package/api/plugins/index.js +6 -6
  108. package/api/projects/index.js +10 -10
  109. package/api/reports/reports.service.ts +173 -0
  110. package/api/resources/index.js +6 -6
  111. package/api/scripts/index.js +9 -6
  112. package/api/users/index.js +87 -8
  113. package/dist/assets/i18n/en.json +110 -11
  114. package/dist/assets/i18n/fr.json +135 -37
  115. package/dist/assets/i18n/zh-cn.json +1 -0
  116. package/dist/assets/lib/svgeditor/fuxa-editor.min.js +5 -5
  117. package/dist/index.html +2 -2
  118. package/dist/main.060518cfc6cd1872.js +329 -0
  119. package/dist/reports.service.js +166 -0
  120. package/dist/styles.fde36dafd04e2e1d.css +1 -0
  121. package/main.js +3 -0
  122. package/package.json +4 -3
  123. package/runtime/alarms/index.js +58 -41
  124. package/runtime/devices/device-utils.js +2 -2
  125. package/runtime/devices/device.js +28 -29
  126. package/runtime/devices/ethernetip/index.js +1 -1
  127. package/runtime/devices/httprequest/index.js +2 -2
  128. package/runtime/devices/mqtt/index.js +1 -1
  129. package/runtime/index.js +106 -14
  130. package/runtime/notificator/index.js +37 -10
  131. package/runtime/plugins/index.js +1 -1
  132. package/runtime/project/index.js +25 -18
  133. package/runtime/scripts/index.js +39 -3
  134. package/runtime/scripts/msm.js +8 -7
  135. package/runtime/users/index.js +89 -2
  136. package/runtime/users/usrstorage.js +61 -1
  137. package/runtime/utils.js +35 -6
  138. package/dist/main.4a37383bf3662c04.js +0 -329
  139. package/dist/styles.2d39fcd293a60646.css +0 -1
@@ -0,0 +1,25 @@
1
+ **How to use**
2
+
3
+ The widgets will be displayed in the editor panel and can be used from there directly, or download the SVG from here make your edits and follow the steps:
4
+ ```
5
+ 1. Select desired SVG image
6
+ 2. Click download Icon
7
+ 3. Import SVG in Fuxa project using embedded Image Import
8
+ ```
9
+
10
+ **How to contribute**
11
+ ```
12
+ 1. Fork the Project
13
+ 2. In your Fork enter FUXA/server/_widgets/(what type your SVG is, control, indicator, dynamicSVG)
14
+ 3. Add File Arrow, Upload file
15
+ 4. Select your SVG and name the commit
16
+ 5. Commit the changes
17
+ 6. Go back to FUXA main page in your Fork
18
+ 7. Click Contribute
19
+ 8. Open a Pull Request
20
+ ```
21
+ Please Include a description in the How to Use section of the SVG file, how your SVG control/indicator/dynamicSVG works and how to use it in Fuxa
22
+
23
+ For more details on building SVG widgets and using them please see the Wiki
24
+
25
+ https://github.com/frangoteam/FUXA/wiki/HowTo-Widgets
@@ -0,0 +1,243 @@
1
+ <svg
2
+ viewBox="0 0 300 100"
3
+ version="1.1"
4
+ id="indicatorSVG"
5
+ xmlns="http://www.w3.org/2000/svg">
6
+ <g
7
+ id="layer1"
8
+ transform="translate(0,0)">
9
+ <rect
10
+ style="fill:#cccccc;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1.0413;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1"
11
+ id="rectBackground"
12
+ width="280"
13
+ height="40"
14
+ x="10"
15
+ y="30" />
16
+ <circle
17
+ style="fill:#ff0000;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1.0413;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1; cursor: pointer; transition: fill 0.2s ease;"
18
+ id="movingIndicator"
19
+ cx="10"
20
+ cy="50"
21
+ r="10" />
22
+ </g>
23
+ <script type="text/javascript">
24
+ <![CDATA[
25
+
26
+ /*
27
+
28
+ #####################################
29
+ # How to Use #
30
+ #####################################
31
+
32
+ Variables with the following prefix:
33
+
34
+ _pb_ = bool parameter ( true or false )
35
+ _pn_ = number parameter ( Int, Float, Real etc )
36
+ _ps_ = string parameter ( string can be entered directly )
37
+ _pc_ = color parameter in hex color code ( #00ff00ff )
38
+
39
+ Indicate variable type to be used in Fuxa Properties and binding of Tags, Important! they have to be place within:
40
+
41
+ //!export-start
42
+ let _pn_setState = 0;
43
+ //!export-end
44
+
45
+
46
+ There are 2x Functions to handle sending and receiving data between Fuxa and the SVG
47
+
48
+ // Recieve Variables From Fuxa
49
+ function putValue(id, value) { }
50
+
51
+ // Send Variables To Fuxa
52
+ function postValue(id, value) {
53
+ console.error('Not defined!');
54
+ }
55
+
56
+ The ID is the name of the variable _pn_setState for example and value is the value to be sent or received from Fuxa
57
+
58
+ The scripts etc need to be within the SVG tags
59
+
60
+ <svg>
61
+ SVG Content Here
62
+ <script>
63
+ JS Script Content Here
64
+ </script>
65
+ </svg>
66
+
67
+
68
+ The below example uses simple SVG elements as an example, but complex logig to handle everything fully dynamic
69
+ you can drag the indicator within if the bounds of the rectangle
70
+ You can change the angle of the SVG in Fuxa editor to 45 degrees for example and it will still work
71
+ You and resize the slider in Fuxa editor and dragging adjusts to the new size
72
+ This can be edited even further to suit your needs, just a good starting point
73
+
74
+ */
75
+
76
+ //!export-start
77
+ let _pn_currentValue = 0; // Current value of the indicator
78
+ let _pn_initValue = 0; // Initial value of the indicator
79
+ let _pn_minVal = -100; // Minimum value for scaling
80
+ let _pn_maxVal = 100; // Maximum value for scaling
81
+ let _pc_defaultColour = '#ff0000'; // Default color of the indicator
82
+ let _pc_clickedColour = '#00ff00'; // Color when the indicator is clicked
83
+ //!export-end
84
+
85
+ let isDragging = false; // Indicates if the indicator is currently being dragged
86
+ let dragOffsetX = 0; // Offset between mouse and indicator when drag starts
87
+ let rectLeft = 0; // Left position of the rectangle
88
+ let rectWidth = 0; // Width of the rectangle
89
+ let indicatorRadius = 0; // Variable to hold the radius of the indicator
90
+
91
+ // Get the bounding box of the SVG to adjust for its size and position
92
+ const svgElement = document.getElementById('indicatorSVG');
93
+
94
+ // Update the indicator's position based on the provided value
95
+ function updateIndicator(value) {
96
+ const rect = document.getElementById('rectBackground');
97
+ const indicator = document.getElementById('movingIndicator');
98
+
99
+ // Get dimensions and position
100
+ rectWidth = rect.width.baseVal.value;
101
+ rectLeft = rect.x.baseVal.value;
102
+
103
+ const scaledValue = (value - _pn_minVal) / (_pn_maxVal - _pn_minVal); // Scale value to range
104
+
105
+ // Calculate position of the indicator
106
+ const indicatorX = scaledValue * (rectWidth - 2 * indicatorRadius) + rectLeft + indicatorRadius; // Adjust for radius
107
+
108
+ // Update the position of the indicator
109
+ indicator.setAttribute('cx', indicatorX);
110
+ // Update the current value
111
+ _pn_currentValue = value;
112
+ }
113
+
114
+ // Change the indicator's color when clicked
115
+ function changeColor() {
116
+ const indicator = document.getElementById('movingIndicator');
117
+ if (indicator) {
118
+ indicator.style.fill = _pc_clickedColour; // Change the fill color
119
+ }
120
+ }
121
+
122
+ // Reset the indicator's color to default
123
+ function resetColor() {
124
+ const indicator = document.getElementById('movingIndicator');
125
+ if (indicator) {
126
+ indicator.style.fill = _pc_defaultColour; // Change the fill color
127
+ }
128
+ }
129
+
130
+ // Start dragging the indicator
131
+ function startDrag(event) {
132
+ isDragging = true;
133
+
134
+ // Get transformation matrix for correct mouse-to-SVG coordinate mapping
135
+ const matrix = svgElement.getScreenCTM().inverse();
136
+ const point = svgElement.createSVGPoint();
137
+
138
+ point.x = event.clientX || (event.touches ? event.touches[0].clientX : 0);
139
+ point.y = event.clientY || (event.touches ? event.touches[0].clientY : 0);
140
+ const transformedPoint = point.matrixTransform(matrix);
141
+
142
+ const indicator = document.getElementById('movingIndicator');
143
+ dragOffsetX = transformedPoint.x - parseFloat(indicator.getAttribute('cx')); // Offset between mouse and indicator center
144
+ event.preventDefault(); // Prevent default actions
145
+ }
146
+
147
+ // Handle dragging the indicator (corrected for rotation)
148
+ function dragMove(event) {
149
+ if (!isDragging) return;
150
+
151
+ const indicator = document.getElementById('movingIndicator');
152
+ if (indicator) {
153
+ // Get the inverse of the current transformation matrix (to map mouse coordinates correctly)
154
+ const matrix = svgElement.getScreenCTM().inverse();
155
+ const point = svgElement.createSVGPoint();
156
+
157
+ // Get the current mouse position in client coordinates
158
+ point.x = event.clientX || (event.touches ? event.touches[0].clientX : 0);
159
+ point.y = event.clientY || (event.touches ? event.touches[0].clientY : 0);
160
+
161
+ // Transform the point into the SVG's local coordinates
162
+ const transformedPoint = point.matrixTransform(matrix);
163
+
164
+ // Calculate the new position for the indicator (adjust for drag offset)
165
+ let newCx = transformedPoint.x - dragOffsetX;
166
+
167
+ // Constrain the new position within the rectangle bounds
168
+ const minX = rectLeft + indicatorRadius;
169
+ const maxX = rectLeft + rectWidth - indicatorRadius;
170
+ newCx = Math.max(minX, Math.min(maxX, newCx));
171
+
172
+ // Update the indicator's position
173
+ indicator.setAttribute('cx', newCx);
174
+
175
+ // Update the current value based on the new position
176
+ const scaledValue = (newCx - rectLeft - indicatorRadius) / (rectWidth - 2 * indicatorRadius);
177
+ _pn_currentValue = _pn_minVal + scaledValue * (_pn_maxVal - _pn_minVal);
178
+
179
+ // Send Value to Fuxa
180
+ postValue('_pn_currentValue', _pn_currentValue);
181
+ }
182
+ }
183
+
184
+ // End dragging the indicator
185
+ function endDrag() {
186
+ if (isDragging) {
187
+ resetColor(); // Reset color when dragging ends
188
+ isDragging = false;
189
+ }
190
+ }
191
+
192
+ // Initial setup and event listeners
193
+ function init() {
194
+ updateIndicator(_pn_initValue); // Set initial position based on initial value
195
+
196
+ const indicator = document.getElementById('movingIndicator');
197
+ if (indicator) {
198
+ indicatorRadius = indicator.r.baseVal.value; // Get the radius once during initialization
199
+ indicator.addEventListener('mousedown', function(event) {
200
+ changeColor();
201
+ startDrag(event);
202
+ });
203
+ indicator.addEventListener('mouseup', endDrag);
204
+ indicator.addEventListener('touchstart', function(event) {
205
+ changeColor();
206
+ startDrag(event);
207
+ });
208
+ indicator.addEventListener('touchend', endDrag);
209
+ }
210
+
211
+ // Add global listeners for drag
212
+ window.addEventListener('mousemove', dragMove);
213
+ window.addEventListener('mouseup', endDrag);
214
+
215
+ // Add global listeners for touch drag
216
+ window.addEventListener('touchmove', dragMove);
217
+ window.addEventListener('touchend', endDrag);
218
+ }
219
+
220
+ // Variables received from Fuxa
221
+ function putValue(id, value) {
222
+ if (id === '_pn_currentValue') {
223
+ updateIndicator(value);
224
+ }
225
+ if (id === '_pn_initValue' ) _pn_initValue = value;
226
+ if (id === '_pn_minVal' ) _pn_minVal = value;
227
+ if (id === '_pn_maxVal' ) _pn_maxVal = value;
228
+ if (id === '_pc_defaultColour') _pc_defaultColour = value;
229
+ if (id === '_pc_clickedColour') _pc_clickedColour = value;
230
+ }
231
+
232
+ // Send Variables to Fuxa
233
+ function postValue(id, value) {
234
+ console.error('Not defined!');
235
+ }
236
+
237
+ init(); // Start initialization
238
+ ]]>
239
+ </script>
240
+ </svg>
241
+
242
+
243
+
@@ -0,0 +1,396 @@
1
+ <svg id="timePicker" width="173.02" height="123.7" version="1.1" viewBox="0 0 173.02 123.7" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- Add style to prevent text selection -->
3
+ <style id="style1">svg text {
4
+ -webkit-touch-callout: none;
5
+ -webkit-user-select: none;
6
+ -moz-user-select: none;
7
+ -ms-user-select: none;
8
+ user-select: none;
9
+ pointer-events: none;
10
+ }</style>
11
+ <!-- Background Rectangle for Time Display -->
12
+ <rect id="timeBox" x=".86273" y="36.504" width="171.29" height="50.733" cursor="pointer" fill="#f0f0f0" stroke="#000000" stroke-width="1.7255"/>
13
+ <!-- Time Display (h:m:s:ms) -->
14
+ <g id="timeDisplay" transform="translate(.1333)" fill="#000000" font-family="Arial" font-size="24px" font-weight="bold" text-anchor="middle">
15
+ <text id="hoursDisplay" x="21.215105" y="71.825523" style="-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-user-select:none">00</text>
16
+ <text id="text1" x="42.98912" y="69.677528" style="-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-user-select:none">:</text>
17
+ <text id="minutesDisplay" x="64.950638" y="71.825523" style="-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-user-select:none">00</text>
18
+ <text id="text2" x="86.724655" y="69.677528" style="-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-user-select:none">:</text>
19
+ <text id="secondsDisplay" x="108.68617" y="71.825523" style="-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-webkit-user-select:none">00</text>
20
+ </g>
21
+ <!-- Adjustment Arrows -->
22
+ <g id="arrows" transform="translate(17.02 -11.82)" style="display:none;stroke:#333333">
23
+ <!-- Hour Arrows -->
24
+ <g id="hourArrows" class="arrow-group" transform="translate(-8)">
25
+ <g id="hourUpArrow" class="arrow" transform="translate(.32843 44)" stroke="#000000" stroke-width="2">
26
+ <path id="hourUpArrowPath" transform="rotate(-90)" width="24" height="24" d="M 8.122,24 4,20 12,12 4,4 8.122,0 20,12 Z"/>
27
+ </g>
28
+ <g id="hourDownArrow" class="arrow" transform="translate(24.328,102.81)" stroke="#000000" stroke-width="2">
29
+ <path id="hourDownArrowPath" transform="rotate(90)" width="24" height="24" d="M 8.122,24 4,20 12,12 4,4 8.122,0 20,12 Z"/>
30
+ </g>
31
+ <!-- Transparent rectangles for clickable area -->
32
+ <rect id="hourUpTransparent" x="-7.6716" y="11.82" width="40" height="35" cursor="pointer" fill="#00000000" stroke="none" style="fill-opacity:.0025674;fill:#000000;stroke-width:1.2491"/>
33
+ <rect id="hourDownTransparent" x="-7.6716" y="100.43" width="40" height="35" cursor="pointer" fill="#00000000" stroke="none" style="fill-opacity:.0025674;fill:#000000;stroke-width:1.2491"/>
34
+ </g>
35
+ <!-- Minute Arrows -->
36
+ <g id="minuteArrows" class="arrow-group" transform="translate(-28)">
37
+ <g id="minuteUpArrow" class="arrow" transform="translate(63.837,44)" cursor="pointer" stroke="#000000" stroke-width="2">
38
+ <path id="minuteUpArrowPath" transform="rotate(-90)" width="24" height="24" d="M 8.122,24 4,20 12,12 4,4 8.122,0 20,12 Z"/>
39
+ </g>
40
+ <g id="minuteDownArrow" class="arrow" transform="translate(87.837,102.78)" cursor="pointer" stroke="#000000" stroke-width="2">
41
+ <path id="minuteDownArrowPath" transform="rotate(90)" width="24" height="24" d="M 8.122,24 4,20 12,12 4,4 8.122,0 20,12 Z"/>
42
+ </g>
43
+ <!-- Transparent rectangles for clickable area -->
44
+ <rect id="minuteUpTransparent" x="55.837" y="12.385" width="40" height="35" cursor="pointer" fill="#00000000" stroke="none" style="fill-opacity:0;stroke-width:1.2491"/>
45
+ <rect id="minuteDownTransparent" x="55.837" y="100.52" width="40" height="35" cursor="pointer" fill="#00000000" stroke="none" style="fill-opacity:0;stroke-width:1.2491"/>
46
+ </g>
47
+ <!-- Second Arrows -->
48
+ <g id="secondArrows" class="arrow-group" transform="translate(-46)">
49
+ <g id="secondUpArrow" class="arrow" transform="translate(124.97,44)" cursor="pointer" stroke="#000000" stroke-width="2">
50
+ <path id="secondUpArrowPath" transform="rotate(-90)" width="24" height="24" d="M 8.122,24 4,20 12,12 4,4 8.122,0 20,12 Z"/>
51
+ </g>
52
+ <g id="secondDownArrow" class="arrow" transform="translate(148.97,102.81)" cursor="pointer" stroke="#000000" stroke-width="2">
53
+ <path id="secondDownArrowPath" transform="rotate(90)" width="24" height="24" d="M 8.122,24 4,20 12,12 4,4 8.122,0 20,12 Z"/>
54
+ </g>
55
+ <!-- Transparent rectangles for clickable area -->
56
+ <rect id="secondUpTransparent" x="116.97" y="12.385" width="40" height="35" cursor="pointer" fill="#00000000" stroke="none" style="fill-opacity:0;stroke-width:1.2491"/>
57
+ <rect id="secondDownTransparent" x="116.97" y="100.43" width="40" height="35" cursor="pointer" fill="#00000000" stroke="none" style="fill-opacity:0;stroke-width:1.2491"/>
58
+ </g>
59
+ <!-- Millisecond Arrows -->
60
+ </g>
61
+ <!-- Time Symbol (Clock) -->
62
+ <g id="timeSymbol" transform="matrix(1.3908 0 0 1.3908 130.24 45.426)" fill="none" stroke="#000000" stroke-width="2">
63
+ <g id="g1" stroke-linecap="round" stroke-linejoin="round">
64
+ <circle id="circle1" cx="12" cy="12" r="10"/>
65
+ <path id="path1" d="m12 6v6l4 4"/>
66
+ </g>
67
+ </g>
68
+ <!-- Transparent clickable area over time symbol -->
69
+ <rect id="timeBoxTransparent" x="1.1333" y="36.774" width="171.11" height="50.191" cursor="pointer" fill="#00000000" style="fill-opacity:0;stroke-width:.74334"/>
70
+
71
+ <!-- JavaScript for Time Adjustment -->
72
+ <script type="text/ecmascript">
73
+ <![CDATA[
74
+
75
+ /*
76
+ #####################################
77
+ # How to Use #
78
+ #####################################
79
+
80
+ Variables with the following prefix:
81
+
82
+ _pb_ = bool parameter ( true or false )
83
+ _pn_ = number parameter ( Int, Float, Real etc )
84
+ _ps_ = string parameter ( string can be entered directly )
85
+ _pc_ = color parameter in hex color code ( #00ff00ff )
86
+
87
+ Indicate variable type to be used in Fuxa Properties and binding of Tags, Important! they have to be place within:
88
+
89
+ //!export-start
90
+ let _pn_setState = 0;
91
+ //!export-end
92
+
93
+
94
+ There are 2x Functions to handle sending and receiving data between Fuxa and the SVG
95
+
96
+ // Recieve Variables From Fuxa
97
+ function putValue(id, value) { }
98
+
99
+ // Send Variables To Fuxa
100
+ function postValue(id, value) {
101
+ console.error('Not defined!');
102
+ }
103
+
104
+ The ID is the name of the variable _pn_setState for example and value is the value to be sent or received from Fuxa
105
+
106
+
107
+ This Time Widget uses the IEC 61131 TIME Datatype Format in MS (milliseconds) and an Number for more details see:
108
+
109
+ https://content.helpme-codesys.com/en/CODESYS%20Development%20System/_cds_datatype_time.html
110
+
111
+ Currently there is a small bug in Fuxa using the TIME data type via OPC-UA is comes through as an 64bit and an array with 2x sections
112
+ You can use the scale script to fix this see https://github.com/frangoteam/FUXA/wiki/HowTo-Devices-and-Tags at the bottom on how to use
113
+
114
+ All you have todo is use the timeMS with a number tag and it will convert the time selection into MS to pass to the PLC TIME Datatype
115
+
116
+ <svg>
117
+ SVG Content Here
118
+ <script>
119
+ JS Script Content Here
120
+ </script>
121
+ </svg>
122
+
123
+ */
124
+
125
+ //!export-start
126
+ let _pn_timeMS = 0;
127
+ let _pc_baseColor = '#f0f0f0';
128
+ let _pc_baseStrokeColor = '#595959';
129
+ let _pc_timeSymbolStrokeColor = '#595959';
130
+ let _pc_arrowColor = '#f0f0f0';
131
+ let _pc_arrowStrokeColor = '#595959';
132
+ let _pc_arrowClickColor = '#595959';
133
+ let _pc_textColor = '#595959';
134
+ //!export-end
135
+
136
+ let currentTime = 0;
137
+ let adjustInterval = null;
138
+ let holdTimer = null;
139
+ let delayTimer = null;
140
+ let postTimer = null;
141
+ let step = 1;
142
+ let adjustingUnit = '';
143
+
144
+ const hoursDisplay = document.getElementById("hoursDisplay");
145
+ const minutesDisplay = document.getElementById("minutesDisplay");
146
+ const secondsDisplay = document.getElementById("secondsDisplay");
147
+ const timeDisplay = document.getElementById("timeDisplay");
148
+ const arrows = document.getElementById("arrows");
149
+ const svgElement = document.getElementById("timePicker");
150
+ const timeBox = document.getElementById("timeBox");
151
+ const timeSymbol = document.getElementById("timeSymbol");
152
+
153
+ // Arrow Elements
154
+ const hourUpArrow = document.getElementById("hourUpArrow");
155
+ const hourDownArrow = document.getElementById("hourDownArrow");
156
+ const minuteUpArrow = document.getElementById("minuteUpArrow");
157
+ const minuteDownArrow = document.getElementById("minuteDownArrow");
158
+ const secondUpArrow = document.getElementById("secondUpArrow");
159
+ const secondDownArrow = document.getElementById("secondDownArrow");
160
+
161
+ // Arrow Transparent Overlay
162
+ const hourUpTransparent = document.getElementById("hourUpTransparent");
163
+ const hourDownTransparent = document.getElementById("hourDownTransparent");
164
+ const minuteUpTransparent = document.getElementById("minuteUpTransparent");
165
+ const minuteDownTransparent = document.getElementById("minuteDownTransparent");
166
+ const secondUpTransparent = document.getElementById("secondUpTransparent");
167
+ const secondDownTransparent = document.getElementById("secondDownTransparent");
168
+
169
+ // Init
170
+ function init() {
171
+ updateTimeDisplay();
172
+ updateColors();
173
+ }
174
+
175
+ // Convert time in milliseconds to HH:mm:ss:sss format
176
+ function formatTime(ms) {
177
+ const hours = Math.floor(ms / (1000 * 60 * 60));
178
+ const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60));
179
+ const seconds = Math.floor((ms % (1000 * 60)) / 1000);
180
+ const milliseconds = ms % 1000;
181
+ return {
182
+ hours: hours.toString().padStart(2, '0'),
183
+ minutes: minutes.toString().padStart(2, '0'),
184
+ seconds: seconds.toString().padStart(2, '0'),
185
+ milliseconds: milliseconds.toString().padStart(3, '0')
186
+ };
187
+ }
188
+
189
+ // Convert time in HH:mm:ss:sss to milliseconds format
190
+ function parseTime(hours, minutes, seconds, milliseconds) {
191
+ const ms = (hours * 3600 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + milliseconds;
192
+ return ms;
193
+ }
194
+
195
+ // Update the displayed time
196
+ function updateTimeDisplay() {
197
+ const time = formatTime(_pn_timeMS);
198
+ hoursDisplay.textContent = time.hours;
199
+ minutesDisplay.textContent = time.minutes;
200
+ secondsDisplay.textContent = time.seconds;
201
+ }
202
+
203
+ // Update the displayed time
204
+ function updateColors() {
205
+
206
+ timeBox.setAttribute('fill', _pc_baseColor);
207
+ timeBox.setAttribute('stroke', _pc_baseStrokeColor);
208
+ timeSymbol.setAttribute('stroke', _pc_timeSymbolStrokeColor);
209
+ timeDisplay.setAttribute('fill', _pc_textColor);
210
+
211
+ document.querySelectorAll(".arrow").forEach(function (arrow) {
212
+ arrow.setAttribute("fill", _pc_arrowColor);
213
+ arrow.setAttribute("stroke", _pc_arrowStrokeColor);
214
+ });
215
+ }
216
+
217
+ // Show or hide the arrows when the time rectangle is clicked
218
+ const timeBoxTransparent = document.getElementById("timeBoxTransparent");
219
+ timeBoxTransparent.addEventListener("click", function (event) {
220
+ if (arrows.style.display === "none") {
221
+ arrows.style.display = "block"; // Show arrows
222
+ } else {
223
+ arrows.style.display = "none"; // Hide arrows
224
+ }
225
+ });
226
+
227
+ // Hide arrows when clicking outside the time box
228
+ document.addEventListener("click", function (event) {
229
+ if (!timeBoxTransparent.contains(event.target) && !arrows.contains(event.target)) {
230
+ arrows.style.display = "none"; // Hide arrows when clicking outside
231
+ }
232
+ });
233
+
234
+ // Wrap hours between 0-23
235
+ function wrapHours(hours) {
236
+ return (hours + 24) % 24;
237
+ }
238
+
239
+ // Wrap minutes between 0-59
240
+ function wrapMinutes(minutes) {
241
+ return (minutes + 60) % 60;
242
+ }
243
+
244
+ // Wrap seconds between 0-59
245
+ function wrapSeconds(seconds) {
246
+ return (seconds + 60) % 60;
247
+ }
248
+
249
+ // Wrap milliseconds between 0-999
250
+ function wrapMilliseconds(ms) {
251
+ return (ms + 1000) % 1000;
252
+ }
253
+
254
+ function startAdjusting(amount, unit, arrow, stepSize) {
255
+ clearTimeout(postTimer);
256
+ adjustingUnit = unit;
257
+ step = 1; // Reset step when starting adjustment
258
+ delayTimer = setTimeout(() => {
259
+ adjustTime(amount, unit);
260
+ }, 20);
261
+
262
+ // Set a timeout to initiate the adjustment every 200ms after 500ms
263
+ holdTimer = setTimeout(() => {
264
+ adjustInterval = setInterval(function () {
265
+ step = Math.min(step + 1, stepSize);
266
+ adjustTime(amount * step, unit);
267
+ }, 200);
268
+ }, 500);
269
+
270
+ // Reset color for all arrows before applying color to the clicked one
271
+ resetArrowColors();
272
+ arrow.setAttribute("fill", _pc_arrowClickColor); // Change the clicked arrow's color to red
273
+ }
274
+
275
+ // Reset all arrows' color to black
276
+ function resetArrowColors() {
277
+ document.querySelectorAll(".arrow").forEach(function (arrow) {
278
+ arrow.setAttribute("fill", _pc_arrowColor);
279
+ });
280
+ }
281
+
282
+ // Stop adjusting time and reset color when mouse is released
283
+ function stopAdjusting() {
284
+ clearTimeout(delayTimer);
285
+ clearTimeout(holdTimer);
286
+ clearInterval(adjustInterval);
287
+ resetArrowColors(); // Reset all arrows' colors
288
+ adjustingUnit = ''; // Reset unit
289
+
290
+ postTimer = setTimeout(() => {
291
+ // Send Value to Fuxa when adjusting done
292
+ postValue('_pn_timeMS', _pn_timeMS);
293
+ }, 2000);
294
+ }
295
+
296
+ // Adjust time based on the selected unit
297
+ function adjustTime(amount, unit) {
298
+ const timeParts = formatTime(_pn_timeMS);
299
+ let hours = parseInt(timeParts.hours, 10);
300
+ let minutes = parseInt(timeParts.minutes, 10);
301
+ let seconds = parseInt(timeParts.seconds, 10);
302
+ let milliseconds = parseInt(timeParts.milliseconds, 10);
303
+
304
+ switch (unit) {
305
+ case "hour":
306
+ hours = wrapHours(hours + amount);
307
+ _pn_timeMS = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + milliseconds;
308
+ break;
309
+ case "minute":
310
+ minutes = wrapMinutes(minutes + amount);
311
+ _pn_timeMS = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + milliseconds;
312
+ break;
313
+ case "second":
314
+ seconds = wrapSeconds(seconds + amount);
315
+ _pn_timeMS = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + milliseconds;
316
+ break;
317
+ case "millisecond":
318
+ milliseconds = wrapMilliseconds(milliseconds + amount);
319
+ _pn_timeMS = (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + (seconds * 1000) + milliseconds;
320
+ break;
321
+ }
322
+ updateTimeDisplay();
323
+ }
324
+
325
+ // Add event listeners for arrows
326
+ hourUpTransparent.addEventListener("mousedown", function () {
327
+ startAdjusting(1, "hour", hourUpArrow, 1);
328
+ });
329
+ hourDownTransparent.addEventListener("mousedown", function () {
330
+ startAdjusting(-1, "hour", hourDownArrow, 1);
331
+ });
332
+ minuteUpTransparent.addEventListener("mousedown", function () {
333
+ startAdjusting(1, "minute", minuteUpArrow, 5);
334
+ });
335
+ minuteDownTransparent.addEventListener("mousedown", function () {
336
+ startAdjusting(-1, "minute", minuteDownArrow, 5);
337
+ });
338
+ secondUpTransparent.addEventListener("mousedown", function () {
339
+ startAdjusting(1, "second", secondUpArrow, 5);
340
+ });
341
+ secondDownTransparent.addEventListener("mousedown", function () {
342
+ startAdjusting(-1, "second", secondDownArrow, 5);
343
+ });
344
+
345
+ // Stop adjusting when mouse is released
346
+ svgElement.addEventListener("mouseup", stopAdjusting);
347
+
348
+ // Add touch event listeners for mobile devices
349
+ hourUpTransparent.addEventListener("touchstart", function () {
350
+ startAdjusting(1, "hour", hourUpArrow, 1);
351
+ });
352
+ hourDownTransparent.addEventListener("touchstart", function () {
353
+ startAdjusting(-1, "hour", hourDownArrow, 1);
354
+ });
355
+ minuteUpTransparent.addEventListener("touchstart", function () {
356
+ startAdjusting(1, "minute", minuteUpArrow, 5);
357
+ });
358
+ minuteDownTransparent.addEventListener("touchstart", function () {
359
+ startAdjusting(-1, "minute", minuteDownArrow, 5);
360
+ });
361
+ secondUpTransparent.addEventListener("touchstart", function () {
362
+ startAdjusting(1, "second", secondUpArrow, 5);
363
+ });
364
+ secondDownTransparent.addEventListener("touchstart", function () {
365
+ startAdjusting(-1, "second", secondDownArrow, 5);
366
+ });
367
+
368
+ // Stop adjusting when touch is released
369
+ svgElement.addEventListener("touchend", stopAdjusting);
370
+
371
+ // Variables received from Fuxa
372
+ function putValue(id, value) {
373
+ if (id === '_pn_timeMS' ) _pn_timeMS = value;
374
+ if (id === '_pc_baseColor' ) _pc_baseColor = value;
375
+ if (id === '_pc_baseStrokeColor' ) _pc_baseStrokeColor = value;
376
+ if (id === '_pc_timeSymbolStrokeColor' ) _pc_timeSymbolStrokeColor = value;
377
+ if (id === '_pc_arrowColor' ) _pc_arrowColor = value;
378
+ if (id === '_pc_arrowStrokeColor' ) _pc_arrowStrokeColor = value;
379
+ if (id === '_pc_arrowClickColor' ) _pc_arrowClickColor = value;
380
+ if (id === '_pc_textColor' ) _pc_textColor = value;
381
+
382
+ updateTimeDisplay();
383
+ updateColors();
384
+ }
385
+
386
+ // Send Variables to Fuxa
387
+ function postValue(id, value) {
388
+ console.error('Not defined!');
389
+ }
390
+
391
+ init(); // Start initialization
392
+
393
+ ]]>
394
+ </script>
395
+
396
+ </svg>