Реализация pymodbus Modbus Server с контекстом нескольких подчиненных устройств - запись в регистр перезаписывает все подчиненные устройства
У меня проблема с простой реализацией сервера pymodbus. Из того, что я прочитал в документации, эта реализация должна иметь уникальные контексты подчиненного устройства для каждого подчиненного устройства, то есть запись в устройство 0x01, адрес регистра 1, должна отличаться от регистра устройства 0x02, регистра 1.
В моем случае запись в регистр 1 записывает в регистр 1 ВСЕ адреса подчиненных. Может ли кто-нибудь просмотреть код моего сервера, чтобы увидеть, не упустил ли я что-то, или, возможно, уточнить, насколько я понимаю, как сервер pymodbus должен работать с единственным флагом, установленным на False.
Ура. Код здесь:
#!/usr/bin/env python3
"""
Pymodbus Synchronous Server
--------------------------------------------------------------------------
This synced server is implemented using TCP, with multiple slave contexts
"""
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
def run_server():
slaves = {
0x01: ModbusSlaveContext(),
0x02: ModbusSlaveContext()
}
context = ModbusServerContext(slaves=slaves, single=False)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
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'
# ----------------------------------------------------------------------- #
# run the server
# ----------------------------------------------------------------------- #
StartTcpServer(context, identity=identity, address=("0.0.0.0", 5020))
if __name__ == "__main__":
run_server()
0 ответов
У меня была аналогичная проблема с сервером RTU, и у меня был более менее такой же код, как и у вас.
Но для меня это было то, что я не сделал отдельные объекты ModbusSlaveContext в подчиненном dict. Но в вашем коде это не так.
Я делюсь своим кодом здесь, может быть, это кому-то поможет.
Python code:
#!/usr/bin/env python
"""
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------
This is an example of having a background thread updating the
context while the server is operating. This can also be done with
a python thread::
from threading import Thread
thread = Thread(target=updating_writer, args=(context,))
thread.start()
"""
# --------------------------------------------------------------------------- #
# import the modbus libraries we need
# --------------------------------------------------------------------------- #
from pymodbus.server.asynchronous import StartSerialServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
# --------------------------------------------------------------------------- #
# import the payload builder
# --------------------------------------------------------------------------- #
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.payload import BinaryPayloadBuilder
# --------------------------------------------------------------------------- #
# import the twisted libraries we need
# --------------------------------------------------------------------------- #
from twisted.internet.task import LoopingCall
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# --------------------------------------------------------------------------- #
# define your callback process
# --------------------------------------------------------------------------- #
def updating_writer(a):
""" A worker process that runs every so often and
updates live values of the context. It should be noted
that there is a race condition for the update.
:param arguments: The input arguments to the call
"""
context = a[0]
register = 3
#### Write to registers slave 1 ####
slave_id = 0x01
log.debug(f"::: Make payload to SLAVE={slave_id} :::")
# Total energy
builder = BinaryPayloadBuilder(
byteorder=Endian.Big,
wordorder=Endian.Little
)
builder.add_32bit_int(20000) # kWh Tot*10
energy = builder.to_registers()
# Phase 1 variables
builder = BinaryPayloadBuilder(
byteorder=Endian.Big,
wordorder=Endian.Little
)
builder.add_32bit_int(4000) # VL1L2*10
builder.add_32bit_int(2300) # VL1N*10
builder.add_32bit_int(1000) # AL1*1000
builder.add_32bit_int(2300) # kWL1*10
phase1 = builder.to_registers()
log.debug(f"::: Write registers to SLAVE={slave_id} :::")
context[slave_id].setValues(register, 0x0112, energy)
context[slave_id].setValues(register, 0x011e, phase1)
context[slave_id].setValues(register, 0x000b, [0x0155])
#### Write to registers slave 100 ####
slave_id = 0x64
log.debug(f"::: Make payload to SLAVE={slave_id} :::")
# Total energy
builder = BinaryPayloadBuilder(
byteorder=Endian.Big,
wordorder=Endian.Little
)
builder.add_32bit_int(20000) # kWh Tot*10
energy = builder.to_registers()
# Phase 1 variables
builder = BinaryPayloadBuilder(
byteorder=Endian.Big,
wordorder=Endian.Little
)
builder.add_32bit_int(4000) # VL1L2*10
builder.add_32bit_int(2300) # VL1N*10
builder.add_32bit_int(2000) # AL1*1000
builder.add_32bit_int(4600) # kWL1*10
phase1 = builder.to_registers()
log.debug(f"::: Write registers to SLAVE={slave_id} :::")
context[slave_id].setValues(register, 0x0112, energy)
context[slave_id].setValues(register, 0x011e, phase1)
context[slave_id].setValues(register, 0x000b, [0x0155])
def run_updating_server():
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
addresses = [1, 100]
slaves = {}
for adress in addresses:
store = ModbusSlaveContext(zero_mode=True)
slaves.update({adress: store})
context = ModbusServerContext(slaves=slaves, single=False)
# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '2.3.0'
# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
time = 5 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartSerialServer(
context=context,
framer=ModbusRtuFramer,
identity=identity,
port='/dev/ttyUSB0',
timeout=0.0001,
baudrate=9600,
parity='N',
bytesize=8,
stopbits=1,
ignore_missing_slaves=True)
if __name__ == "__main__":
run_updating_server()