加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 一、了解pymodbus的Server
    • 二、一個(gè)Demo
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

python實(shí)現(xiàn)ModBusTCP協(xié)議的server

12/06 14:05
1548
閱讀需 12 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

python實(shí)現(xiàn)ModBusTCP協(xié)議的server是一件簡(jiǎn)單的事情,只要通過pymodbus、pyModbusTCP等模塊都可以實(shí)現(xiàn),本文采用pymodbus。

相關(guān)文章見:python實(shí)現(xiàn)ModBusTCP協(xié)議的client

一、了解pymodbus的Server

1、pymodbus.server的模塊

pymodbus.server中的模塊,能夠用于用于實(shí)現(xiàn) Modbus 協(xié)議的服務(wù)器端。以下是每個(gè)模塊的功能介紹:

(1)ModbusSerialServer: 這個(gè)模塊提供了一個(gè)基于串口的 Modbus 服務(wù)器。它允許通過串口與 Modbus 客戶端通信。

(2)ModbusTcpServer: 這個(gè)模塊提供了一個(gè)基于 TCP/IP 的 Modbus 服務(wù)器。它通過 TCP/IP 網(wǎng)絡(luò)接口與 Modbus 客戶端通信。

(3)ModbusTlsServer: 這個(gè)模塊提供了一個(gè)基于 TLS 加密的 Modbus 服務(wù)器。它通過安全的 TLS 連接與 Modbus 客戶端通信,確保通信的安全性。

(4)ModbusUdpServer: 這個(gè)模塊提供了一個(gè)基于 UDP 的 Modbus 服務(wù)器。與 TCP 不同,UDP 是一種面向無連接的協(xié)議,適用于某些特定的網(wǎng)絡(luò)環(huán)境。

(5)ServerAsyncStop: 這個(gè)類用于異步服務(wù)器的停止信號(hào)。通過發(fā)送這個(gè)信號(hào),可以優(yōu)雅地停止異步服務(wù)器的運(yùn)行。

(6)ServerStop: 這個(gè)類用于同步服務(wù)器的停止信號(hào)。與 ServerAsyncStop 類似,但用于同步服務(wù)器。

(7)StartAsyncSerialServer: 這個(gè)函數(shù)用于啟動(dòng)一個(gè)異步的基于串口的 Modbus 服務(wù)器。

(8)StartAsyncTcpServer: 這個(gè)函數(shù)用于啟動(dòng)一個(gè)異步的基于 TCP/IP 的 Modbus 服務(wù)器。

(9)StartAsyncTlsServer: 這個(gè)函數(shù)用于啟動(dòng)一個(gè)異步的基于 TLS 加密的 Modbus 服務(wù)器。

(10)StartAsyncUdpServer: 這個(gè)函數(shù)用于啟動(dòng)一個(gè)異步的基于 UDP 的 Modbus 服務(wù)器。

(11)StartSerialServer: 這個(gè)函數(shù)用于啟動(dòng)一個(gè)同步的基于串口的 Modbus 服務(wù)器。

(12)StartTcpServer: 這個(gè)函數(shù)期望用于啟動(dòng)一個(gè)同步的基于 TCP/IP 的 Modbus 服務(wù)器。(備注:源碼其實(shí)是異步的)

(13)StartTlsServer: 這個(gè)函數(shù)期望用于啟動(dòng)一個(gè)同步的基于 TLS 加密的 Modbus 服務(wù)器。(備注:源碼其實(shí)是異步的)

(14)StartUdpServer: 這個(gè)函數(shù)期望用于啟動(dòng)一個(gè)同步的基于 UDP 的 Modbus 服務(wù)器。(備注:源碼其實(shí)是異步的)

這些模塊和函數(shù)提供了多種不同類型的 Modbus 服務(wù)器的實(shí)現(xiàn)方式,可以根據(jù)具體的需求選擇合適的模塊和函數(shù)來創(chuàng)建和啟動(dòng) Modbus 服務(wù)器。

2、模塊的選用

StartAsyncTcpServerStartTcpServerpymodbus 庫中用于啟動(dòng) Modbus TCP 服務(wù)器的兩種不同的方法,其主要區(qū)別在于同步(Synchronous)和異步(Asynchronous)執(zhí)行方式。

(1)StartAsyncTcpServer(異步方式):

StartAsyncTcpServer 是一個(gè)異步函數(shù),它使用 Pythonasyncio 模塊來實(shí)現(xiàn)異步的 Modbus TCP 服務(wù)器。在異步編程中,事件循環(huán)(event loop)可以處理多個(gè)任務(wù),使得程序在等待某些耗時(shí)操作(比如 I/O 操作)時(shí)不會(huì)被阻塞。這意味著它可以同時(shí)處理多個(gè)客戶端連接,提高了服務(wù)器的并發(fā)性能。

該函數(shù)的調(diào)用方式是異步的,需要在異步上下文(比如異步函數(shù)內(nèi)部或者異步腳本中)使用。

(2)StartTcpServer(同步方式):

