Appearance
原生TCP告警
重要说明
本章节介绍通过原生TCP
协议触发设备进行声光告警,虽然是TCP
对接,但是实际上只是借用了Modbus TCP
协议。
本章节只给出直接演示,不详细赘述Modbus TCP协议,请自行搜索Modbus TCP的协议,了解底层实现。
什么时候使用原生TCP告警?
- 需要将其他厂家的设备更换为此设备
- 原设备采用TCP协议
- 不修改原设备的对接方式
总的来说,在不更改原有对接方式的情况下,仅修改发送的数据,即可直接对接本公司设备。
使用提醒
若非以上情况,或您第一次对接,则不建议使用此方案,因为原生TCP采用二进制收发,较为繁琐,请使用Api方式进行对接!
指令模板播报
比较简单的对接方法,只需要在界面中配置好播报模板和对应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
的模板。
python
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
自定义声光播报
强大的播报方法,可以自定义灯光样式、颜色、播报内容,但构造数据较为复杂,使用写多寄存器实现。
该方法对应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代码示例
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