簡體   English   中英

如何使用pymodbus編寫用於外部Modbus客戶端的INPUT寄存器,它將讀取它們

[英]How to write INPUT REGISTERS using pymodbus for external Modbus client which will read them

我的任務是實現基於pymodbus的Modbus服務器。 該服務器將在Raspberry Pi或Up2控制器等Linux機器上運行。 預計將與我無法控制的Modbus客戶端對接。 該外部Modbus客戶端期望能夠讀取輸入寄存器以及保存由我的Modbus服務器提供的寄存器。

我可以設置將由外部客戶端讀取的HOLDING寄存器的值。 我無法設置外部客戶端將讀取的INPUT寄存器的值。 怎么做到的?

我看到這篇文章問了類似的問題,但似乎從未有人回答過這個問題:

如何使用pymodbus寫入PLC輸入寄存器

在此先感謝您的幫助!

就像我說的那樣,我對python或pymodbus不熟悉,但是請看一下這個示例,它與我預期的存在類似: https ://pymodbus.readthedocs.io/en/latest/source/example/updating_server 。 HTML

創建四個100個“寄存器”陣列作為數據存儲。 我假設di =數字輸入,co =線圈,hr =保持寄存器,ir =輸入寄存器

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)

然后在后台線程調用的“ updating_writer(a)”中更新這些值。 在我看來,它每次調用時都會對每個值加1。 在實際的PLC中,此功能可能會讀取諸如傳感器,設置以及其他操作/狀態/配置數據之類的信息。

感謝Marker和所有在線示例。 我終於按需完成了這項工作。 希望這對其他人有幫助。

我遇到了幾個陷阱:

  1. 我嘗試了以下示例,這些示例在網上找到,所有示例都使用pymodbus.server.async而不是pymodbus.server.sync。 我發現我無法導入pymodbus.server.async,因為“異步”是Python3.7中的保留字! (不適用於舊版本的Python)。 無論哪種方式,我都想使用pymodbus.server.sync,因為我想盡可能避免導入twisted。 該服務器最多將有1-3個客戶端與其連接。
  2. 所有顯示更新編寫器的示例都使用了Twisted的“ LoopingCall”。 我不知道Twisted是什么,除非我必須,否則不想使用它。 我熟悉多處理和線程。 我已經在一個進程中啟動了ModbusTcpServer,並試圖在商店/上下文周圍創建托管對象,因此我可以讓另一個進程來進行更新。 但這沒有用:我猜想StartTcpServer不喜歡接收托管對象(?),並且我不想深入研究該函數。
  3. 其中一個示例評論說可以使用Python線程,從而解決了該問題。 我仍然在進程中啟動了ModbusTcpServer,但是在我調用“ StartTcpServer”之前,我用更新編寫器啟動了一個線程而不是一個過程。 然后,我不需要將存儲/上下文放在托管對象中,因為線程可以看到與啟動它的進程相同的數據空間。 我只需要一個ANANT托管對象就可以像以前使用Process一樣將消息發送到該線程中。

SOOO ...

首先,我必須這樣做: from threading import Thread

然后,我像以前一樣在進程中開始以下操作,但是在調用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))

在update_writer的While循環中,我有代碼輪詢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的列表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM