自定义映射

This commit is contained in:
zj
2026-03-23 15:57:58 +08:00
parent 3ecba44f9e
commit e97d9c427a
8 changed files with 365 additions and 338 deletions

184
README.md
View File

@@ -42,11 +42,24 @@ upc_resent/
"python_path": "/usr/bin/python3", "python_path": "/usr/bin/python3",
"base_dir": "/opt/upc_resent" "base_dir": "/opt/upc_resent"
}, },
"command_sets": {
"set1": {
"name": "标准8路控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"open2": "000100000008010F006500010001",
"close2": "000100000008010F006500010000"
}
}
},
"devices": [ "devices": [
{ {
"id": "dev1", "id": "dev1",
"name": "设备1-TCP", "name": "设备1-TCP",
"protocol": "tcp", // 协议类型: tcp 或 udp "listen_protocol": "tcp", // 监听协议: tcp 或 udp
"device_protocol": "tcp", // 设备协议: tcp 或 udp
"command_set": "set1", // 使用的指令集
"upc_ip": "192.168.8.73", "upc_ip": "192.168.8.73",
"upc_port": 502, "upc_port": 502,
"listen_port": 10079, "listen_port": 10079,
@@ -55,7 +68,9 @@ upc_resent/
{ {
"id": "dev2", "id": "dev2",
"name": "设备2-UDP", "name": "设备2-UDP",
"protocol": "udp", // 使用UDP协议 "listen_protocol": "udp",
"device_protocol": "udp",
"command_set": "set1",
"upc_ip": "192.168.8.200", "upc_ip": "192.168.8.200",
"upc_port": 502, "upc_port": 502,
"listen_port": 10080, "listen_port": 10080,
@@ -112,26 +127,93 @@ crontab crontab.txt
|-----|------|-----| |-----|------|-----|
| `id` | 设备唯一标识(用于文件名) | `"dev1"` | | `id` | 设备唯一标识(用于文件名) | `"dev1"` |
| `name` | 设备名称(用于日志显示) | `"设备1-TCP"` | | `name` | 设备名称(用于日志显示) | `"设备1-TCP"` |
| `protocol` | 协议类型`tcp``udp` | `"tcp"` / `"udp"` | | `listen_protocol` | 客户端连接协议:`tcp``udp` | `"tcp"` / `"udp"` |
| `device_protocol` | 设备通信协议:`tcp``udp` | `"tcp"` / `"udp"` |
| `command_set` | 使用的指令集ID | `"set1"` |
| `upc_ip` | 控制设备的IP地址 | `"192.168.8.73"` | | `upc_ip` | 控制设备的IP地址 | `"192.168.8.73"` |
| `upc_port` | 控制设备的端口Modbus默认502 | `502` | | `upc_port` | 控制设备的端口Modbus默认502 | `502` |
| `listen_port` | 本机监听端口(每个设备需不同) | `10079` | | `listen_port` | 本机监听端口(每个设备需不同) | `10079` |
| `enabled` | 是否启用该设备 | `true` / `false` | | `enabled` | 是否启用该设备 | `true` / `false` |
#### 指令定义 (commands) **协议组合说明**
- `listen_protocol`: 主机监听客户端连接的协议(客户端 -> 主机)
- `device_protocol`: 主机与设备通信的协议(主机 -> 设备)
- 支持任意组合TCP->TCP、UDP->UDP、TCP->UDP、UDP->TCP
**配置示例**
```json
// 场景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)
支持为不同设备配置不同的指令集:
```json ```json
{ {
"command_sets": {
"set1": {
"name": "标准8路控制",
"commands": { "commands": {
"open1": "000100000008010F006400010001", "open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000", "close1": "000100000008010F006400010000",
"openall4": "000100000008010F00640004000F", "open2": "000100000008010F006500010001",
"closeall4": "000100000008010F006400040000" "close2": "000100000008010F006500010000"
}
},
"set2": {
"name": "4路精简控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"openall": "000100000008010F00640004000F",
"closeall": "000100000008010F006400040000"
}
}
} }
} }
``` ```
格式:指令名 -> 16进制字符串 设备通过 `command_set` 字段指定使用的指令集:
```json
{
"id": "dev1",
"command_set": "set1"
}
```
#### 指令映射 (mappings) #### 指令映射 (mappings)
@@ -157,7 +239,7 @@ crontab crontab.txt
## 如何适配自己的设备 ## 如何适配自己的设备
### 场景1添加 TCP 设备 ### 场景1添加 TCP 设备客户端TCP -> 主机TCP -> 设备TCP
```json ```json
{ {
@@ -165,7 +247,8 @@ crontab crontab.txt
{ {
"id": "dev_tcp", "id": "dev_tcp",
"name": "TCP设备", "name": "TCP设备",
"protocol": "tcp", "listen_protocol": "tcp",
"device_protocol": "tcp",
"upc_ip": "192.168.1.100", "upc_ip": "192.168.1.100",
"upc_port": 502, "upc_port": 502,
"listen_port": 10081, "listen_port": 10081,
@@ -175,7 +258,7 @@ crontab crontab.txt
} }
``` ```
### 场景2添加 UDP 设备 ### 场景2添加 UDP 设备客户端UDP -> 主机UDP -> 设备UDP
```json ```json
{ {
@@ -183,7 +266,8 @@ crontab crontab.txt
{ {
"id": "dev_udp", "id": "dev_udp",
"name": "UDP设备", "name": "UDP设备",
"protocol": "udp", "listen_protocol": "udp",
"device_protocol": "udp",
"upc_ip": "192.168.1.200", "upc_ip": "192.168.1.200",
"upc_port": 502, "upc_port": 502,
"listen_port": 10082, "listen_port": 10082,
@@ -193,18 +277,86 @@ crontab crontab.txt
} }
``` ```
### 场景3添加新的控制指令 ### 场景3协议转换客户端TCP -> 主机UDP -> 设备UDP
```json ```json
{ {
"commands": { "devices": [
"open9": "000100000008010F006C00010001", {
"close9": "000100000008010F006C00010000" "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禁用某个设备 ### 场景4协议转换客户端UDP -> 主机TCP -> 设备TCP
```json
{
"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不同设备使用不同指令集
```json
{
"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禁用某个设备
```json ```json
{ {

View File

@@ -4,35 +4,9 @@
"python_path": "/usr/bin/python3", "python_path": "/usr/bin/python3",
"base_dir": "/home/smart/pythonPJ/upc_resent" "base_dir": "/home/smart/pythonPJ/upc_resent"
}, },
"devices": [ "command_sets": {
{ "set1": {
"id": "dev1", "name": "标准8路控制",
"name": "设备73-TCP",
"protocol": "tcp",
"upc_ip": "192.168.8.73",
"upc_port": 502,
"listen_port": 10079,
"enabled": true
},
{
"id": "dev2",
"name": "设备125-TCP",
"protocol": "tcp",
"upc_ip": "192.168.8.125",
"upc_port": 502,
"listen_port": 10129,
"enabled": true
},
{
"id": "dev3",
"name": "设备UDP示例",
"protocol": "udp",
"upc_ip": "192.168.8.200",
"upc_port": 502,
"listen_port": 10080,
"enabled": true
}
],
"commands": { "commands": {
"open1": "000100000008010F006400010001", "open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000", "close1": "000100000008010F006400010000",
@@ -52,7 +26,77 @@
"close7": "000100000008010F006A00010000", "close7": "000100000008010F006A00010000",
"open8": "000100000008010F006B00010001", "open8": "000100000008010F006B00010001",
"close8": "000100000008010F006B00010000" "close8": "000100000008010F006B00010000"
}
}, },
"set2": {
"name": "4路精简控制",
"commands": {
"open1": "000100000008010F006400010001",
"close1": "000100000008010F006400010000",
"open2": "000100000008010F006500010001",
"close2": "000100000008010F006500010000",
"open3": "000100000008010F006600010001",
"close3": "000100000008010F006600010000",
"open4": "000100000008010F006700010001",
"close4": "000100000008010F006700010000",
"openall": "000100000008010F00640004000F",
"closeall": "000100000008010F006400040000"
}
},
"set3": {
"name": "单路控制",
"commands": {
"open": "000100000008010F006400010001",
"close": "000100000008010F006400010000"
}
}
},
"devices": [
{
"id": "dev1",
"name": "设备1-8路控制",
"listen_protocol": "tcp",
"device_protocol": "tcp",
"command_set": "set1",
"upc_ip": "192.168.8.73",
"upc_port": 502,
"listen_port": 10079,
"enabled": true
},
{
"id": "dev2",
"name": "设备2-8路控制",
"listen_protocol": "tcp",
"device_protocol": "tcp",
"command_set": "set1",
"upc_ip": "192.168.8.125",
"upc_port": 502,
"listen_port": 10129,
"enabled": true
},
{
"id": "dev3",
"name": "设备3-4路控制",
"listen_protocol": "udp",
"device_protocol": "udp",
"command_set": "set2",
"upc_ip": "192.168.8.200",
"upc_port": 502,
"listen_port": 10080,
"enabled": true
},
{
"id": "dev4",
"name": "设备4-单路控制",
"listen_protocol": "tcp",
"device_protocol": "udp",
"command_set": "set3",
"upc_ip": "192.168.8.201",
"upc_port": 502,
"listen_port": 10081,
"enabled": true
}
],
"mappings": { "mappings": {
"open": "openall4", "open": "openall4",
"close": "closeall4", "close": "closeall4",

View File

@@ -1,236 +0,0 @@
#!/bin/bash
# Auto-generated control script
# Usage: ./control.sh {start|stop|restart|status}
BASE_DIR="/home/smart/pythonPJ/upc_resent"
PYTHON_PATH="/usr/bin/python3"
# 设备列表: ID 名称 协议
DEVICES=(
"dev1" "设备73-TCP" "TCP"
"dev2" "设备125-TCP" "TCP"
"dev3" "设备UDP示例" "UDP"
)
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
show_help() {
echo "Usage: $0 {start|stop|restart|status}"
echo ""
echo "Commands:"
echo " start 启动所有服务"
echo " stop 停止所有服务"
echo " restart 重启所有服务"
echo " status 查看服务状态"
}
check_root() {
if [[ $EUID -ne 0 ]]; then
echo -e "${YELLOW}Warning: 建议使用 root 权限运行${NC}"
fi
}
start_service() {
local device_id=$1
local device_name=$2
local protocol=$3
local script_path="$BASE_DIR/bin/$device_id.py"
# 检查进程是否已存在(使用更精确的匹配)
local pid=$(pgrep -f "${PYTHON_PATH}.*${script_path}" | head -1)
if [[ -n "$pid" ]]; then
echo -e "[${YELLOW}SKIP${NC}] $device_name ($device_id) - ${protocol} 已在运行 (PID: $pid)"
return 0
fi
echo -n "[START] $device_name ($device_id) - ${protocol} ... "
nohup $PYTHON_PATH "$script_path" > /dev/null 2>&1 &
sleep 1
# 检查是否启动成功
pid=$(pgrep -f "${PYTHON_PATH}.*${script_path}" | head -1)
if [[ -n "$pid" ]]; then
echo -e "${GREEN}OK${NC} (PID: $pid)"
return 0
else
echo -e "${RED}FAILED${NC}"
return 1
fi
}
stop_service() {
local device_id=$1
local device_name=$2
local protocol=$3
local script_path="$BASE_DIR/bin/$device_id.py"
# 使用更精确的匹配模式
local pids=$(pgrep -f "${PYTHON_PATH}.*${script_path}")
if [[ -z "$pids" ]]; then
echo -e "[${YELLOW}SKIP${NC}] $device_name ($device_id) - 未运行"
return 0
fi
# 获取第一个匹配的 PID
local pid=$(echo "$pids" | head -1)
echo -n "[STOP] $device_name ($device_id) - ${protocol} (PID: $pid) ... "
# 结束所有匹配的进程
echo "$pids" | while read -r p; do
kill "$p" 2>/dev/null
done
# 等待进程结束
local i
for i in {1..10}; do
sleep 0.5
pids=$(pgrep -f "${PYTHON_PATH}.*${script_path}" || true)
if [[ -z "$pids" ]]; then
break
fi
done
# 检查是否还有残留进程
pids=$(pgrep -f "${PYTHON_PATH}.*${script_path}" || true)
if [[ -n "$pids" ]]; then
# 强制结束残留进程
echo "$pids" | while read -r p; do
kill -9 "$p" 2>/dev/null
done
sleep 0.5
fi
# 最终检查
pids=$(pgrep -f "${PYTHON_PATH}.*${script_path}" || true)
if [[ -z "$pids" ]]; then
echo -e "${GREEN}OK${NC}"
return 0
else
echo -e "${RED}FAILED${NC}"
return 1
fi
}
status_service() {
local device_id=$1
local device_name=$2
local protocol=$3
local script_path="$BASE_DIR/bin/$device_id.py"
local pid=$(pgrep -f "${PYTHON_PATH}.*${script_path}" | head -1)
if [[ -n "$pid" ]]; then
echo -e "${GREEN}[RUNNING]${NC} $device_name ($device_id) - ${protocol} (PID: $pid)"
return 0
else
echo -e "${RED}[STOPPED]${NC} $device_name ($device_id) - ${protocol}"
return 1
fi
}
cmd_start() {
echo "======================================="
echo "启动 UPC Resent 服务"
echo "======================================="
check_root
echo ""
local count=0
local device_id device_name protocol
for ((i=0; i<${#DEVICES[@]}; i+=3)); do
device_id="${DEVICES[i]}"
device_name="${DEVICES[i+1]}"
protocol="${DEVICES[i+2]}"
if start_service "$device_id" "$device_name" "$protocol"; then
((count++))
fi
done
echo ""
echo "======================================="
echo "启动完成: $count/$((${#DEVICES[@]}/3)) 个服务"
echo "======================================="
}
cmd_stop() {
echo "======================================="
echo "停止 UPC Resent 服务"
echo "======================================="
echo ""
local count=0
local device_id device_name protocol
for ((i=0; i<${#DEVICES[@]}; i+=3)); do
device_id="${DEVICES[i]}"
device_name="${DEVICES[i+1]}"
protocol="${DEVICES[i+2]}"
if stop_service "$device_id" "$device_name" "$protocol"; then
((count++))
fi
done
echo ""
echo "======================================="
echo "停止完成: $count/$((${#DEVICES[@]}/3)) 个服务"
echo "======================================="
}
cmd_restart() {
cmd_stop
echo ""
sleep 1
cmd_start
}
cmd_status() {
echo "======================================="
echo "UPC Resent 服务状态"
echo "======================================="
echo ""
local running=0
local stopped=0
local device_id device_name protocol
for ((i=0; i<${#DEVICES[@]}; i+=3)); do
device_id="${DEVICES[i]}"
device_name="${DEVICES[i+1]}"
protocol="${DEVICES[i+2]}"
if status_service "$device_id" "$device_name" "$protocol"; then
((running++))
else
((stopped++))
fi
done
echo ""
echo "======================================="
echo "运行中: $running, 已停止: $stopped, 总计: $((${#DEVICES[@]}/3))"
echo "======================================="
}
# 主逻辑
case "${1:-}" in
start)
cmd_start
;;
stop)
cmd_stop
;;
restart)
cmd_restart
;;
status)
cmd_status
;;
*)
show_help
exit 1
;;
esac

View File

@@ -1,4 +0,0 @@
* */1 * * * /usr/bin/python3 /home/smart/pythonPJ/upc_resent/crond/upcrond-dev1.py
* */1 * * * /usr/bin/python3 /home/smart/pythonPJ/upc_resent/crond/upcrond-dev2.py
* */1 * * * /usr/bin/python3 /home/smart/pythonPJ/upc_resent/crond/upcrond-dev3.py
0 0 * * * /home/smart/pythonPJ/upc_resent/cutlog.sh

View File

@@ -60,10 +60,11 @@ def get_template_path(protocol, template_type):
raise ValueError(f"未知的模板类型: {template_type}") raise ValueError(f"未知的模板类型: {template_type}")
def generate_listener(device, global_config, mappings): def generate_listener(device, global_config, mappings, command_sets, global_commands):
"""生成监听服务文件""" """生成监听服务文件"""
protocol = device.get('protocol', 'tcp').lower() listen_protocol = device.get('listen_protocol', 'tcp').lower()
template_file = get_template_path(protocol, 'listener') device_protocol = device.get('device_protocol', 'tcp').lower()
template_file = get_template_path(listen_protocol, 'listener')
# 检查模板文件是否存在 # 检查模板文件是否存在
if not os.path.exists(template_file): if not os.path.exists(template_file):
@@ -73,16 +74,35 @@ def generate_listener(device, global_config, mappings):
with open(template_file, 'r', encoding='utf-8') as f: with open(template_file, 'r', encoding='utf-8') as f:
template = f.read() template = f.read()
# 获取该设备的指令集
command_set_id = device.get('command_set')
if command_set_id and command_set_id in command_sets:
device_commands = command_sets[command_set_id].get('commands', {})
else:
# 兼容旧配置
device_commands = global_commands
# 构建命令映射字典字符串 # 构建命令映射字典字符串
# 1. 首先添加该设备指令集中所有指令的默认映射(指令名映射到自身)
device_mappings = {}
for cmd in device_commands.keys():
device_mappings[cmd] = cmd
# 2. 然后添加全局 mappings 中的映射(外部命令 -> 内部指令)
# 但只添加目标指令在该设备指令集中存在的映射
for external_cmd, internal_cmd in mappings.items():
if internal_cmd in device_commands:
device_mappings[external_cmd] = internal_cmd
# 3. 生成映射表字符串
mappings_str = "{\n" mappings_str = "{\n"
for cmd, op in mappings.items(): for cmd, op in device_mappings.items():
mappings_str += f' "{cmd}": "{op}",\n' mappings_str += f' "{cmd}": "{op}",\n'
# 添加默认映射open1-open8, close1-close8
for i in range(1, 9):
mappings_str += f' "open{i}": "open{i}",\n'
mappings_str += f' "close{i}": "close{i}",\n'
mappings_str += " }" mappings_str += " }"
# 确定使用哪个 sender 脚本(每个设备独立的 sender
sender_file = f"{global_config['base_dir']}/bin/sender_{device['id']}.py"
# 替换模板变量 # 替换模板变量
content = template.format( content = template.format(
device_id=device['id'], device_id=device['id'],
@@ -93,7 +113,9 @@ def generate_listener(device, global_config, mappings):
upc_port=device['upc_port'], upc_port=device['upc_port'],
base_dir=global_config['base_dir'], base_dir=global_config['base_dir'],
python_path=global_config['python_path'], python_path=global_config['python_path'],
command_mappings=mappings_str command_mappings=mappings_str,
sender_file=sender_file,
device_protocol=device_protocol.upper()
) )
output_file = os.path.join(global_config['base_dir'], 'bin', f"{device['id']}.py") output_file = os.path.join(global_config['base_dir'], 'bin', f"{device['id']}.py")
@@ -101,14 +123,15 @@ def generate_listener(device, global_config, mappings):
f.write(content) f.write(content)
os.chmod(output_file, 0o755) os.chmod(output_file, 0o755)
print(f"[生成] {output_file} ({protocol.upper()})") print(f"[生成] {output_file} (监听:{listen_protocol.upper()}, 设备:{device_protocol.upper()})")
return output_file return output_file
def generate_crond(device, global_config): def generate_crond(device, global_config):
"""生成保活检查文件""" """生成保活检查文件"""
protocol = device.get('protocol', 'tcp').lower() listen_protocol = device.get('listen_protocol', 'tcp').lower()
template_file = get_template_path(protocol, 'crond') device_protocol = device.get('device_protocol', 'tcp').lower()
template_file = get_template_path(listen_protocol, 'crond')
# 检查模板文件是否存在 # 检查模板文件是否存在
if not os.path.exists(template_file): if not os.path.exists(template_file):
@@ -124,7 +147,8 @@ def generate_crond(device, global_config):
tms_server_ip=global_config['tms_server_ip'], tms_server_ip=global_config['tms_server_ip'],
listen_port=device['listen_port'], listen_port=device['listen_port'],
base_dir=global_config['base_dir'], base_dir=global_config['base_dir'],
python_path=global_config['python_path'] python_path=global_config['python_path'],
device_protocol=device_protocol.upper()
) )
output_file = os.path.join(global_config['base_dir'], 'crond', f"upcrond-{device['id']}.py") output_file = os.path.join(global_config['base_dir'], 'crond', f"upcrond-{device['id']}.py")
@@ -132,14 +156,15 @@ def generate_crond(device, global_config):
f.write(content) f.write(content)
os.chmod(output_file, 0o755) os.chmod(output_file, 0o755)
print(f"[生成] {output_file} ({protocol.upper()})") print(f"[生成] {output_file} (监听:{listen_protocol.upper()}, 设备:{device_protocol.upper()})")
return output_file return output_file
def generate_sender(commands, global_config, protocol='tcp'): def generate_sender_for_device(device, command_set, global_config):
"""生成发送模块文件""" """为指定设备生成独立的发送模块文件"""
protocol = protocol.lower() device_id = device['id']
template_file = get_template_path(protocol, 'sender') device_protocol = device.get('device_protocol', device.get('protocol', 'tcp')).lower()
template_file = get_template_path(device_protocol, 'sender')
# 检查模板文件是否存在 # 检查模板文件是否存在
if not os.path.exists(template_file): if not os.path.exists(template_file):
@@ -150,6 +175,7 @@ def generate_sender(commands, global_config, protocol='tcp'):
template = f.read() template = f.read()
# 构建指令定义字典字符串 # 构建指令定义字典字符串
commands = command_set.get('commands', {})
cmd_defs = "{\n" cmd_defs = "{\n"
for cmd, hex_str in commands.items(): for cmd, hex_str in commands.items():
cmd_defs += f' "{cmd}": "{hex_str}",\n' cmd_defs += f' "{cmd}": "{hex_str}",\n'
@@ -159,31 +185,39 @@ def generate_sender(commands, global_config, protocol='tcp'):
command_definitions=cmd_defs command_definitions=cmd_defs
) )
# 根据协议生成不同的文件 # 为每个设备生成独立的 sender 文件
output_file = os.path.join(global_config['base_dir'], 'bin', f'sender_{protocol}.py') output_file = os.path.join(global_config['base_dir'], 'bin', f'sender_{device_id}.py')
with open(output_file, 'w', encoding='utf-8') as f: with open(output_file, 'w', encoding='utf-8') as f:
f.write(content) f.write(content)
os.chmod(output_file, 0o755) os.chmod(output_file, 0o755)
print(f"[生成] {output_file} ({protocol.upper()})") print(f"[生成] {output_file} ({device_protocol.upper()}, 指令集: {command_set.get('name', '未命名')})")
return output_file return output_file
def generate_all_senders(config, global_config): def generate_all_senders(config, global_config):
"""为所有使用到的协议生成 sender 文件""" """为所有设备生成独立的 sender 文件"""
commands = config['commands'] command_sets = config.get('command_sets', {})
devices = config['devices'] devices = config['devices']
# 收集所有使用的协议类型
protocols = set()
for device in devices:
if device.get('enabled', True):
protocol = device.get('protocol', 'tcp').lower()
protocols.add(protocol)
generated = [] generated = []
for protocol in protocols: for device in devices:
generated.append(generate_sender(commands, global_config, protocol)) if not device.get('enabled', True):
continue
# 获取设备使用的指令集
command_set_id = device.get('command_set', 'default')
if command_set_id not in command_sets:
# 兼容旧配置:如果找不到指令集,使用全局 commands
if 'commands' in config:
command_set = {'name': '全局指令', 'commands': config['commands']}
else:
print(f"[!] 警告: 设备 {device['id']} 指定的指令集 '{command_set_id}' 不存在")
continue
else:
command_set = command_sets[command_set_id]
generated.append(generate_sender_for_device(device, command_set, global_config))
return generated return generated
@@ -220,13 +254,16 @@ def generate_control_script(devices, global_config):
device_list = [] device_list = []
for device in devices: for device in devices:
if device.get('enabled', True): if device.get('enabled', True):
listen_protocol = device.get('listen_protocol', device.get('protocol', 'tcp')).upper()
device_protocol = device.get('device_protocol', device.get('protocol', 'tcp')).upper()
protocol_str = f"{listen_protocol}->{device_protocol}"
device_list.append({ device_list.append({
'id': device['id'], 'id': device['id'],
'name': device['name'], 'name': device['name'],
'protocol': device.get('protocol', 'tcp').upper() 'protocol': protocol_str
}) })
# 构建设备列表字符串 # 构建设备列表字符串: ID 名称 协议
device_defs = "\n".join([ device_defs = "\n".join([
f" \"{d['id']}\" \"{d['name']}\" \"{d['protocol']}\"" f" \"{d['id']}\" \"{d['name']}\" \"{d['protocol']}\""
for d in device_list for d in device_list
@@ -253,7 +290,8 @@ def check_config(config):
global_cfg = config.get('global', {}) global_cfg = config.get('global', {})
devices = config.get('devices', []) devices = config.get('devices', [])
commands = config.get('commands', {}) command_sets = config.get('command_sets', {})
commands = config.get('commands', {}) # 兼容旧配置
mappings = config.get('mappings', {}) mappings = config.get('mappings', {})
# 检查全局配置 # 检查全局配置
@@ -269,6 +307,15 @@ def check_config(config):
device_ids = [] device_ids = []
listen_ports = [] listen_ports = []
# 收集所有指令(用于验证映射)
all_commands = set()
if command_sets:
for set_id, cmd_set in command_sets.items():
if 'commands' in cmd_set:
all_commands.update(cmd_set['commands'].keys())
if commands:
all_commands.update(commands.keys())
for device in devices: for device in devices:
device_id = device.get('id') device_id = device.get('id')
if not device_id: if not device_id:
@@ -284,10 +331,23 @@ def check_config(config):
if field not in device: if field not in device:
errors.append(f"设备 {device_id} 缺少字段: {field}") errors.append(f"设备 {device_id} 缺少字段: {field}")
# 检查协议类型 # 检查协议类型(支持新旧配置兼容)
protocol = device.get('protocol', 'tcp').lower() listen_protocol = device.get('listen_protocol', device.get('protocol', 'tcp')).lower()
if protocol not in ['tcp', 'udp']: device_protocol = device.get('device_protocol', device.get('protocol', 'tcp')).lower()
errors.append(f"设备 {device_id} 的协议类型无效: {protocol} (必须是 tcp 或 udp)")
if listen_protocol not in ['tcp', 'udp']:
errors.append(f"设备 {device_id} 的监听协议类型无效: {listen_protocol} (必须是 tcp 或 udp)")
if device_protocol not in ['tcp', 'udp']:
errors.append(f"设备 {device_id} 的设备协议类型无效: {device_protocol} (必须是 tcp 或 udp)")
# 检查指令集
if 'command_set' in device:
command_set_id = device['command_set']
if command_set_id not in command_sets:
errors.append(f"设备 {device_id} 指定的指令集 '{command_set_id}' 不存在")
elif not commands and not command_sets:
warnings.append(f"设备 {device_id} 未指定指令集,且没有全局指令")
if 'listen_port' in device: if 'listen_port' in device:
port = device['listen_port'] port = device['listen_port']
@@ -295,27 +355,36 @@ def check_config(config):
errors.append(f"设备 {device_id} 的监听端口 {port} 与其他设备冲突") errors.append(f"设备 {device_id} 的监听端口 {port} 与其他设备冲突")
listen_ports.append(port) listen_ports.append(port)
# 检查指令定义 # 检查指令
if not commands: if not command_sets and not commands:
warnings.append("没有定义任何指令") warnings.append("没有定义任何指令集或指令")
# 检查映射 # 检查映射
for cmd, op in mappings.items(): for cmd, op in mappings.items():
if op not in commands and not any(op == f"open{i}" or op == f"close{i}" for i in range(1, 9)): if op not in all_commands and not any(op == f"open{i}" or op == f"close{i}" for i in range(1, 9)):
warnings.append(f"映射 '{cmd}' -> '{op}' 的目标指令未在 commands 中定义") warnings.append(f"映射 '{cmd}' -> '{op}' 的目标指令未在任何指令集中定义")
# 检查模板文件是否存在 # 检查模板文件是否存在
protocols = set() listen_protocols = set()
device_protocols = set()
for device in devices: for device in devices:
if device.get('enabled', True): if device.get('enabled', True):
protocols.add(device.get('protocol', 'tcp').lower()) listen_protocols.add(device.get('listen_protocol', device.get('protocol', 'tcp')).lower())
device_protocols.add(device.get('device_protocol', device.get('protocol', 'tcp')).lower())
for protocol in protocols: # 检查监听协议模板
for template_type in ['listener', 'crond', 'sender']: for protocol in listen_protocols:
for template_type in ['listener', 'crond']:
template_file = get_template_path(protocol, template_type) template_file = get_template_path(protocol, template_type)
if not os.path.exists(template_file): if not os.path.exists(template_file):
warnings.append(f"模板文件不存在: {template_file}") warnings.append(f"模板文件不存在: {template_file}")
# 检查设备协议模板sender
for protocol in device_protocols:
template_file = get_template_path(protocol, 'sender')
if not os.path.exists(template_file):
warnings.append(f"模板文件不存在: {template_file}")
# 输出检查结果 # 输出检查结果
if errors: if errors:
print("\n[!] 配置错误:") print("\n[!] 配置错误:")
@@ -381,9 +450,11 @@ def main():
# 2. 为每个设备生成监听服务和保活脚本 # 2. 为每个设备生成监听服务和保活脚本
print("\n[生成设备服务]") print("\n[生成设备服务]")
command_sets = config.get('command_sets', {})
global_commands = config.get('commands', {})
for device in devices: for device in devices:
print(f"\n处理设备: {device['name']} ({device['id']}) - {device.get('protocol', 'tcp').upper()}") print(f"\n处理设备: {device['name']} ({device['id']}) - {device.get('protocol', 'tcp').upper()}")
generated_files.append(generate_listener(device, global_config, config['mappings'])) generated_files.append(generate_listener(device, global_config, config['mappings'], command_sets, global_commands))
generated_files.append(generate_crond(device, global_config)) generated_files.append(generate_crond(device, global_config))
# 3. 生成 crontab # 3. 生成 crontab

View File

@@ -5,7 +5,7 @@
BASE_DIR="{base_dir}" BASE_DIR="{base_dir}"
PYTHON_PATH="{python_path}" PYTHON_PATH="{python_path}"
# 设备列表: ID 名称 协议 # 设备列表: ID 名称 监听协议 设备协议
DEVICES=( DEVICES=(
{device_defs} {device_defs}
) )

View File

@@ -14,7 +14,7 @@ TMS_SERVER_IP = '{tms_server_ip}'
TMS_PORT = {listen_port} TMS_PORT = {listen_port}
UPC_DEV_IP = '{upc_ip}' UPC_DEV_IP = '{upc_ip}'
UPC_DEV_PORT = '{upc_port}' UPC_DEV_PORT = '{upc_port}'
SENDER_FILE = '{base_dir}/bin/sender_tcp.py' SENDER_FILE = '{sender_file}'
LOG_FILE = '{base_dir}/log/{device_id}.log' LOG_FILE = '{base_dir}/log/{device_id}.log'
PYTHON_PATH = '{python_path}' PYTHON_PATH = '{python_path}'
DEVICE_ID = '{device_id}' DEVICE_ID = '{device_id}'

View File

@@ -14,7 +14,7 @@ TMS_SERVER_IP = '{tms_server_ip}'
TMS_PORT = {listen_port} TMS_PORT = {listen_port}
UPC_DEV_IP = '{upc_ip}' UPC_DEV_IP = '{upc_ip}'
UPC_DEV_PORT = {upc_port} UPC_DEV_PORT = {upc_port}
SENDER_FILE = '{base_dir}/bin/sender_udp.py' SENDER_FILE = '{sender_file}'
LOG_FILE = '{base_dir}/log/{device_id}.log' LOG_FILE = '{base_dir}/log/{device_id}.log'
PYTHON_PATH = '{python_path}' PYTHON_PATH = '{python_path}'
DEVICE_ID = '{device_id}' DEVICE_ID = '{device_id}'