python實(shí)現(xiàn)MC協(xié)議(SLMP 3E幀)的TCP服務(wù)端是一件稍微麻煩點(diǎn)的事情。它不像modbusTCP那樣,可以使用現(xiàn)成的pymodbus模塊去實(shí)現(xiàn)。但是,我們可以根據(jù)協(xié)議幀進(jìn)行組包,自己去實(shí)現(xiàn)幀的格式,而這一切可以基于socket模塊。本文為第二篇。
二、讀寫保持寄存器的完整交互包
# 客戶端發(fā)送(讀) -》
50 00 00 FF FF 03 00 0C 00 10 00 01 04 00 00 00 00 00 A8 05 00
# 《- 服務(wù)端應(yīng)答
D0 00 00 FF FF 03 00 0C 00 00 00 73 00 00 00 00 00 00 00 00 00
# 客戶端發(fā)送(寫) -》
50 00 00 FF FF 03 00 16 00 10 00 01 14 00 00 0A 00 00 A8 05 00 4E 47 00 00 00 00 00 00 00 00
# 《- 服務(wù)端應(yīng)答
D0 00 00 FF FF 03 00 02 00 00 00
1、分析交互包
基于上述交互包,我們查閱官方文檔發(fā)現(xiàn)交互包使用的是二進(jìn)制代碼。那么,二進(jìn)制代碼與ASCII代碼有什么區(qū)別呢?
SLMP(Seamless Message Protocol)3E幀有兩種表示方式:二進(jìn)制格式和ASCII格式。它們的區(qū)別在于數(shù)據(jù)的傳輸方式和呈現(xiàn)形式。
(1)二進(jìn)制格式:
在二進(jìn)制格式中,SLMP 3E幀中的各個(gè)字段(如幀頭、副幀頭、命令碼、數(shù)據(jù)等)以二進(jìn)制形式直接編碼和傳輸。數(shù)據(jù)在網(wǎng)絡(luò)中以原始的二進(jìn)制位模式傳輸,這種方式效率較高,適用于網(wǎng)絡(luò)傳輸。二進(jìn)制格式通常用于實(shí)際的網(wǎng)絡(luò)通信中,數(shù)據(jù)以二進(jìn)制流的形式在網(wǎng)絡(luò)上傳輸。
(2)ASCII格式:
在ASCII格式中,SLMP 3E幀中的各個(gè)字段被轉(zhuǎn)換成ASCII字符表示。數(shù)據(jù)以ASCII碼的文本形式進(jìn)行傳輸,每個(gè)字節(jié)被轉(zhuǎn)換為兩個(gè)ASCII字符(通常是十六進(jìn)制表示)。ASCII格式通常用于調(diào)試和人機(jī)界面中,方便人們查看和理解數(shù)據(jù)。
總的來(lái)說(shuō),二進(jìn)制格式適用于機(jī)器之間的網(wǎng)絡(luò)通信,而ASCII格式適用于人機(jī)交互和調(diào)試過(guò)程中的數(shù)據(jù)顯示。選擇哪種格式取決于具體的應(yīng)用場(chǎng)景和需求。
因此,本文實(shí)現(xiàn)的是二進(jìn)制格式,如果你會(huì)實(shí)現(xiàn)二進(jìn)制格式,那么你也能實(shí)現(xiàn)ASCII格式。
2、讀寫保持寄存器的請(qǐng)求處理
(1)表頭
客戶端的兩個(gè)請(qǐng)求,相同部分都為50 00 00 FF FF 03 00,我們姑且稱之為表頭。
(2)讀/寫長(zhǎng)度(協(xié)議幀的長(zhǎng)度)
0C 00是固定長(zhǎng)度(讀的時(shí)候報(bào)文都是這么長(zhǎng))與16 00 根據(jù)實(shí)際長(zhǎng)度變化,表示后面數(shù)據(jù)的長(zhǎng)度,例如前者,應(yīng)該以00 0C來(lái)看長(zhǎng)度,表示后面有12個(gè)00那樣的長(zhǎng)度。
(3)固定值
10 00
(4)讀/寫指令
01 04 / 01 14
(5)讀/寫寄存器地址
00 00 00 00 00 A8 05 00 /??00 00 0A 00 00 A8 05 00,其中寫的0A 00代表從第10個(gè)保持寄存器,05表示讀寫5個(gè)寄存器
3、讀寫保持寄存器的響應(yīng)處理
(1)表頭
客戶端的兩個(gè)請(qǐng)求,相同部分都為D0 00 00 FF FF 03 00,我們姑且稱之為表頭。
(2)長(zhǎng)度(協(xié)議幀的長(zhǎng)度)
讀:0C 00根據(jù)實(shí)際長(zhǎng)度變化,寫:02 00 可以不變化。
(3)固定值
00 00
(4)讀/寫響應(yīng)
響應(yīng)實(shí)際讀到的數(shù)據(jù)?/ 無(wú)
4、程序設(shè)計(jì)
根據(jù)上述內(nèi)容,實(shí)現(xiàn)了一個(gè)定制MC服務(wù)器,能夠處理保持寄存器的讀寫請(qǐng)求,給出正確的響應(yīng)。
import socket
import struct
# 創(chuàng)建一個(gè)TCP/IP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 綁定套接字到特定地址和端口
server_address = ('192.168.1.188', 12345) # 服務(wù)器地址和端口
server_socket.bind(server_address)
# 監(jiān)聽連接
server_socket.listen(1)
print('等待客戶端連接...')
connection, client_address = server_socket.accept()
print('客戶端已連接:', client_address)
def request_verdict(req_bytes_frame): # req_bytes_frame是字節(jié)數(shù)據(jù)b'x02x00x08x00x00x00x00x00x10x00x01x01x02x03x04x03'
command = req_bytes_frame.hex()[22:26] # 轉(zhuǎn)成16進(jìn)制字符串好數(shù)據(jù)處理
if command in ["0104", "0401"]: # 判斷讀寫
return False # 讀
elif command in ["0114", "1401"]:
return True # 寫
else:
raise ValueError("讀寫指令錯(cuò)誤!")
def write_response_frame(req_bytes_frame):
response = "D00000FFFF030002000000" # 寫成功則返回這一串?dāng)?shù)據(jù)
content = req_bytes_frame.hex()[42:] # 看一下客戶端想寫的內(nèi)容
print("客戶端想要寫入的內(nèi)容:", bytes.fromhex(content).decode())
return bytes().fromhex(response)
def read_response_frame(req_bytes_frame, res_data):
header = "D00000FFFF03000C000000" # 讀的響應(yīng)頭
nums = req_bytes_frame.hex()[38:42] # 獲取客戶端想要讀的寄存器個(gè)數(shù)
act_nums_hex = nums[2:] + nums[:2] # 涉及大端序和小端序,需要轉(zhuǎn)一下
act_nums = int(act_nums_hex, 16) # 得到實(shí)際數(shù)量
res_data_hex = ''.join([hex(ord(c))[2:].zfill(2) for c in res_data]) # 將要返回的數(shù)據(jù)轉(zhuǎn)成16進(jìn)制字符串
response = header + res_data_hex + '0'*(act_nums*2*2-len(res_data_hex)) # 根據(jù)請(qǐng)求數(shù)量返回對(duì)應(yīng)的內(nèi)容
return bytes().fromhex(response)
try:
while True:
# 接收客戶端請(qǐng)求
request = connection.recv(1024)
print("001:", request)
if request:
flag = request_verdict(request)
if flag: # 響應(yīng)寫
response = write_response_frame(request)
print("002:",response)
else: # 響應(yīng)讀
response = read_response_frame(request, "start")
print("003:",response)
connection.sendall(response)
finally:
# 清理連接
connection.close()
三、關(guān)于MC協(xié)議的整體實(shí)現(xiàn)
通過(guò)“二”,我們實(shí)現(xiàn)了一個(gè)基于MC協(xié)議的保持寄存器的讀寫服務(wù)器,但并沒有像pymodbus這種現(xiàn)成模塊那樣完整實(shí)現(xiàn),這里探討一下還可以做的事。
1、SLMP 3E幀實(shí)現(xiàn)步驟
實(shí)現(xiàn)SLMP(Seamless Message Protocol) 3E幀協(xié)議涉及到網(wǎng)絡(luò)通信、數(shù)據(jù)處理、錯(cuò)誤處理等多個(gè)步驟。以下是實(shí)現(xiàn)SLMP 3E幀的一般步驟:
(1)建立TCP連接:
在服務(wù)端,監(jiān)聽指定端口(通常是4999)。
在客戶端,連接到服務(wù)端的IP地址和端口。
(2)接收請(qǐng)求:
服務(wù)端接收客戶端發(fā)送的SLMP 3E幀請(qǐng)求。
解析SLMP 3E幀,獲取命令碼、子命令碼、數(shù)據(jù)等信息。
(3)處理請(qǐng)求:
根據(jù)SLMP 3E幀中的命令碼和數(shù)據(jù),執(zhí)行相應(yīng)的操作,如讀取、寫入、控制等。
處理請(qǐng)求可能涉及到對(duì)PLC或其他設(shè)備進(jìn)行讀寫操作,具體實(shí)現(xiàn)根據(jù)設(shè)備和應(yīng)用需求而定。
(4)生成響應(yīng):
根據(jù)請(qǐng)求處理的結(jié)果,生成SLMP 3E幀的響應(yīng)數(shù)據(jù)。
設(shè)置響應(yīng)幀的命令碼、子命令碼、數(shù)據(jù)等。
(5)發(fā)送響應(yīng):
將生成的SLMP 3E幀響應(yīng)數(shù)據(jù)發(fā)送回客戶端。
(6)錯(cuò)誤處理:
在處理請(qǐng)求和生成響應(yīng)的過(guò)程中,可能出現(xiàn)各種錯(cuò)誤,如無(wú)效命令、數(shù)據(jù)不合法等。
針對(duì)不同的錯(cuò)誤情況,生成相應(yīng)的錯(cuò)誤響應(yīng)幀。
(7)關(guān)閉連接:
當(dāng)通信結(jié)束或出現(xiàn)錯(cuò)誤時(shí),關(guān)閉TCP連接,釋放資源。
請(qǐng)注意,SLMP 3E幀協(xié)議具體的實(shí)現(xiàn)步驟和數(shù)據(jù)格式可能因具體設(shè)備和應(yīng)用而有所不同。在實(shí)際開發(fā)中,需要參考設(shè)備文檔和SLMP協(xié)議規(guī)范來(lái)進(jìn)行具體的實(shí)現(xiàn)。
2、SLMP協(xié)議規(guī)范
查閱三菱PLC關(guān)于SLMP的文檔!
SLMP的詳細(xì)規(guī)范通常由設(shè)備廠商提供,以便開發(fā)者能夠正確地使用該協(xié)議與設(shè)備進(jìn)行通信。規(guī)范文件通常包含SLMP協(xié)議的命令碼、數(shù)據(jù)格式、通信流程、錯(cuò)誤處理等方面的詳細(xì)信息。
3、為什么會(huì)有SLMP協(xié)議
SLMP(Seamless Message Protocol)協(xié)議是為了在自動(dòng)化領(lǐng)域(例如工業(yè)自動(dòng)化、制造業(yè)、機(jī)器人技術(shù)等)中實(shí)現(xiàn)設(shè)備之間的無(wú)縫通信而設(shè)計(jì)的。它提供了一種標(biāo)準(zhǔn)化的通信協(xié)議,使不同廠商、不同類型的設(shè)備能夠在同一個(gè)網(wǎng)絡(luò)上進(jìn)行通信,實(shí)現(xiàn)設(shè)備的互聯(lián)互通。
在現(xiàn)代工業(yè)自動(dòng)化系統(tǒng)中,通常涉及到各種各樣的設(shè)備,這些設(shè)備由不同的廠商制造,可能使用不同的通信協(xié)議和數(shù)據(jù)格式。為了實(shí)現(xiàn)這些設(shè)備之間的互聯(lián)互通,需要一種通用的、標(biāo)準(zhǔn)化的通信協(xié)議。SLMP就是為了滿足這種需求而被開發(fā)出來(lái)的。
SLMP協(xié)議的設(shè)計(jì)目標(biāo)包括:
標(biāo)準(zhǔn)化通信: 提供一種通用的通信協(xié)議,使得不同廠商生產(chǎn)的設(shè)備可以在同一個(gè)網(wǎng)絡(luò)上進(jìn)行通信。
靈活性: 允許不同類型的數(shù)據(jù)(例如狀態(tài)信息、控制命令等)通過(guò)同一個(gè)協(xié)議進(jìn)行傳輸。
高效性: 設(shè)計(jì)為高效的通信協(xié)議,以滿足工業(yè)自動(dòng)化系統(tǒng)對(duì)實(shí)時(shí)性和響應(yīng)速度的要求。
易用性: 設(shè)計(jì)為易于實(shí)現(xiàn)和配置,使得工程師能夠方便地將SLMP協(xié)議集成到他們的設(shè)備和系統(tǒng)中。
綜上所述,SLMP協(xié)議的存在使得不同類型的自動(dòng)化設(shè)備能夠方便地相互通信,實(shí)現(xiàn)了工業(yè)自動(dòng)化系統(tǒng)的互操作性和靈活性。
4、我是否可以自定義一套協(xié)議
你完全可以自定義一套通信協(xié)議,以滿足特定需求或者應(yīng)用場(chǎng)景。自定義通信協(xié)議通常涉及到以下幾個(gè)步驟:
確定通信需求: 首先,你需要明確通信雙方之間需要傳輸哪些數(shù)據(jù),以及數(shù)據(jù)的格式和類型。確定通信的數(shù)據(jù)結(jié)構(gòu)、命令類型、錯(cuò)誤處理機(jī)制等。
選擇傳輸方式: 確定通信采用的傳輸方式,可以是基于串口的通信(例如RS-232、RS-485)、基于網(wǎng)絡(luò)的通信(例如TCP/IP、UDP)、無(wú)線通信(例如Wi-Fi、藍(lán)牙)等。
制定協(xié)議規(guī)范: 定義協(xié)議的數(shù)據(jù)幀格式,包括幀頭、幀尾、校驗(yàn)碼等信息。確保通信雙方遵循相同的協(xié)議規(guī)范。
實(shí)現(xiàn)協(xié)議解析和封裝: 在通信的發(fā)送端和接收端分別實(shí)現(xiàn)協(xié)議的封裝和解析邏輯。封裝就是將待發(fā)送的數(shù)據(jù)按照協(xié)議格式組織成數(shù)據(jù)幀,解析則是在接收端將接收到的數(shù)據(jù)幀按照協(xié)議格式解析成可處理的數(shù)據(jù)。
添加錯(cuò)誤處理和安全性: 考慮數(shù)據(jù)傳輸中可能出現(xiàn)的錯(cuò)誤情況,設(shè)計(jì)相應(yīng)的錯(cuò)誤處理機(jī)制,確保數(shù)據(jù)的完整性和可靠性。如果通信需要保密性,可以考慮加密通信數(shù)據(jù)。
測(cè)試和驗(yàn)證: 在實(shí)際環(huán)境中進(jìn)行測(cè)試和驗(yàn)證,確保自定義協(xié)議能夠正常工作,并且滿足通信需求。
請(qǐng)注意,自定義協(xié)議需要考慮通信的穩(wěn)定性、可靠性和安全性。在設(shè)計(jì)過(guò)程中,建議參考現(xiàn)有通信協(xié)議的設(shè)計(jì)經(jīng)驗(yàn),以及相關(guān)領(lǐng)域的最佳實(shí)踐。同時(shí),文檔化自定義協(xié)議的規(guī)范,以便未來(lái)的維護(hù)和擴(kuò)展。