I'm a newbie to either docker and Modbus, I am trying to write custom client/server application using Modbus (pymodbus toolkit to be precise), and I run into some issues probably with docker.
I've tried sample scripts client , server
The parts I'm using are:
Server (dockerized)
from pymodbus.server.sync import StartTcpServer
from pymodbus.server.sync import StartTlsServer
from pymodbus.server.sync import StartUdpServer
from pymodbus.server.sync import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSparseDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
def run_server():
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
identity.ProductName = 'Pymodbus Server'
identity.ModelName = 'Pymodbus Server'
identity.MajorMinorRevision = '2.3.0'
StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020))
if __name__ == "__main__":
print("running")
run_server()
And I've tinker only with IP addresses. I can get it working on localhost, but when I put the server part in Docker container, I'm not able to communicate with it.
I am using docker-compose, with .yml as follows:
version: '3.4'
services:
server:
build: ./server
#network_mode: host
expose:
- 5020
ports:
- 5020:5020
Client
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
# from pymodbus.client.sync import ModbusUdpClient as ModbusClient
# from pymodbus.client.sync import ModbusSerialClient as ModbusClient
# --------------------------------------------------------------------------- #
# configure the client logging
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
UNIT = 0x1
def run_sync_client():
client = ModbusClient('127.0.0.1', port=5020)
client.connect()
log.debug("Reading Coils")
rr = client.read_coils(1, 1, unit=UNIT)
log.debug(rr)
log.debug("Write to a Coil and read back")
rq = client.write_coil(0, True, unit=UNIT)
rr = client.read_coils(0, 1, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.bits[0] == True) # test the expected value
log.debug("Write to multiple coils and read back- test 1")
rq = client.write_coils(1, [True]*8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
rr = client.read_coils(1, 21, unit=UNIT)
assert(not rr.isError()) # test that we are not an error
resp = [True]*21
resp.extend([False]*3)
assert(rr.bits == resp) # test the expected value
log.debug("Write to multiple coils and read back - test 2")
rq = client.write_coils(1, [False]*8, unit=UNIT)
rr = client.read_coils(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.bits == [False]*8) # test the expected value
log.debug("Read discrete inputs")
rr = client.read_discrete_inputs(0, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
log.debug("Write to a holding register and read back")
rq = client.write_register(1, 10, unit=UNIT)
rr = client.read_holding_registers(1, 1, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.registers[0] == 10) # test the expected value
log.debug("Write to multiple holding registers and read back")
rq = client.write_registers(1, [10]*8, unit=UNIT)
rr = client.read_holding_registers(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rr.registers == [10]*8) # test the expected value
log.debug("Read input registers")
rr = client.read_input_registers(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
arguments = {
'read_address': 1,
'read_count': 8,
'write_address': 1,
'write_registers': [20]*8,
}
log.debug("Read write registeres simulataneously")
rq = client.readwrite_registers(unit=UNIT, **arguments)
rr = client.read_holding_registers(1, 8, unit=UNIT)
assert(not rq.isError()) # test that we are not an error
assert(rq.registers == [20]*8) # test the expected value
assert(rr.registers == [20]*8) # test the expected value
client.close()
if __name__ == "__main__":
run_sync_client()
Docker image starts ok, but when I try to run the client script, I get an assertion error, which is different from failed-to-connect
error, when no container is running.
2019-11-11 16:57:36,456 MainThread DEBUG client :83 Reading Coils
2019-11-11 16:57:36,456 MainThread DEBUG transaction :115 Current transaction state - IDLE
2019-11-11 16:57:36,456 MainThread DEBUG transaction :120 Running transaction 1
2019-11-11 16:57:36,457 MainThread DEBUG transaction :219 SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x1 0x0 0x1 0x0 0x1
2019-11-11 16:57:36,457 MainThread DEBUG sync :75 New Transaction state 'SENDING'
2019-11-11 16:57:36,457 MainThread DEBUG transaction :228 Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-11-11 16:57:41,460 MainThread DEBUG transaction :238 Transaction failed. (Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received))
2019-11-11 16:57:41,460 MainThread DEBUG socket_framer :147 Processing:
2019-11-11 16:57:41,460 MainThread DEBUG transaction :394 Getting transaction 1
2019-11-11 16:57:41,460 MainThread DEBUG transaction :193 Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
2019-11-11 16:57:41,460 MainThread DEBUG client :85 Modbus Error: [Input/Output] Modbus Error: [Invalid Message] Incomplete message received, expected at least 8 bytes (0 received)
2019-11-11 16:57:41,460 MainThread DEBUG client :100 Write to a Coil and read back
2019-11-11 16:57:41,461 MainThread DEBUG transaction :115 Current transaction state - TRANSACTION_COMPLETE
2019-11-11 16:57:41,461 MainThread DEBUG transaction :120 Running transaction 2
2019-11-11 16:57:41,461 MainThread DEBUG transaction :219 SEND: 0x0 0x2 0x0 0x0 0x0 0x6 0x1 0x5 0x0 0x0 0xff 0x0
2019-11-11 16:57:41,461 MainThread DEBUG sync :75 New Transaction state 'SENDING'
2019-11-11 16:57:41,461 MainThread DEBUG transaction :228 Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-11-11 16:57:45,001 MainThread DEBUG transaction :304 Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2019-11-11 16:57:45,001 MainThread DEBUG transaction :233 RECV:
2019-11-11 16:57:45,001 MainThread DEBUG socket_framer :147 Processing:
2019-11-11 16:57:45,001 MainThread DEBUG transaction :394 Getting transaction 2
2019-11-11 16:57:45,001 MainThread DEBUG transaction :193 Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
2019-11-11 16:57:45,001 MainThread DEBUG transaction :115 Current transaction state - TRANSACTION_COMPLETE
2019-11-11 16:57:45,001 MainThread DEBUG transaction :120 Running transaction 3
2019-11-11 16:57:45,001 MainThread DEBUG transaction :219 SEND: 0x0 0x3 0x0 0x0 0x0 0x6 0x1 0x1 0x0 0x0 0x0 0x1
2019-11-11 16:57:45,001 MainThread DEBUG sync :75 New Transaction state 'SENDING'
2019-11-11 16:57:45,002 MainThread DEBUG transaction :238 Transaction failed. ([Errno 32] Broken pipe)
2019-11-11 16:57:45,002 MainThread DEBUG socket_framer :147 Processing:
2019-11-11 16:57:45,002 MainThread DEBUG transaction :394 Getting transaction 3
2019-11-11 16:57:45,002 MainThread DEBUG transaction :193 Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Traceback (most recent call last):
File "client.py", line 166, in <module>
run_sync_client()
File "client.py", line 103, in run_sync_client
assert(not rq.isError()) # test that we are not an error
AssertionError
I also tried to make the container use the host interface (with network-mode: host
) and that works just ok. Therefore I believe I've got some mistakes in network setup of my container. From what I can piece together, docker container does listen on the specified port, but my script is not?
I would be glad to know where my mistake is and possibly how to debug these things, i've tried to found something in tcpdump and docker network inspect
, but I couldn't find anything relevant.