赞
踩
python实现FINS协议的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样,可以使用现成的pymodbus模块去实现。但是,我们可以根据协议帧进行组包,自己去实现帧的格式,而这一切可以基于socket模块。本文为第二篇。
通过对比请求,来判断读写请求,以对应不同的响应。
- # 以写为例,赋予读的对比
-
- Header:46 49 4E 53 固定值
-
- Length:00 00 02 30 包的长度 -- 根据实际情况改变
-
- Command:00 00 00 02 固定值
-
- Error Code:00 00 00 00 固定值
-
- ICF:固定值80
-
- RSV:固定值00
-
- GCT:固定值02
-
- DNA:目标网络号00
-
- DA1:目标节点号01
-
- DA2:目标单元号00
-
- SNA:源网络号00
-
- SA1:源节点号01
-
- SA2:目标单元号00
-
- SID:源网络号 3E(也可能是其他) -- 根据获取数量从00 开始变FF后再为00,可忽略
-
- MRC:01
-
- SRC:02 -- 读的时候为01,写的时候为02
-
- Area:82 -- 保持寄存器地址对应82
-
- Address:03 EC 00 -- 实际地址+位地址
-
- length:01 0B -- 写的长度可变!读的长度也可变?
-
- value:...
由于一个请求是以2个请求或多个请求进行的,因此在编写服务器的时候,确实加了一些小困难,简单解释如下:
(1)先发请求头
(2)再发指令
但对于响应来说,却是一个完整的包:
(1)请求头与指令一起发
- import socket
-
- def recognition_frame(req_bytes_frame, Trigger):
- get_frame = req_bytes_frame.hex().upper()
- print("设备请求:", get_frame)
- # 判断是否为握手命令
- if get_frame == "46494E530000000C000000000000000000000000":
- response = "46494E530000001000000000000000000000000100000001"
- return bytes().fromhex(response)
- # 判断是否为其他FINS命令的请求头,只要是请求头都只反应空
- elif "46494E53" in get_frame: # 收到FINS的请求头 == "46494E530000001A0000000200000000" 或 其他请求头
- print("只收到请求头,响应将为空!")
- response = ""
- return bytes().fromhex(response)
- else:
- SRC_value = get_frame[22:24] # 判断读写,01为读,02为写
- Area_value = get_frame[24:26] # 判断寄存器区域,82为保持寄存器
- # print(SRC_value)
- # print(Area_value)
- if SRC_value == "01":
- if Area_value == "82":
- response_1 = "46494E5300000018000000000000000000000000000000000000010100000001" # Trigger位为True
- response_0 = "46494E5300000018000000000000000000000000000000000000010100000000" # Trigger位为False
- if Trigger == True:
- return bytes().fromhex(response_1)
- else:
- return bytes().fromhex(response_0)
- else:
- raise ValueError("Area_value is error!")
- elif SRC_value == "02":
- if Area_value == "82":
- print("***************************************")
- # 写保持寄存器的响应
- print("扫码器写入的结果数据:", bytes().fromhex(get_frame))
- response = "46494E530000001600000000000000000000000000000000000001020000"
- return bytes().fromhex(response)
- else:
- raise ValueError("Area_value is error!")
- else:
- raise ValueError("SRC_value is error!")
-
- if __name__ == "__main__":
- DM_start = 1000
-
- # 创建FINS服务端
- # 创建一个TCP/IP套接字
- server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- # 绑定套接字到特定地址和端口
- server_address = ('192.168.1.188', 9600) # 服务器地址和端口
- server_socket.bind(server_address)
- # 监听连接
- server_socket.listen(1)
- print('等待客户端连接...')
- connection, client_address = server_socket.accept()
- print('客户端已连接:', client_address)
-
- try:
- num = 0 # 触发标志
- Trigger_rec = 0 # Trigger置为True时,对应变为1,表示触发一次
- response = "" # 响应
- while True:
- # 接收客户端请求
- request = connection.recv(1024)
- if request:
- # 如果收到的不是请求头
- if "8000020001000001" in request.hex():
- # print(request.hex()[22:24])
- # 实现扫码触发
- if request.hex()[22:24] == "01": # 判断读写,01为读触发指令,02为写触发结果
- if Trigger_rec != 2:
- Trigger_rec += 1
- if Trigger_rec == 1:
- response = recognition_frame(request, Trigger=False) # 先清空触发信号
- connection.sendall(response)
- elif Trigger_rec == 2: # 复位Trigger信号
- response = recognition_frame(request, Trigger=True) # 再置位触发信号
- connection.sendall(response)
- # 实现结果接收
- elif request.hex()[22:24] == "02":
- print(request.hex())
- # print("---------------", int(request.hex()[26:30], 16))
- if int(request.hex()[26:30], 16) == DM_start + 4:
- if any(c != '0' for c in request.hex()[36:]): # 不全为0
- print("扫码结果:", request.hex()[36:])
- num += 1
- Trigger_rec = 0
- else:
- response = recognition_frame(request, Trigger=True)
- connection.sendall(response)
- print("还没有收到结果,继续等待扫码结果!")
- else:
- response = recognition_frame(request, Trigger=True)
- connection.sendall(response)
- # 处理其他请求
- else:
- response = recognition_frame(request, Trigger=True)
- connection.sendall(response)
- print("服务响应:", response.hex())
- if num == 1:
- assert bytes().fromhex(request.hex()[36:]).decode() == "NG", "实际扫码结果为:{},不符合预期".format(bytes().fromhex(request.hex()[36:]).decode())
- break
- request = False
- finally:
- # 清理连接
- connection.close()
这段代码是一个使用FINS协议的服务器端程序。它监听指定地址和端口,接收客户端请求,根据请求内容作出相应的响应。以下是对主要部分的解释:
(1)recognition_frame 函数:
接收一个 req_bytes_frame 参数,这是客户端请求的字节表示。
get_frame 变量将字节表示转换为大写的十六进制字符串。
通过一系列条件判断,判断请求类型并返回相应的响应。
(2)if __name__ == "__main__": 部分:
初始化一些变量,如 DM_start、num、Trigger_rec 和 response。
创建一个 TCP 服务器套接字,绑定地址和端口,然后监听连接。
在一个无限循环中,接收客户端请求,判断请求类型并发送相应的响应。
扫码触发和结果接收部分:
如果接收到的请求的十六进制表示包含特定的模式("8000020001000001"),则执行扫码触发或结果接收的逻辑。
①触发时,通过 recognition_frame 函数发送相应的响应。
②结果接收时,判断是否是指定寄存器的写入,如果写入的内容不全为零,则认为收到了扫码结果。
recognition_frame 函数中的异常处理:
如果在解析请求时发现不符合预期的情况,抛出 ValueError 异常。
finally 块:
在程序结束时关闭连接。
总体来说,这是一个基于 FINS 协议的服务器程序,主要用于处理扫码触发和结果接收,并通过 FINS 协议进行通信。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。