# 原生TCP告警

适用范围

可用版本:V1.8.0及以后

仅支持博灵A4系列

# 重要说明

本章节介绍通过原生TCP协议触发设备进行声光告警,虽然是TCP对接,但是实际上只是借用了Modbus TCP协议。

本章节只给出直接演示,不详细赘述Modbus TCP协议,您可以自行搜索Modbus TCP的协议,了解底层实现。

# 什么时候使用原生TCP告警?

  • 需要将其他厂家的设备更换为此设备
  • 原设备采用TCP协议
  • 不修改原设备的对接方式

总的来说,在不更改原有对接方式的情况下,仅修改发送的数据,即可直接对接本公司设备。

使用提醒

若非以上情况,或您第一次对接,则不建议使用此方案,因为原生TCP采用二进制收发,较为繁琐,请使用Api方式进行对接

# 方式1-指令模板播报

# 功能介绍

比较简单的对接方法,只需要在界面中配置好播报模板和对应ID,播报时调用对应ID即可播报。

该方法对应Modbus TCP中的指令模板

# 准备工作

  • 打开Modbus TCP的服务开关,配置端口号
  • 在Modbus TCP中配置指令列表,该功能详细描述请参考指令模板

# 调用演示

调用指令模板中的0x01模板,发送的字节如下:

0x00 0x01 0x00 0x00 0x00 0x06 0x01 0x06 0x00 0x63 0x00 0x01

其中最后的一个字节为要调用的模板编号,该方式可以调用0x01~0xff范围内的模板。

# 代码示例

以下为Python脚本模拟发送该TCP请求的实例,需要提前在Modbus Tcp配置页面中配置指令编号1的模板。

def templateModbusTcp():
    '''
    写单寄存器,触发模板告警,需要提前定义模板
    响应报文:MBAP header + 功能码 + 寄存器地址H 寄存器地址L + 寄存器值H 寄存器值L
    :return:
    '''
    # 写单寄存器,100地址(1开始算),指令中以0开始计算,所以为0x63,指令编号为1。
    return bytes.fromhex("0001 0000 0006 0106 0063 0001")
    # 写单寄存器,100地址,指令编号为2
    # return bytes.fromhex("0001 0000 0006 0106 0063 0002")

# 目标 IP 和端口
ip = "192.168.0.66"
port = 502

# 创建一个 socket 对象
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    # raw_data = sendModbusTCP("123哈哈哈哈哈-发送的2024年01月16日14:23:04")
    raw_data = templateModbusTcp()
    # 建立连接
    s.connect((ip, port))
    # 发送数据
    s.sendall(bytes(raw_data))
    # 可以接收响应数据
    response = s.recv(1024)
    print("Received:", response.hex())
    # 由于这里不确定服务端是否返回数据,所以暂时注释掉接收部分
    print("Data sent successfully.")

# 响应数据(与发送数据一致)

0001 0000 0006 0106 0063 0001

# 方式2-自定义声光播报

# 功能介绍

强大的播报方法,可以自定义灯光样式、颜色、播报内容,但构造数据较为复杂,使用写多寄存器实现。

该方法对应Modbus TCP-自定义声光播报

# 准备工作

  • 打开Modbus TCP的服务开关,配置端口号

# 调用演示

使用一下声光告警参数进行调用

参数名
led_style 15
color ["#8A2BE2"]
led_flag [0.03,1]
play_time 0
tone 0
tts_speed 0
text 这是TCP调用演示

# 构造数据

下表数据中,字节1~13为Modbus TCP协议写多寄存器的包头 14~79为声光播报的具体内容,相关介绍参见Modbus TCP-地址表

字节 说明 实例值
1-4 包头 '0x00', '0x01', '0x00', '0x00'
5-6 包长度
下一个字节开始到最后的字节数
'0x00', '0x49'
7-8 固定值 '0x01', '0x10'
9-10 固定值 '0x00', '0x64'
11-12 写入的寄存器数量
声光播报内容数据字节数 / 2
本案例内容为14~79
66字节
66 / 2 = 33
转16进制为0x21
'0x00', '0x21'
13 写入的字节数
写入的寄存器数量*2即可
'0x42'
14-15 Led style '0x00', '0x0f',
16-31 led_color '0x00', '0x8a', '0x2b', '0xe2',
'0x00', '0x00','0x00', '0x00',
'0x00', '0x00', '0x00', '0x00',
'0x00', '0x00', '0x00', '0x00'
32-33 play_time 播报时长 '0x00', '0x00'
34-35 tone 铃声编号 '0x00', '0x00'
36-37 tts_speed 播报速率 '0x00', '0x00'
38-39 led_style 样式参数 '0x03', '0x01'
40-43 保留字节 '0x00', '0x00', '0x00', '0x00'
44-47 文本内容-"这" '0x00', '0xe8', '0xbf', '0x99'
48-51 文本内容-"是" '0x00', '0xe6', '0x98', '0xaf'
52-55 文本内容-"T" '0x00', '0x00', '0x00', '0x54'
56-59 文本内容-"C" '0x00', '0x00', '0x00', '0x43'
60-63 文本内容-"P" '0x00', '0x00', '0x00', '0x50
64-67 文本内容-"调" '0x00', '0xe8', '0xb0', '0x83',
68-71 文本内容-"用" '0x00', '0xe7', '0x94', '0xa8',
72-75 文本内容-"演" '0x00', '0xe6', '0xbc', '0x94'
76-79 文本内容-"示" '0x00', '0xe7', '0xa4', '0xba'

