[我是Docker和Modbus的新手,我试图使用Modbus(准确地说是pymodbus工具包)编写自定义客户端/服务器应用程序,而我可能遇到了一些与docker有关的问题。
我正在使用的部分是:
服务器(泊坞窗)
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()
而且我只修改IP地址。我可以在localhost上运行它,但是当我将服务器部件放在Docker容器中时,我无法与其通信。
我正在使用docker-compose,带有.yml,如下所示:
version: '3.4'
services:
server:
build: ./server
#network_mode: host
expose:
- 5020
ports:
- 5020:5020
客户
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镜像开始正常,但是当我尝试运行客户端脚本时,我得到一个断言错误,该错误不同于failed-to-connect
错误,当没有容器在运行时。
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
我还试图使容器使用主机接口(带有network-mode: host
),并且可以正常工作。因此,我相信我的容器的网络设置中存在一些错误。据我所知,泊坞窗容器确实在指定端口上侦听,但我的脚本不是吗?
我很高兴知道我的错误在哪里,以及可能如何调试这些东西,我试图在tcpdump和docker network inspect
中找到某些东西,但是我找不到任何相关的东西。
[好吧,一段时间后,我意识到我正在从同一构建中重新部署Docker容器-因此,我实际上没有对服务器脚本IP进行任何更改,因为Docker始终使用第一个构建。现在我明白为什么我应该使用卷挂载而不是复制文件了。
我已将服务器IP设置为0.0.0.0(在所有IP上监听)重建了docker镜像,并且它按预期工作。希望至少这可以帮助某人节省一些时间。
首先,在与服务器的客户端连接中,不应使用本地主机(127.0.0.1
)IP,因为在使用dockerized服务器时,其IP不再是本地主机。因此,您有两种选择:
如果客户端和服务器使用同一台机器/主机,则必须使用Docker容器IP(How to get the docker container IP)或主机IP
否则,您必须使用唯一的主机IP。
[NOTE]:
您的docker-compose文件应如下所示:
version: '3.4'
services:
server:
build: ./server
container_name: modbus_server
network_mode: host
ports:
- 5020:5020
不需要expose
属性。
另外,如果您的服务器和客户端都被docker化,并且它们与docker-compose配置中的每一个都相关(使用links:
属性),则可以使用Modbus服务器容器名称(我选择了[ []中的C0]),而不是Modbus客户端中的IP。