• 正文
    • 一、一個(gè)Demo及其引發(fā)的問(wèn)題
    • 二、pymodbus相關(guān)文檔
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

python實(shí)現(xiàn)ModBusRTU服務(wù)端

2024/12/06
4875
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

python實(shí)現(xiàn)基于串口通信的ModBusRTU服務(wù)端是一件簡(jiǎn)單的事情,只要通過(guò)pymodbus模塊就可以實(shí)現(xiàn)。

一、一個(gè)Demo及其引發(fā)的問(wèn)題

1、一個(gè)Demo

import asyncio
import json
import threading
import time
from pymodbus.server import StartSerialServer, ServerAsyncStop
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusServerContext, ModbusSlaveContext
from pymodbus.transaction import ModbusRtuFramer
from Application.common.private_socket import Request

if __name__ == "__main__":
    # 定義串口配置
    port = "COM46"
    # Serial(port="COM46", baudrate=115200, timeout=2, bytesize=8, parity="N", stopbits=1)
    framer = ModbusRtuFramer

    # 創(chuàng)建數(shù)據(jù)存儲(chǔ)區(qū)
    # data_block = ModbusSequentialDataBlock(0, [0] * 100)  # 100個(gè)保持寄存器?
    data_block = ModbusSlaveContext(
        hr=ModbusSequentialDataBlock(0, [0] * 100)
    )
    # store = ModbusSlaveContext(hr=data_block)
    # context = ModbusServerContext(slaves=store)
    context = ModbusServerContext(slaves={1: data_block}, single=False)

    # 啟動(dòng)Modbus RTU服務(wù)器
    # StartSerialServer(context=context, framer=framer, port="COM46")
    # thread = threading.Thread(target=StartSerialServer, kwargs={"context": context, "framer": framer, "port": port, "baudrate":9600, "timeout":2, "bytesize":8, "parity":"N", "stopbits":1})
    thread = threading.Thread(target=StartSerialServer, kwargs={"context": context, "framer": framer, "port": port, "baudrate":9600})
    thread.start()
    # 設(shè)置保持寄存器的0地址的值為s
    # 定義函數(shù)參數(shù)
    write_address = 0  # 起始地址
    read_address = 10
    read_count = 10
    values = [115]  # 觸發(fā)指令s 要設(shè)置的多個(gè)值列表,如[10, 20, 30]

    # 調(diào)用函數(shù),設(shè)置0地址為觸發(fā)指令s
    data_block.setValues(16, write_address, values)

    while True:
        time.sleep(3)
        # 獲取保持寄存器的值并打印
        hr_values = data_block.getValues(3, 0, count=20)

        print("Hold Register Values:", hr_values)
    asyncio.run(ServerAsyncStop())  # 停止服務(wù)器

在這里簡(jiǎn)單說(shuō)明上述程序涉及到的對(duì)象:

(1)ModbusRtuFramer的作用

在 Modbus 通信協(xié)議中,數(shù)據(jù)以幀的形式進(jìn)行傳輸。ModbusRtuFramer 是 pymodbus 庫(kù)中的一個(gè)類(lèi),它的作用是負(fù)責(zé)處理 Modbus RTU 幀的編碼和解碼。

具體來(lái)說(shuō),ModbusRtuFramer 完成了以下幾個(gè)主要任務(wù):

①編碼(封裝)Modbus RTU 幀:當(dāng)你需要發(fā)送 Modbus RTU 請(qǐng)求或響應(yīng)時(shí),ModbusRtuFramer 負(fù)責(zé)將請(qǐng)求或響應(yīng)的數(shù)據(jù)按照 Modbus RTU 協(xié)議的格式進(jìn)行封裝,生成符合 Modbus RTU 規(guī)范的幀,以便發(fā)送到 Modbus 設(shè)備。

②解碼(解析)Modbus RTU 幀:當(dāng)你從 Modbus 設(shè)備接收到數(shù)據(jù)時(shí),ModbusRtuFramer 負(fù)責(zé)將接收到的二進(jìn)制數(shù)據(jù)按照 Modbus RTU 協(xié)議的格式進(jìn)行解析,提取出請(qǐng)求或響應(yīng)的數(shù)據(jù),以便進(jìn)行后續(xù)的處理和分析。

