Спасибо Маркеру и всем примерам онлайн. Я, наконец, получил это работает, как я хотел. Надеюсь, это поможет кому-то еще.
Было несколько ошибок, с которыми я столкнулся:
- Я попробовал следующие примеры, которые я нашел в Интернете, и все они использовали pymodbus.server.async вместо pymodbus.server.sync. Я обнаружил, что не могу импортировать pymodbus.server.async, потому что «async» - зарезервированное слово в Python3.7! (не в более старых версиях Python). В любом случае я хотел использовать pymodbus.server.sync, потому что я хотел избежать импорта витой, если это вообще возможно. К этому серверу будет подключено не более 1-3 клиентов.
- Все примеры, показывающие, что писатель обновлений использовал "LoopingCall" из Twisted. Я понятия не имею, что такое Twisted, и я не хотел использовать его, если бы мне не пришлось. Я был знаком с многопроцессорностью и многопоточностью. Я уже запускал ModbusTcpServer в процессе и пытался создать управляемый объект (ы) вокруг хранилища / контекста, чтобы у меня был другой процесс, выполняющий обновление. Но это не сработало: я предполагаю, что StartTcpServer не нравится получать управляемые объекты (?), И я не хотел углубляться в эту функцию.
- В одном из примеров отмечалось, что можно использовать поток Python, и это решает эту проблему. У меня все еще есть ModbusTcpServer, запущенный в Process, но прямо перед вызовом «StartTcpServer» я запускаю THREAD, а не PROCESS с модулем обновления. Тогда мне не нужно было помещать хранилище / контекст в управляемый объект (ы), поскольку поток может видеть то же пространство данных, что и процесс, который его запустил. Мне просто нужен ДРУГОЙ управляемый объект для отправки сообщений в этот поток, как я уже привык делать с процессом.
Тааак ...
Сначала я должен был сделать это:
from threading import Thread
Затем я запустил следующее в Process, как я делал раньше, но ПРЯМО ПЕРЕД вызовом StartTcpServer я запустил нить update_writer (все переменные start_addr, init_val и num_addrs установлены ранее).
discrete_inputs_obj = ModbusSequentialDataBlock(di_start_addr, [di_init_val]*di_num_addrs)
coils_obj = ModbusSequentialDataBlock(co_start_addr, [co_init_val]*co_num_addrs)
holding_regs_obj = ModbusSequentialDataBlock(hr_start_addr, [hr_init_val]*hr_num_addrs)
input_regs_obj = ModbusSequentialDataBlock(ir_start_addr, [ir_init_val]*ir_num_addrs)
mb_store = ModbusSlaveContext(di=discrete_inputs_obj, co=coils_obj, hr=holding_regs_obj, ir=input_regs_obj, zero_mode=True)
mb_context = ModbusServerContext(slaves=mb_store, single=True)
mb_store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(di_start_addr, [di_init_val]*di_num_addrs),
co=ModbusSequentialDataBlock(co_start_addr, [co_init_val]*co_num_addrs),
hr=ModbusSequentialDataBlock(hr_start_addr, [hr_init_val]*hr_num_addrs),
ir=ModbusSequentialDataBlock(ir_start_addr, [ir_init_val]*ir_num_addrs))
mb_context = ModbusServerContext(slaves=mb_store, single=True)
updating_writer_cfg = {}
updating_writer_cfg["mb_context"] = mb_context
updating_writer_cfg["managed_obj"] = managed_obj #For being able to send messages to this Thread
updating_writer_thread = Thread(target = updating_writer, args = [updating_writer_cfg]) # We need this to be a thread in this process so that they can share the same datastore
updating_writer_thread.start()
StartTcpServer(mb_context, address=("", port))
В цикле while update_writer у меня есть код, который опрашивает managed_obj для получения сообщений. При добавлении ключевых битов кода в этом цикле:
mb_context[0].setValues(4, addr_to_write, regs_to_write)
... где 4 - функция записи, addr_to_write - адрес регистра, с которого начинается запись, а regs_to_write - список значений регистра ... AND ...
regs_to_read = mb_context[0].getValues(3, addr_to_read, num_regs_to_read)
... где 3 - функция чтения, addr_to_read - адрес регистра, с которого начинается чтение. regs_to_read будет списком длины num_regs_to_read.