# 组合后数据

['0x00', '0x01', '0x00', '0x00', '0x00', '0x49', '0x01', '0x10', '0x00', '0x64', '0x00', '0x21', '0x42', '0x00', '0x0f', '0x00', '0x8a', '0x2b', '0xe2', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x03', '0x01', '0x00', '0x00', '0x00', '0x00', '0x00', '0xe8', '0xbf', '0x99', '0x00', '0xe6', '0x98', '0xaf', '0x00', '0x00', '0x00', '0x54', '0x00', '0x00', '0x00', '0x43', '0x00', '0x00', '0x00', '0x50', '0x00', '0xe8', '0xb0', '0x83', '0x00', '0xe7', '0x94', '0xa8', '0x00', '0xe6', '0xbc', '0x94', '0x00', '0xe7', '0xa4', '0xba']

# Python代码示例

import socket
import time

write_multi_reg_header = [0x0001, 0x0000, 0x0000, 0x0110]

'''
MBAP header报文头格式:
示例值      长度(byte)      说明
0x0001      2             可以一直写1,返回时会附带此值
0x0000      2             固定值
0x0006      2             从下一个字节开始算,一直到最后的字节数
0x01        1             固定值,1即可
0x10        1             功能码,定义参见Modbus TCP
'''
def wordList2byteList(l):
    '''
    双字节List转单字节List
    :param l:
    :return:
    '''
    byte_list = []
    for word in l:
        byte_list.append(word >> 8)
        byte_list.append(word & 0xff)
    return byte_list

def createTcpPkg(start_addr, reg_number, data:list):
    '''
    创建写多寄存器的pkg
    响应报文:MBAP header + 功能码 + 起始地址H 起始地址L + 寄存器数量H 寄存器数量L(共12字节)
    :param start_addr:
    :param reg_number:  双字节,例如0x000f
    :param addr:        双字节list
    :return:
    '''
    # 报文头
    pkg = write_multi_reg_header
    # 修改报文头的长度,一般为请求的数据从航都字节数+1
    pkg[2] = len(data)*2 + 7

    # 写多寄存器的起始地址2字节
    pkg += [start_addr, reg_number]
    # 将之前的数据转为byte的list
    byte_list = wordList2byteList(pkg)

    # 写字节个数,长度为1字节
    byte_list.append(len(data) * 2 & 0xff)

    byte_list += wordList2byteList(data)
    return byte_list

def encodeStr(s):
    '''
    字符串编码
    :param s:
    :return:
    '''
    raw = []
    for bit in s:
        bit = int.from_bytes(bit.encode('utf-8'), 'big')
        word = [0] * 2
        word[0] = (0xffff0000 & bit) >> 16
        word[1] = 0xffff & bit
        raw += word
    return raw


# 触发自定义指令demo
def sendModbusTCP(tts):
    '''
    TCP发送自定义指令
    :param tts:
    :return:
    '''
    data_arr = []
    led_color = [0] * 8

    # LED样式
    led_style = 15

    # LED颜色
    led_color[0] = 0x8A
    led_color[1] = 0x2be2

    play_time = 0
    tone = 0
    tts_speed = 0
    led_flag = 0x0301

    empty = [0] * 2
    tts_str = encodeStr(tts)
    data_arr += [led_style] + led_color + [play_time] + [tone] + [tts_speed] + [led_flag] + empty + tts_str

    print('播报Payload:')
    print([f"0x{x:04x}" for x in data_arr])
    print('Payload长度:', len(data_arr))
    raw_data = createTcpPkg(100, len(data_arr), data_arr)

    print('封装后的原始数据:')
    print([f"0x{x:02x}" for x in raw_data])
    print(len(raw_data))
    return raw_data


def templateModbusTcp():
    '''
    写单寄存器,触发模板告警,需要提前定义模板
    响应报文:MBAP header + 功能码 + 寄存器地址H 寄存器地址L + 寄存器值H 寄存器值L
    :return:
    '''
    # 写单寄存器,100地址(1开始算),指令中以0开始计算,所以为0x63,指令编号为1。
    return bytes.fromhex("0001 0000 0006 0106 0063 0001")
    # 写单寄存器,100地址,指令编号为2
    # return bytes.fromhex("0001 0000 0006 0106 0063 0002")

# 目标 IP 和端口
ip = "192.168.0.216"
port = 502

# 创建一个 socket 对象
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    raw_data = sendModbusTCP("这是TCP调用演示")
    # raw_data = templateModbusTcp()
    # 建立连接
    s.connect((ip, port))
    # 发送数据
    s.sendall(bytes(raw_data))
    # 可以接收响应数据
    response = s.recv(1024)
    print("Received:", response.hex())
    # 由于这里不确定服务端是否返回数据,所以暂时注释掉接收部分
    print("Data sent successfully.")

# 响应数据

最后的两个字节为写入的寄存器数量,本案例为0x0021,其他不变

0x0001 0x0000 0x0006 0x0110 0x0064 0x0021