Skip to content

Commit 740878b

Browse files
committed
Modbus simulator - Added support for additional datatypes
This update adds native support for some of the most commonly found datatypes in OT systems. This includes support for bitfields, unsigned integers, signed integers, and floating point numbers in multiple word sizes (16 bit, 32 bit and 64 bit). Relevant changes include: A reworked version of simulator.py that relies on struct.unpack, struct.pack, and other native functions to simplifly datatype handling while also incorportating additional checks to ensure the register layout behaves as expected. In order to simplifly future work development, the Bits field has been renamed bitfield16, while also adding support for 32 and 64 bit fields. To help facilitate adoption, several changes have been done to the project files to ensure a smooth transition. This includes: - Updated documentation to reflect new datatypes, including examples. - Updated unit tests, to ensure the code works as expected. - Updated the simulator example to illustrate how the new datatypes can be used - Performed linting on 3.12 Resolves [discussion]: #1458 Partially addresses: #1284
1 parent a0e3044 commit 740878b

File tree

6 files changed

+801
-239
lines changed

6 files changed

+801
-239
lines changed

doc/source/library/simulator/config.rst

+103-7
Original file line numberDiff line numberDiff line change
@@ -331,26 +331,79 @@ Registers can be singulars (first entry) or arrays (second entry)
331331
Bits section
332332
^^^^^^^^^^^^
333333

334-
Example "bits" configuration:
334+
Breaking change, bits have now been renamed into bitfield16, bitfield32 and bitfield64 to promote consistency
335+
336+
337+
338+
339+
BitField16 section
340+
^^^^^^^^^^^^^^
341+
Replaces "bits"
342+
343+
Example "bitfield16" configuration:
335344