③錯(cuò)誤檢測(cè)和糾正:ModbusRtuFramer 也負(fù)責(zé)檢測(cè) Modbus RTU 幀中的錯(cuò)誤,比如奇偶校驗(yàn)錯(cuò)誤、幀起始符和結(jié)束符錯(cuò)誤等。如果幀中存在錯(cuò)誤,ModbusRtuFramer 會(huì)幫助你識(shí)別錯(cuò)誤的位置和類(lèi)型,便于進(jìn)行糾正或錯(cuò)誤處理。

總之,ModbusRtuFramer 是一個(gè)處理 Modbus RTU 幀的工具,它確保了在 Modbus RTU 通信中,數(shù)據(jù)的正確封裝和解析,以及錯(cuò)誤的檢測(cè)和處理。

(2)ModbusSequentialDataBlock、ModbusSlaveContext與ModbusServerContext的作用

在 pymodbus 庫(kù)中,ModbusSequentialDataBlock、ModbusSlaveContext 和 ModbusServerContext 是用來(lái)處理 Modbus 數(shù)據(jù)存儲(chǔ)和上下文的類(lèi),它們的作用如下:

①M(fèi)odbusSequentialDataBlock:

ModbusSequentialDataBlock 是一個(gè)用于創(chuàng)建順序排列的 Modbus 寄存器塊的類(lèi)。
它通常用于模擬設(shè)備的保持寄存器(Holding Registers)或輸入寄存器(Input Registers)。
這個(gè)類(lèi)允許你定義寄存器的起始地址和初始化寄存器的值。
例如:ModbusSequentialDataBlock(0, [0] * 100) 創(chuàng)建了一個(gè)從地址 0 開(kāi)始,包含 100 個(gè)初始值為 0 的保持寄存器的塊。

②ModbusSlaveContext:

ModbusSlaveContext 是一個(gè)用于表示 Modbus 從設(shè)備的類(lèi)。
它包含一個(gè)或多個(gè)數(shù)據(jù)存儲(chǔ)塊(比如保持寄存器塊、輸入寄存器塊等)。
這個(gè)類(lèi)可以用來(lái)創(chuàng)建一個(gè)模擬的 Modbus 從設(shè)備上下文。
你可以在這個(gè)上下文中添加多個(gè)不同類(lèi)型的數(shù)據(jù)塊,模擬一個(gè)完整的 Modbus 從設(shè)備。

③ModbusServerContext:

ModbusServerContext 是一個(gè)用于表示整個(gè) Modbus 服務(wù)器的類(lèi)。
它包含一個(gè)或多個(gè) Modbus 從設(shè)備的上下文(ModbusSlaveContext 實(shí)例)。
這個(gè)類(lèi)可以用來(lái)創(chuàng)建一個(gè)完整的 Modbus 服務(wù)器環(huán)境,包含多個(gè)模擬的 Modbus 從設(shè)備。

在搭建 Modbus 通信環(huán)境時(shí),你通常會(huì)創(chuàng)建 ModbusSequentialDataBlock 實(shí)例作為寄存器的存儲(chǔ),然后將它們添加到 ModbusSlaveContext 中。最后,將多個(gè) ModbusSlaveContext 實(shí)例添加到 ModbusServerContext 中,以構(gòu)建一個(gè)包含多個(gè)從設(shè)備的 Modbus 服務(wù)器環(huán)境。這樣的架構(gòu)可以讓你模擬多個(gè)不同類(lèi)型的 Modbus 從設(shè)備。

(3)context = ModbusServerContext(slaves={1: data_block}, single=False)的解釋

