- 添加 is_valid_hex() 函数检测16进制格式 - 有效的16进制(偶数长度+0-9a-fA-F)按 bytes.fromhex() 发送 - 非16进制字符串按 UTF-8 编码直接发送 - 日志中标注发送类型 (hex/string)
UPC Resent - 自动化控制设备指令转发系统
基于配置驱动的代码生成系统,支持 TCP 和 UDP 协议,监听端口接收外部指令,转换为 Modbus TCP/UDP 协议发送到控制设备(继电器/PLC等)。
项目结构
upc_resent/
├── config/
│ └── devices.json # 设备配置文件(唯一需要修改的文件)
├── templates/ # 代码模板目录
│ ├── listener_tcp.py.tpl # TCP监听服务模板
│ ├── listener_udp.py.tpl # UDP监听服务模板
│ ├── crond_tcp.py.tpl # TCP保活检查模板
│ ├── crond_udp.py.tpl # UDP保活检查模板
│ ├── sender_tcp.py.tpl # TCP发送模块模板
│ ├── sender_udp.py.tpl # UDP发送模块模板
│ └── control.sh.tpl # 控制脚本模板
├── scripts/
│ └── generate.py # 代码生成器
├── bin/ # 生成的监听服务文件(自动生成)
│ ├── sender_tcp.py # TCP发送模块
│ ├── sender_udp.py # UDP发送模块
│ └── {device_id}.py # 各设备监听服务
├── crond/ # 生成的保活检查文件(自动生成)
├── log/ # 日志目录
├── control.sh # 服务控制脚本(自动生成)
├── crontab.txt # 定时任务配置(自动生成)
└── cutlog.sh # 日志清理脚本
快速开始
1. 克隆仓库
git clone https://git.yuyujing.cn/zj/UPC-Resent/
cd UPC-Resent
2. 配置设备
编辑 config/devices.json:
{
"global": {
"tms_server_ip": "192.168.8.9",
"python_path": "/usr/bin/python3",
"base_dir": "/opt/upc_resent"
},
"command_sets": {
"set1": {
"name": "标准8路控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"open2": "000100000008010F006500010001",
"close2": "000100000008010F006500010000"
}
}
},
"devices": [
{
"id": "dev1",
"name": "设备1-TCP",
"listen_protocol": "tcp", // 监听协议: tcp 或 udp
"device_protocol": "tcp", // 设备协议: tcp 或 udp
"command_set": "set1", // 使用的指令集
"upc_ip": "192.168.8.73",
"upc_port": 502,
"listen_port": 10079,
"enabled": true,
"keep_alive": true // TCP长连接模式(可选,默认false)
},
{
"id": "dev2",
"name": "设备2-UDP",
"listen_protocol": "udp",
"device_protocol": "udp",
"command_set": "set1",
"upc_ip": "192.168.8.200",
"upc_port": 502,
"listen_port": 10080,
"enabled": true
}
]
}
3. 生成代码
# 检查配置是否正确
python3 scripts/generate.py --check
# 生成代码
python3 scripts/generate.py
4. 部署与启动
# 查看服务状态
./control.sh status
# 启动所有服务
./control.sh start
# 停止所有服务
./control.sh stop
# 重启所有服务
./control.sh restart
# 配置定时任务(保活)
crontab crontab.txt
配置文件详解
config/devices.json
全局配置 (global)
| 参数 | 说明 | 示例 |
|---|---|---|
tms_server_ip |
本机监听IP,接收外部指令 | "192.168.8.9" |
python_path |
Python3 解释器路径 | "/usr/bin/python3" |
base_dir |
项目部署路径 | "/opt/upc_resent" |
设备配置 (devices)
| 参数 | 说明 | 示例 |
|---|---|---|
id |
设备唯一标识(用于文件名) | "dev1" |
name |
设备名称(用于日志显示) | "设备1-TCP" |
listen_protocol |
客户端连接协议:tcp 或 udp |
"tcp" / "udp" |
device_protocol |
设备通信协议:tcp 或 udp |
"tcp" / "udp" |
command_set |
使用的指令集ID | "set1" |
upc_ip |
控制设备的IP地址 | "192.168.8.73" |
upc_port |
控制设备的端口(Modbus默认502) | 502 |
listen_port |
本机监听端口(每个设备需不同) | 10079 |
enabled |
是否启用该设备 | true / false |
keep_alive |
TCP 专用 是否保持连接(长连接模式) | true / false (默认) |
协议组合说明:
listen_protocol: 主机监听客户端连接的协议(客户端 -> 主机)device_protocol: 主机与设备通信的协议(主机 -> 设备)- 支持任意组合:TCP->TCP、UDP->UDP、TCP->UDP、UDP->TCP
配置示例:
// 场景1: 客户端TCP -> 主机TCP -> 设备TCP
{
"id": "dev1",
"name": "设备1-纯TCP",
"listen_protocol": "tcp",
"device_protocol": "tcp",
"upc_ip": "192.168.8.73",
"upc_port": 502,
"listen_port": 10079
}
// 场景2: 客户端TCP -> 主机UDP -> 设备UDP(协议转换)
{
"id": "dev2",
"name": "设备2-TCP转UDP",
"listen_protocol": "tcp",
"device_protocol": "udp",
"upc_ip": "192.168.8.200",
"upc_port": 502,
"listen_port": 10080
}
// 场景3: 客户端UDP -> 主机TCP -> 设备TCP(协议转换)
{
"id": "dev3",
"name": "设备3-UDP转TCP",
"listen_protocol": "udp",
"device_protocol": "tcp",
"upc_ip": "192.168.8.201",
"upc_port": 502,
"listen_port": 10081
}
指令集定义 (command_sets)
支持为不同设备配置不同的指令集:
{
"command_sets": {
"set1": {
"name": "标准8路控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"open2": "000100000008010F006500010001",
"close2": "000100000008010F006500010000"
}
},
"set2": {
"name": "4路精简控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"openall": "000100000008010F00640004000F",
"closeall": "000100000008010F006400040000"
}
}
}
}
设备通过 command_set 字段指定使用的指令集:
{
"id": "dev1",
"command_set": "set1"
}
指令映射 (mappings)
{
"mappings": {
"open": "openall4",
"close": "closeall4",
"guanggao-guan": "closeall4"
}
}
TCP vs UDP 差异
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输 | 尽力传输 |
| 响应 | 有确认响应 | 可选响应(可能超时) |
| 适用场景 | 可靠性要求高 | 实时性要求高 |
| 保活检查 | 端口连通性检查 | 进程状态检查 |
如何适配自己的设备
场景1:添加 TCP 设备(客户端TCP -> 主机TCP -> 设备TCP)
{
"devices": [
{
"id": "dev_tcp",
"name": "TCP设备",
"listen_protocol": "tcp",
"device_protocol": "tcp",
"upc_ip": "192.168.1.100",
"upc_port": 502,
"listen_port": 10081,
"enabled": true
}
]
}
场景2:添加 UDP 设备(客户端UDP -> 主机UDP -> 设备UDP)
{
"devices": [
{
"id": "dev_udp",
"name": "UDP设备",
"listen_protocol": "udp",
"device_protocol": "udp",
"upc_ip": "192.168.1.200",
"upc_port": 502,
"listen_port": 10082,
"enabled": true
}
]
}
场景3:协议转换(客户端TCP -> 主机UDP -> 设备UDP)
{
"devices": [
{
"id": "dev_tcp2udp",
"name": "TCP转UDP设备",
"listen_protocol": "tcp",
"device_protocol": "udp",
"upc_ip": "192.168.1.201",
"upc_port": 502,
"listen_port": 10083,
"enabled": true
}
]
}
场景4:协议转换(客户端UDP -> 主机TCP -> 设备TCP)
{
"devices": [
{
"id": "dev_udp2tcp",
"name": "UDP转TCP设备",
"listen_protocol": "udp",
"device_protocol": "tcp",
"upc_ip": "192.168.1.202",
"upc_port": 502,
"listen_port": 10084,
"enabled": true
}
]
}
场景5:不同设备使用不同指令集
{
"command_sets": {
"set1": {
"name": "8路控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"open8": "000100000008010F006B00010001",
"close8": "000100000008010F006B00010000"
}
},
"set2": {
"name": "4路控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"openall": "000100000008010F00640004000F",
"closeall": "000100000008010F006400040000"
}
}
},
"devices": [
{
"id": "dev1",
"name": "8路设备",
"command_set": "set1",
"upc_ip": "192.168.1.100"
},
{
"id": "dev2",
"name": "4路设备",
"command_set": "set2",
"upc_ip": "192.168.1.200"
}
]
}
场景6:禁用某个设备
{
"devices": [
{
"id": "dev2",
"enabled": false
}
]
}
测试指令发送
TCP 设备测试
# 向TCP设备发送开灯指令
echo "open1" | nc 192.168.8.9 10079
# 向TCP设备发送全关指令
echo "close" | nc 192.168.8.9 10079
UDP 设备测试
# 向UDP设备发送开灯指令
echo "open1" | nc -u 192.168.8.9 10080
# 向UDP设备发送全关指令
echo "close" | nc -u 192.168.8.9 10080
# 使用 socat 测试 UDP
echo "open1" | socat - UDP:192.168.8.9:10080
支持的指令列表
| 外部指令 | 内部指令 | 功能 |
|---|---|---|
open |
openall4 |
1-4路全开 |
close |
closeall4 |
1-4路全关 |
open1~open8 |
open1~open8 |
第N路开 |
close1~close8 |
close1~close8 |
第N路关 |
guanggao-guan |
closeall4 |
广告关闭(全关) |
Modbus 指令格式
本项目支持 Modbus TCP/UDP 协议,指令为16进制字符串:
示例:000100000008010F006400010001
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ └── 数据值 (01=开, 00=关)
│ │ │ │ │ │ │ │ │ └──── 寄存器数量
│ │ │ │ │ │ │ │ └─────── 起始地址 (64=100)
│ │ │ │ │ │ │ └────────── 功能码 (0F=写多个线圈)
│ │ │ │ │ │ └──────────── 单元标识
│ │ │ │ └───────────────── 协议标识
│ │ │ └─────────────────── 长度
│ └─────────────────────── 事务标识
└────────────────────────── 事务标识
代码生成器用法
# 检查配置是否正确
python3 scripts/generate.py --check
# 生成代码
python3 scripts/generate.py
# 查看帮助
python3 scripts/generate.py --help
服务控制脚本
control.sh 提供以下命令:
| 命令 | 说明 |
|---|---|
start |
启动所有服务 |
stop |
停止所有服务 |
restart |
重启所有服务 |
status |
查看服务运行状态 |
示例:
./control.sh status
./control.sh start
./control.sh stop
./control.sh restart
日志文件
| 文件 | 说明 |
|---|---|
log/{device_id}.log |
设备监听服务的日志 |
log/crond-{device_id}.log |
保活检查的日志 |
日志自动轮转:每天清理7天前的日志。
注意事项
-
不要直接修改
bin/、crond/和control.sh,这些文件是自动生成的,重新生成时会覆盖。 -
修改配置后必须重新运行
python3 scripts/generate.py才能生效。 -
确保每个设备的
listen_port不冲突,无论 TCP 还是 UDP,端口都不能重复。 -
UDP 是无连接协议,设备可能不会返回响应,这是正常现象。
-
部署到生产环境前,请将
base_dir修改为实际的部署路径(如/opt/upc_resent)。 -
控制脚本使用
pgrep来查找进程,确保系统中没有同名脚本冲突。
Description
Languages
Smarty
65%
Python
34.4%
Shell
0.6%