336345
.. code-block::
337346
338-
"bits": [
339-
5,
340-
[6, 7],
347+
"bitfield16": [
348+
6
349+
[6],
341350
{"addr": 8, "value": 7},
342-
{"addr": 9, "value": 7, "action": "random"},
343-
{"addr": [11, 12], "value": 7, "action": "random"}
351+
{"addr": 9, "value": 3, "action": "increment"},
352+
{"addr": [11], "value": 1, "action": "random"}
344353
],
345354
346-
defines registers which contain bits (discrete input and coils),
355+
Defines registers which contain a 16 bit field, they are effectively sintactic sugar for Uint16
347356

348357
Registers can be singulars (first entry) or arrays (second entry),
349358
furthermore a value and/or a action can be defined,
350359
the value and/or action is inserted into each register defined in "addr".
351360

352361

353362

363+
BitField32 section
364+
^^^^^^^^^^^^^^
365+
366+
Example "bitfield32" configuration:
367+
368+
.. code-block::
369+
370+
"bitfield32": [
371+
[6, 7],
372+
{"addr": [8, 9], "value": 31},
373+
{"addr": [10, 13], "value": 255, "action": "increment"},
374+
{"addr": [14, 15], "value": 65535, "action": "random"}
375+
],
376+
377+
Defines registers which contain a 32 bit field, they are effectively sintactic sugar for Uint32
378+
379+
Registers can only be arrays in multiples of 2,
380+
furthermore a value and/or a action can be defined,
381+
the value and/or action is converted (high/low value) and inserted into each register set defined in "addr".
382+
383+
384+
Uint64 section
385+
^^^^^^^^^^^^^^
386+
387+
Example "uint64" configuration:
388+
389+
.. code-block::
390+
391+
"uint64": [
392+
[6, 9],
393+
{"addr": [8, 11], "value": 18446744073709551615},
394+
{"addr": [12, 15], "value": 255, "action": "increment"},
395+
{"addr": [16, 119], "value": 7, "action": "random"}
396+
],
397+
398+
Defines registers which contain a 64 bit field, they are effectively sintactic sugar for Uint64
399+
400+
Registers can only be arrays in multiples of 4,
401+
furthermore a value and/or a action can be defined,
402+
the value and/or action is converted (high/low value) and inserted into each register set defined in "addr".
403+
404+
405+
406+
354407
Uint16 section
355408
^^^^^^^^^^^^^^
356409

@@ -395,6 +448,26 @@ furthermore a value and/or a action can be defined,
395448
the value and/or action is converted (high/low value) and inserted into each register set defined in "addr".
396449

397450

451+
Uint64 section
452+
^^^^^^^^^^^^^^
453+
454+
Example "uint64" configuration:
455+
456+
.. code-block::
457+
458+
"uint64": [
459+
[6, 9],
460+
{"addr": [8, 11], "value": 30012300},
461+
{"addr": [12, 15], "value": 40071200, "action": "increment"},
462+
{"addr": [16, 119], "value": 50051700, "action": "random"}
463+
],
464+
465+
defines sets of registers (4) which contain a 64 bit unsigned integer,
466+
467+
Registers can only be arrays in multiples of 4,
468+
furthermore a value and/or a action can be defined,
469+
the value and/or action is converted (high/low value) and inserted into each register set defined in "addr".
470+
398471

399472
Float32 section
400473
^^^^^^^^^^^^^^^
@@ -419,6 +492,29 @@ the value and/or action is converted (high/low value) and inserted into each reg
419492
Remark remember to set ``"value": <float value>`` like 512.0 (float) not 512 (integer).
420493

421494

495+
Float64 section
496+
^^^^^^^^^^^^^^^
497+
498+
Example "float64" configuration:
499+
500+
.. code-block::
501+
502+
"float64": [
503+
[6, 9],
504+
{"addr": [8, 11], "value": 3123.17},
505+
{"addr": [12, 15], "value": 712.5, "action": "increment"},
506+
{"addr": [16, 20], "value": 517.0, "action": "random"}
507+
],
508+
509+
defines sets of registers (4) which contain a 64 bit float,
510+
511+
Registers can only be arrays in multiples of 4,
512+
furthermore a value and/or a action can be defined,
513+
the value and/or action is converted (high/low value) and inserted into each register set defined in "addr".
514+
515+
Remark remember to set ``"value": <float value>`` like 512.0 (float) not 512 (integer).
516+
517+
422518

423519
String section
424520
^^^^^^^^^^^^^^

examples/datastore_simulator_share.py

+123-66
Original file line numberDiff line numberDiff line change
@@ -41,74 +41,131 @@
4141
_logger = logging.getLogger(__file__)
4242

4343
demo_config = {
44-
"setup": {
45-
"co size": 100,
46-
"di size": 150,
47-
"hr size": 200,
48-
"ir size": 250,
49-
"shared blocks": True,
50-
"type exception": False,
51-
"defaults": {
52-
"value": {
53-
"bits": 0x0708,
54-
"uint16": 1,
55-
"uint32": 45000,
56-
"float32": 127.4,
57-
"string": "X",
58-
},
59-
"action": {
60-
"bits": None,
61-
"uint16": None,
62-
"uint32": None,
63-
"float32": None,
64-
"string": None,
44+
"setup": {
45+
"co size": 100,
46+
"di size": 150,
47+
"hr size": 200,
48+
"ir size": 300,
49+
"shared blocks": True,
50+
"type exception": False,
51+
"defaults": {
52+
"value": {
53+
"bitfield16": 0x0708,
54+
"bitfield32": 0x10010708,
55+
"bitfield64": 0x8001000000003708,
56+
"int16": -1,
57+
"int32": -45000,
58+
"int64": -450000000,
59+
"uint16": 1,
60+
"uint32": 45000,
61+
"uint64": 450000000,
62+
"float32": 127.4,
63+
"float64": 10127.4,
64+
"string": "X",
65+
},
66+
"action": {
67+
"bitfield16": None,
68+
"bitfield32": None,
69+
"bitfield64": None,
70+
"int16": None,
71+
"int32": None,
72+
"int64": None,
73+
"uint16": None,
74+
"uint32": None,
75+
"uint64": None,
76+
"float32": None,
77+
"float64": None,
78+
"string": None,
79+
},
6580
},
6681
},
67-
},
68-
"invalid": [
69-
1,
70-
[6, 6],
71-
],
72-
"write": [
73-
3,
74-
[7, 8],
75-
[16, 18],
76-
[21, 26],
77-
[31, 36],
78-
],
79-
"bits": [
80-
[7, 9],
81-
{"addr": 2, "value": 0x81},
82-
{"addr": 3, "value": 17},
83-
{"addr": 4, "value": 17},
84-
{"addr": 5, "value": 17},
85-
{"addr": 10, "value": 0x81},
86-
{"addr": [11, 12], "value": 0x04342},
87-
{"addr": 13, "action": "reset"},
88-
{"addr": 14, "value": 15, "action": "reset"},
89-
],
90-
"uint16": [
91-
{"addr": 16, "value": 3124},
92-
{"addr": [17, 18], "value": 5678},
93-
{"addr": [19, 20], "value": 14661, "action": "increment"},
94-
],
95-
"uint32": [
96-
{"addr": [21, 22], "value": 3124},
97-
{"addr": [23, 26], "value": 5678},
98-
{"addr": [27, 30], "value": 345000, "action": "increment"},
99-
],
100-
"float32": [
101-
{"addr": [31, 32], "value": 3124.17},
102-
{"addr": [33, 36], "value": 5678.19},
103-
{"addr": [37, 40], "value": 345000.18, "action": "increment"},
104-
],
105-
"string": [
106-
{"addr": [41, 42], "value": "Str"},
107-
{"addr": [43, 44], "value": "Strx"},
108-
],
109-
"repeat": [{"addr": [0, 45], "to": [46, 138]}],
110-
}
111-
82+
"invalid": [
83+
1,
84+
[3, 4],
85+
],
86+
"write": [
87+
5,
88+
[7, 8],
89+
[16, 18],
90+
[21, 26],
91+
[33, 38],
92+
],
93+
"bitfield16": [
94+
[7, 7],
95+
[8, 8],
96+
{"addr": 2, "value": 0x81},
97+
{"addr": 3, "value": 17},
98+
{"addr": 4, "value": 17},
99+
{"addr": 5, "value": 17},
100+
{"addr": 10, "value": 0x81},
101+
{"addr": [11, 11], "value": 0x04342},
102+
{"addr": [12, 12], "value": 0x04342},
103+
{"addr": 13, "action": "random"},
104+
{"addr": 14, "value": 15, "action": "reset"},
105+
],
106+
"bitfield32": [
107+
[50, 51],
108+
{"addr": [52,53], "value": 0x04342},
109+
],
110+
"bitfield64": [
111+
[54, 57],
112+
{"addr": [58,61], "value": 0x04342},
113+
],
114+
"int16": [
115+
70,
116+
[71, 71],
117+
{"addr": 72, "value": 0x81},
118+
{"addr": [73, 73], "value": 0x04342},
119+
{"addr": 74, "action": "random"},
120+
{"addr": 75, "value": 15, "action": "reset"},
121+
],
122+
"int32": [
123+
[76, 77],
124+
{"addr": [78,79], "value": 0x04342},
125+
],
126+
"int64": [
127+
[80, 83],
128+
{"addr": [84,87], "value": 0x04342},
129+
],
130+
"uint16": [
131+
{"addr": 16, "value": 3124},
132+
{"addr": [17, 18], "value": 5678},
133+
{
134+
"addr": [19, 20],
135+
"value": 14661,
136+
"action": "increment",
137+
"args": {"minval": 1, "maxval": 100},
138+
},
139+
],
140+
"uint32": [
141+
{"addr": [21, 22], "value": 3124},
142+
{"addr": [23, 26], "value": 5678},
143+
{"addr": [27, 30], "value": 345000, "action": "increment"},
144+
{
145+
"addr": [31, 32],
146+
"value": 50,
147+
"action": "random",
148+
"kwargs": {"minval": 10, "maxval": 80},
149+
},
150+
],
151+
"uint64": [
152+
{"addr": [62, 65], "value": 3124}
153+
],
154+
"float32": [
155+
{"addr": [33, 34], "value": 3124.5},
156+
{"addr": [35, 38], "value": 5678.19},
157+
{"addr": [39, 42], "value": 345000.18, "action": "increment"},
158+
],
159+
"float64": [
160+
{"addr": [66, 69], "value": 3124.5},
161+
],
162+
"string": [
163+
{"addr": [43, 44], "value": "Str"},
164+
{"addr": [45, 48], "value": "Strxyz12"},
165+
],
166+
"repeat": [{"addr": [0, 95], "to": [96, 191]},
167+
{"addr": [0, 95], "to": [192, 287]}],
168+
}
112169

113170
def custom_action1(_inx, _cell):
114171
"""Test action."""

examples/simulator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def read_registers(
2828
if count == 1:
2929
value = rr.registers[0]
3030
else:
31-
value = ModbusSimulatorContext.build_value_from_registers(rr.registers, is_int)
31+
value = ModbusSimulatorContext.build_value_from_registers(rr.registers, is_int,4,False)
3232
if not is_int:
3333
value = round(value, 1)
3434
if curval:

0 commit comments

Comments
 (0)