Skip to content

Commit 6d23798

Browse files
authored
Fix coils and discrete input register initialization (#20)
Try fixing issue #19
1 parent c7f2390 commit 6d23798

File tree

5 files changed

+88
-21
lines changed

5 files changed

+88
-21
lines changed

Dockerfile

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,16 @@ LABEL site.local.os.version="3.17"
88
LABEL site.local.runtime.name="Python"
99
LABEL site.local.runtime.version="3.10.9"
1010
LABEL site.local.program.name="Python Modbus TCP Server"
11-
LABEL site.local.program.version="1.1.3"
11+
LABEL site.local.program.version="1.1.4"
1212

13-
RUN addgroup -g 1000 -S pythonuser \
14-
&& adduser -u 1000 -S pythonuser -G pythonuser \
15-
&& mkdir -p /app \
16-
&& pip3 install --no-cache-dir 'pymodbus>=2,<3'
17-
COPY --chown=root:root app/* /app/
13+
COPY --chown=root:root /src /
14+
15+
RUN pip3 install --no-cache-dir -r /requirements.txt
1816

19-
USER pythonuser
2017
EXPOSE 5020/tcp
2118

19+
USER 1434:1434
20+
2221
# Start Server
2322
ENTRYPOINT ["python", "-u", "/app/modbus_server.py"]
2423
CMD ["-f", "/app/modbus_server.json"]

examples/test.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"server": {
3+
"listenerAddress": "0.0.0.0",
4+
"listenerPort": 5020,
5+
"tlsParams": {
6+
"description": "path to certificate and private key to enable tls",
7+
"privateKey": null,
8+
"certificate": null
9+
},
10+
"logging": {
11+
"format": "%(asctime)-15s %(threadName)-15s %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s",
12+
"logLevel": "DEBUG"
13+
}
14+
},
15+
"registers": {
16+
"description": "initial values for the register types",
17+
"zeroMode": false,
18+
"initializeUndefinedRegisters": true,
19+
"discreteInput": {
20+
"1": true,
21+
"2": false,
22+
"3": true,
23+
"4": false
24+
},
25+
"coils": {
26+
"1": false,
27+
"2": true,
28+
"3": true,
29+
"4": false
30+
},
31+
"holdingRegister": {
32+
"1": "0xCC01",
33+
"2": "0xCC02",
34+
"3": "0xCC03",
35+
"4": "0xCC04"
36+
},
37+
"inputRegister": {
38+
"1": "0xDD01",
39+
"2": "0xDD02",
40+
"3": "0xDD03",
41+
"4": "0xDD04"
42+
}
43+
}
44+
}
File renamed without changes.

app/modbus_server.py renamed to src/app/modbus_server.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
Modbus TCP server script for debugging
44
Author: Michael Oberdorf IT-Consulting
55
Datum: 2020-03-30
6+
Last modified by: Hackergarden Meetup@Codecentric
7+
Last modified at: 2023-11-07
68
*************************************************************************** """
79
import sys
810
import os
@@ -18,8 +20,8 @@
1820
import json
1921

2022
# default configuration file path
21-
config_file='/opt/modbus_server/etc/modbus_server.json'
22-
VERSION='1.1.3'
23+
config_file='/app/modbus_server.json'
24+
VERSION='1.1.4'
2325
"""
2426
###############################################################################
2527
# F U N C T I O N S
@@ -39,7 +41,7 @@ def get_ip_address():
3941
pass
4042
return(ipaddr)
4143

42-
def run_server(listener_address='0.0.0.0', listener_port=5020, tls_cert=None, tls_key=None, zeroMode=False, discreteInputs=dict(), coils=dict(), holdingRegisters=dict(), inputRegisters=dict()):
44+
def run_server(listener_address: str = '0.0.0.0', listener_port: int = 5020, tls_cert: str = None, tls_key: str = None, zeroMode: bool = False, discreteInputs: dict = dict(), coils: dict = dict(), holdingRegisters: dict = dict(), inputRegisters: dict = dict()):
4345
"""
4446
Run the modbus server(s)
4547
@param listener_address: string, IP address to bind the listener (default: '0.0.0.0')
@@ -139,10 +141,11 @@ def run_server(listener_address='0.0.0.0', listener_port=5020, tls_cert=None, tl
139141
# StartTcpServer(context, identity=identity, framer=ModbusRtuFramer, address=(listener_address, listener_port))
140142

141143

142-
def prepareRegister(register, initializeUndefinedRegisters=False):
144+
def prepareRegister(register: dict, initType: str, initializeUndefinedRegisters: bool = False) -> dict:
143145
"""
144146
Function to prepare the register to have the correct data types
145147
@param register: dict(), the register dictionary, loaded from json file
148+
@param initType: str(), how to initialize the register values 'boolean' or 'word'
146149
@param initializeUndefinedRegisters: boolean, fill undefined registers with 0x00 (default: False)
147150
@return: dict(), register with correct data types
148151
"""
@@ -160,17 +163,34 @@ def prepareRegister(register, initializeUndefinedRegisters=False):
160163

161164
val = register[key]
162165
valOut = val
163-
if isinstance(val, str) and str(val)[0:2] == '0x':
166+
if initType == 'word' and isinstance(val, str) and str(val)[0:2] == '0x':
164167
valOut = int(val, 16)
165168
log.debug(' Transform value for register: ' + str(keyOut) + ' from: ' + str(val) + ' ('+ str(type(key)) + ') to: ' + str(valOut) + ' (' + str(type(valOut)) + ')')
169+
elif initType == 'boolean':
170+
if isinstance(val, bool):
171+
valOut = val
172+
log.debug(' Set register: ' + str(keyOut) + ' to: ' + str(valOut) + ' (' + str(type(valOut)) + ')')
173+
elif isinstance(val, int):
174+
if val == 0:
175+
valOut = False
176+
else:
177+
valOut = True
178+
log.debug(' Transform value for register: ' + str(keyOut) + ' from: ' + str(val) + ' ('+ str(type(key)) + ') to: ' + str(valOut) + ' (' + str(type(valOut)) + ')')
166179
outRegister[keyOut] = valOut
167180

168181
if initializeUndefinedRegisters:
169-
log.debug(' Fill undefined registers with 0x00')
182+
if initType == 'word':
183+
log.debug(' Fill undefined registers with 0x00')
184+
elif initType == 'boolean':
185+
log.debug(' Fill undefined registers with False')
170186
for r in range(0, 65536, 1):
171187
if r not in outRegister:
188+
if initType == 'word':
172189
#log.debug(' Initialize address: ' + str(r) + ' with 0')
173190
outRegister[r] = 0
191+
elif initType == 'boolean':
192+
#log.debug(' Initialize address: ' + str(r) + ' with False')
193+
outRegister[r] = False
174194

175195
return(outRegister)
176196

@@ -194,7 +214,8 @@ def prepareRegister(register, initializeUndefinedRegisters=False):
194214
config_file=args.config_file
195215

196216
# read configuration file
197-
with open(config_file, encoding='utf-8') as f: CONFIG = json.load(f)
217+
with open(config_file, encoding='utf-8') as f:
218+
CONFIG = json.load(f)
198219

199220

200221
# Initialize logger
@@ -209,15 +230,16 @@ def prepareRegister(register, initializeUndefinedRegisters=False):
209230
else: log.setLevel(logging.INFO)
210231

211232

212-
# be sure the data types within the dictionaries are correct (json will only allow strings as keys)
213-
discreteInputs = prepareRegister(CONFIG['registers']['discreteInput'], CONFIG['registers']['initializeUndefinedRegisters'])
214-
coils=prepareRegister(CONFIG['registers']['coils'], CONFIG['registers']['initializeUndefinedRegisters'])
215-
holdingRegisters=prepareRegister(CONFIG['registers']['holdingRegister'], CONFIG['registers']['initializeUndefinedRegisters'])
216-
inputRegisters=prepareRegister(CONFIG['registers']['inputRegister'], CONFIG['registers']['initializeUndefinedRegisters'])
217-
218-
219233
# start the server
220234
log.info('Starting Modbus TCP Server, v' + str(VERSION))
235+
log.debug('Loaded successfully the configuration file: {}'.format(config_file))
236+
237+
# be sure the data types within the dictionaries are correct (json will only allow strings as keys)
238+
discreteInputs = prepareRegister(register = CONFIG['registers']['discreteInput'], initType='boolean', initializeUndefinedRegisters = CONFIG['registers']['initializeUndefinedRegisters'])
239+
coils=prepareRegister(register = CONFIG['registers']['coils'], initType='boolean', initializeUndefinedRegisters = CONFIG['registers']['initializeUndefinedRegisters'])
240+
holdingRegisters=prepareRegister(register = CONFIG['registers']['holdingRegister'], initType='word', initializeUndefinedRegisters = CONFIG['registers']['initializeUndefinedRegisters'])
241+
inputRegisters=prepareRegister(register = CONFIG['registers']['inputRegister'], initType='word', initializeUndefinedRegisters = CONFIG['registers']['initializeUndefinedRegisters'])
242+
221243
# try to get the interface IP address
222244
localIPAddr = get_ip_address()
223245
if localIPAddr != '': log.info('Outbund device IP address is: ' + localIPAddr)

src/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pip >= 23
2+
pymodbus >= 2, < 3

0 commit comments

Comments
 (0)