python实现FINS协议的TCP服务端(篇二)

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

三、定制服务器

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

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

# 以写为例,赋予读的对比

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、编写程序注意事项

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

(1)先发请求头

 (2)再发指令

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

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

 四、程序

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()

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 协议进行通信。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion King

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值