在這個(gè)代碼行中,你創(chuàng)建了一個(gè) ModbusServerContext 對(duì)象,該對(duì)象用于模擬一個(gè) Modbus 服務(wù)器的上下文。ModbusServerContextpymodbus 模塊中的一個(gè)類(lèi),它用于存儲(chǔ)和管理 Modbus 服務(wù)器的數(shù)據(jù)。在這個(gè)特定的代碼行中,你傳遞了一些參數(shù)給 ModbusServerContext 構(gòu)造函數(shù):

  • slaves={1: data_block}: 這個(gè)參數(shù)是一個(gè)字典,表示 Modbus 服務(wù)器的從設(shè)備。在這里,你創(chuàng)建了一個(gè)從設(shè)備,其 Modbus 地址為 1,并且將這個(gè)從設(shè)備關(guān)聯(lián)到了一個(gè) data_block 對(duì)象上。data_block 可能是一個(gè) ModbusSequentialDataBlock 對(duì)象,用于存儲(chǔ) Modbus 寄存器的數(shù)據(jù)。
  • single=False: 這個(gè)參數(shù)用于確定是否將所有的從設(shè)備視為一個(gè)整體。當(dāng) single=True 時(shí),所有的從設(shè)備共享相同的 Modbus 地址空間,它們沒(méi)有獨(dú)立的地址范圍。而當(dāng) single=False 時(shí),每個(gè)從設(shè)備都有獨(dú)立的 Modbus 地址空間,它們的地址范圍是相互獨(dú)立的。

所以,這一行代碼的意義是創(chuàng)建了一個(gè) ModbusServerContext 對(duì)象,該對(duì)象包含一個(gè)從設(shè)備(Modbus 地址為 1),并且這個(gè)從設(shè)備擁有獨(dú)立的 Modbus 地址空間。這個(gè)上下文可以在模擬多個(gè)獨(dú)立的 Modbus 設(shè)備時(shí)使用。

(4)關(guān)于StartSerialServer啟動(dòng)服務(wù)器時(shí)必須的參數(shù)

必須傳入以下參數(shù)才能正常啟動(dòng)

  • "context": context 表示傳入的 Modbus 上下文。
  • "framer": framer 表示 Modbus 使用的幀格式。
  • "port": port 表示串口號(hào),例如 "COM46"。
  • "baudrate": baudrate表示波特率,即每秒傳輸?shù)奈粩?shù)。

這些參數(shù)將被傳遞給 StartSerialServer 函數(shù),用于啟動(dòng) Modbus RTU 服務(wù)器。

這需要根據(jù)客戶(hù)端的設(shè)置來(lái)對(duì)應(yīng)設(shè)置。

2、權(quán)限問(wèn)題

建議使用管理員權(quán)限運(yùn)行腳本。

3、端口占用問(wèn)題

確保端口沒(méi)有被其他軟件占用。

4、pymodbus版本問(wèn)題

網(wǎng)上的示例代碼,StartSerialServer要求傳入serial模塊的Serial(port=COM, baudrate=Baudrate, timeout=2, bytesize=Databits, parity=Parity, stopbits=Stopbits)實(shí)例,我不確定是哪個(gè)版本,你可以試一下,目前在新版本只要傳入波特率和端口即可。

二、pymodbus相關(guān)文檔

1、官方文檔地址

Datastore — PyModbus 3.6.0dev documentation

2、Datastore對(duì)象

Datastore is responsible for managing registers for a server.

(1)pymodbus.datastore.ModbusSparseDataBlock

pymodbus.datastore.ModbusSparseDataBlock 是 PyModbus 庫(kù)中用于創(chuàng)建稀疏數(shù)據(jù)塊(sparse data block)的類(lèi)。在 Modbus 協(xié)議中,數(shù)據(jù)通常被組織成多個(gè)數(shù)據(jù)塊,而每個(gè)數(shù)據(jù)塊包含一定數(shù)量的數(shù)據(jù)寄存器或者線圈。

ModbusSparseDataBlock 允許您創(chuàng)建包含不連續(xù)地址的數(shù)據(jù)塊。具體來(lái)說(shuō),您可以在數(shù)據(jù)塊中指定特定地址的數(shù)據(jù),而無(wú)需為數(shù)據(jù)塊的每個(gè)地址都分配內(nèi)存。這種方式可以有效地節(jié)省內(nèi)存空間,尤其是在處理大量數(shù)據(jù)時(shí)。

以下是 ModbusSparseDataBlock 的初始化參數(shù):

  • values:一個(gè)字典,包含要存儲(chǔ)的數(shù)據(jù)。字典的鍵是地址,值是相應(yīng)地址的數(shù)據(jù)值。
  • address:數(shù)據(jù)塊的起始地址。
  • size:數(shù)據(jù)塊的大小,即包含的地址數(shù)量。

相關(guān)推薦