宣武做網(wǎng)站百度推廣怎么登錄
python實(shí)現(xiàn)ModBusTCP協(xié)議的server是一件簡單的事情,只要通過pymodbus、pyModbusTCP等模塊都可以實(shí)現(xiàn),本文采用pymodbus。
相關(guān)文章見:
python實(shí)現(xiàn)ModBusTCP協(xié)議的client-CSDN博客
一、了解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、模塊的選用
StartAsyncTcpServer
和 StartTcpServer
是 pymodbus
庫中用于啟動(dòng) Modbus TCP 服務(wù)器的兩種不同的方法,其主要區(qū)別在于同步(Synchronous)和異步(Asynchronous)執(zhí)行方式。
(1)StartAsyncTcpServer(異步方式):
StartAsyncTcpServer
是一個(gè)異步函數(shù),它使用 Python 的 asyncio
模塊來實(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)用場景。如果需要處理大量并發(fā)連接并提高性能,可以選擇異步方式;如果簡單的同步處理能夠滿足需求,可以選擇同步方式。
本文采用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, ModbusServerContextif __name__ == "__main__":# Modbus TCP服務(wù)器的IP地址和端口號(hào)server_ip = "192.168.1.188"port = 502station = 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 = 10read_count = 1values = [115] # 觸發(fā)指令s 要設(shè)置的多個(gè)值列表,如[10, 20, 30]# 調(diào)用函數(shù),設(shè)置0地址為觸發(fā)指令sstore.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