StartTcpServer 是一個(gè)同步函數(shù),它使用 Python 的標(biāo)準(zhǔn)同步執(zhí)行方式,會(huì)阻塞當(dāng)前線程。在同步模式下,每次只能處理一個(gè)客戶端連接。這意味著服務(wù)器只能順序處理客戶端請(qǐng)求,一個(gè)接一個(gè)地處理。

該函數(shù)的調(diào)用方式是同步的,可以在任何地方調(diào)用。

總之,選擇哪種方式取決于你的應(yīng)用場(chǎng)景。如果需要處理大量并發(fā)連接并提高性能,可以選擇異步方式;如果簡(jiǎn)單的同步處理能夠滿足需求,可以選擇同步方式。

本文采用StartAsyncTcpServer,所以他涉及到的模塊是StartTcpServer,源碼如下:

def StartTcpServer(**kwargs):  # pylint: disable=invalid-name
    """Start and run a serial modbus server."""
    return asyncio.run(StartAsyncTcpServer(**kwargs))

如果要停止異步,需要調(diào)用ServerAsyncStop模塊。

二、一個(gè)Demo

1、服務(wù)端讀寫自身的保持寄存器的示例

import asyncio
import threading
import time
import json
from pymodbus.server import StartTcpServer, ServerAsyncStop
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext


if __name__ == "__main__":
    # Modbus TCP服務(wù)器的IP地址和端口號(hào)
    server_ip = "192.168.1.188"
    port = 502
    station = 1

    # 創(chuàng)建一個(gè)數(shù)據(jù)存儲(chǔ)區(qū),用于存儲(chǔ)從客戶端讀取的數(shù)據(jù)
    store = ModbusSlaveContext(
        hr=ModbusSequentialDataBlock(0, [0] * 100)
    )

    # 創(chuàng)建一個(gè)服務(wù)器上下文,用于處理客戶端的請(qǐng)求
    context = ModbusServerContext(slaves=store, single=True)
    # 啟動(dòng)ModBusTCP服務(wù)器
    # 創(chuàng)建并啟動(dòng)線程(啟動(dòng)異步服務(wù)器)
    # StartTcpServer(context=context, address=(server_ip, port))
    modbus_server_thread = threading.Thread(target=StartTcpServer, kwargs=({"context":context, "address":(server_ip, port)}))
    modbus_server_thread.start()

    # 設(shè)置保持寄存器的0地址的值為s
    # 定義函數(shù)參數(shù)
    fc_as_hex = 0x03  # 功能碼,例如讀保持寄存器是"0x03"
    write_address = 0  # 起始地址
    read_address = 10
    read_count = 1
    values = [115]  # 觸發(fā)指令s 要設(shè)置的多個(gè)值列表,如[10, 20, 30]

    # 調(diào)用函數(shù),設(shè)置0地址為觸發(fā)指令s
    store.setValues(fc_as_hex, write_address, values)
    # 獲取保持寄存器的值并打印
    hr_values = store.getValues(3, read_address, count=read_count)
    print("Hold Register Values:", hr_values)

    assert hr_values == [0], print("測(cè)試失敗!")

    time.sleep(10)

    try:
        print("停止服務(wù)器")
        asyncio.run(ServerAsyncStop())  # 停止服務(wù)器
    except:
        pass

2、實(shí)際協(xié)議幀解析

上一章節(jié)我們說到協(xié)議幀的格式,現(xiàn)在我們用上一章節(jié)的內(nèi)容解析一下實(shí)際協(xié)議幀的情況。

(1)一個(gè)完整的收發(fā)協(xié)議幀

[2023-11-02 10:45:31-315]NET001-發(fā)送:[00 23 00 00 00 0b] 01 10 00 00 00 02 04 73 00 00 00 [2023-11-02 10:45:31-317]NET001-接收:[00 23 00 00 00 06] 01 10 00 00 00 02

(2)發(fā)送

這條消息的結(jié)構(gòu)可以分解為:

  • [00 23 00 00 00 0b]: 幀頭,pymodbus自動(dòng)封裝好,不用自己寫,表示消息的起始標(biāo)志和長度。依次包含事務(wù)標(biāo)識(shí)符(2字節(jié)協(xié)議標(biāo)識(shí)符(2字節(jié)、長度字段(2字節(jié)。
  • 01: 請(qǐng)求目標(biāo)站號(hào)。
  • 10: 命令碼(功能碼),這里表示寫入多個(gè)寄存器。
  • 00 00: 請(qǐng)求的寄存器起始地址。
  • 00 02: 請(qǐng)求的寄存器數(shù)量。
  • 04: 請(qǐng)求數(shù)據(jù)的字節(jié)數(shù)。
  • 73 00 00 00: 具體的請(qǐng)求數(shù)據(jù),以十六進(jìn)制表示。

(3)接收

這條消息的結(jié)構(gòu)可以分解為:

  • [00 23 00 00 00 06]: 幀頭,表示消息的起始標(biāo)志和長度。
  • 01: 請(qǐng)求目標(biāo)站號(hào)。
  • 10: 命令碼(功能碼),這里表示寫入多個(gè)寄存器。
  • 00 00: 請(qǐng)求的寄存器起始地址。
  • 00 02: 請(qǐng)求的寄存器數(shù)量。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