当前位置:   article > 正文

python实现FINS协议的TCP服务端(篇二)_fins协议数据帧

fins协议数据帧

python实现FINS协议的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样,可以使用现成的pymodbus模块去实现。但是,我们可以根据协议帧进行组包,自己去实现帧的格式,而这一切可以基于socket模块。本文为第二篇。

三、定制服务器

1、对比读写保持寄存器的请求

通过对比请求,来判断读写请求,以对应不同的响应。

  1. # 以写为例,赋予读的对比
  2. Header:46 49 4E 53 固定值
  3. Length00 00 02 30 包的长度 -- 根据实际情况改变
  4. Command:00 00 00 02 固定值
  5. Error Code00 00 00 00 固定值
  6. ICF:固定值80
  7. RSV:固定值00
  8. GCT:固定值02
  9. DNA:目标网络号00
  10. DA1:目标节点号01
  11. DA2:目标单元号00
  12. SNA:源网络号00
  13. SA1:源节点号01
  14. SA2:目标单元号00
  15. SID:源网络号 3E(也可能是其他) -- 根据获取数量从00 开始变FF后再为00,可忽略
  16. MRC:01
  17. SRC:02 -- 读的时候为01,写的时候为02
  18. Area82 -- 保持寄存器地址对应82
  19. Address03 EC 00 -- 实际地址+位地址
  20. length01 0B -- 写的长度可变!读的长度也可变?
  21. value:...

2、编写程序注意事项

由于一个请求是以2个请求或多个请求进行的,因此在编写服务器的时候,确实加了一些小困难,简单解释如下:

(1)先发请求头

 (2)再发指令

 但对于响应来说,却是一个完整的包:

(1)请求头与指令一起发

 四、程序

1、代码

  1. import socket
  2. def recognition_frame(req_bytes_frame, Trigger):
  3. get_frame = req_bytes_frame.hex().upper()
  4. print("设备请求:", get_frame)
  5. # 判断是否为握手命令
  6. if get_frame == "46494E530000000C000000000000000000000000":
  7. response = "46494E530000001000000000000000000000000100000001"
  8. return bytes().fromhex(response)
  9. # 判断是否为其他FINS命令的请求头,只要是请求头都只反应空
  10. elif "46494E53" in get_frame: # 收到FINS的请求头 == "46494E530000001A0000000200000000" 或 其他请求头
  11. print("只收到请求头,响应将为空!")
  12. response = ""
  13. return bytes().fromhex(response)
  14. else:
  15. SRC_value = get_frame[22:24] # 判断读写,01为读,02为写
  16. Area_value = get_frame[24:26] # 判断寄存器区域,82为保持寄存器
  17. # print(SRC_value)
  18. # print(Area_value)
  19. if SRC_value == "01":
  20. if Area_value == "82":
  21. response_1 = "46494E5300000018000000000000000000000000000000000000010100000001" # Trigger位为True
  22. response_0 = "46494E5300000018000000000000000000000000000000000000010100000000" # Trigger位为False
  23. if Trigger == True:
  24. return bytes().fromhex(response_1)
  25. else:
  26. return bytes().fromhex(response_0)
  27. else:
  28. raise ValueError("Area_value is error!")
  29. elif SRC_value == "02":
  30. if Area_value == "82":
  31. print("***************************************")
  32. # 写保持寄存器的响应
  33. print("扫码器写入的结果数据:", bytes().fromhex(get_frame))
  34. response = "46494E530000001600000000000000000000000000000000000001020000"
  35. return bytes().fromhex(response)
  36. else:
  37. raise ValueError("Area_value is error!")
  38. else:
  39. raise ValueError("SRC_value is error!")
  40. if __name__ == "__main__":
  41. DM_start = 1000
  42. # 创建FINS服务端
  43. # 创建一个TCP/IP套接字
  44. server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  45. # 绑定套接字到特定地址和端口
  46. server_address = ('192.168.1.188', 9600) # 服务器地址和端口
  47. server_socket.bind(server_address)
  48. # 监听连接
  49. server_socket.listen(1)
  50. print('等待客户端连接...')
  51. connection, client_address = server_socket.accept()
  52. print('客户端已连接:', client_address)
  53. try:
  54. num = 0 # 触发标志
  55. Trigger_rec = 0 # Trigger置为True时,对应变为1,表示触发一次
  56. response = "" # 响应
  57. while True:
  58. # 接收客户端请求
  59. request = connection.recv(1024)
  60. if request:
  61. # 如果收到的不是请求头
  62. if "8000020001000001" in request.hex():
  63. # print(request.hex()[22:24])
  64. # 实现扫码触发
  65. if request.hex()[22:24] == "01": # 判断读写,01为读触发指令,02为写触发结果
  66. if Trigger_rec != 2:
  67. Trigger_rec += 1
  68. if Trigger_rec == 1:
  69. response = recognition_frame(request, Trigger=False) # 先清空触发信号
  70. connection.sendall(response)
  71. elif Trigger_rec == 2: # 复位Trigger信号
  72. response = recognition_frame(request, Trigger=True) # 再置位触发信号
  73. connection.sendall(response)
  74. # 实现结果接收
  75. elif request.hex()[22:24] == "02":
  76. print(request.hex())
  77. # print("---------------", int(request.hex()[26:30], 16))
  78. if int(request.hex()[26:30], 16) == DM_start + 4:
  79. if any(c != '0' for c in request.hex()[36:]): # 不全为0
  80. print("扫码结果:", request.hex()[36:])
  81. num += 1
  82. Trigger_rec = 0
  83. else:
  84. response = recognition_frame(request, Trigger=True)
  85. connection.sendall(response)
  86. print("还没有收到结果,继续等待扫码结果!")
  87. else:
  88. response = recognition_frame(request, Trigger=True)
  89. connection.sendall(response)
  90. # 处理其他请求
  91. else:
  92. response = recognition_frame(request, Trigger=True)
  93. connection.sendall(response)
  94. print("服务响应:", response.hex())
  95. if num == 1:
  96. assert bytes().fromhex(request.hex()[36:]).decode() == "NG", "实际扫码结果为:{},不符合预期".format(bytes().fromhex(request.hex()[36:]).decode())
  97. break
  98. request = False
  99. finally:
  100. # 清理连接
  101. connection.close()

2、解释

这段代码是一个使用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 协议进行通信。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/309547
推荐阅读
相关标签
  

闽ICP备14008679号