diff --git a/README.md b/README.md index 10686a7..ff174b7 100644 --- a/README.md +++ b/README.md @@ -42,11 +42,24 @@ upc_resent/ "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", - "protocol": "tcp", // 协议类型: tcp 或 udp + "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, @@ -55,7 +68,9 @@ upc_resent/ { "id": "dev2", "name": "设备2-UDP", - "protocol": "udp", // 使用UDP协议 + "listen_protocol": "udp", + "device_protocol": "udp", + "command_set": "set1", "upc_ip": "192.168.8.200", "upc_port": 502, "listen_port": 10080, @@ -112,26 +127,93 @@ crontab crontab.txt |-----|------|-----| | `id` | 设备唯一标识(用于文件名) | `"dev1"` | | `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_port` | 控制设备的端口(Modbus默认502) | `502` | | `listen_port` | 本机监听端口(每个设备需不同) | `10079` | | `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 { - "commands": { - "open1": "000100000008010F006400010001", - "close1": "000100000008010F006400010000", - "openall4": "000100000008010F00640004000F", - "closeall4": "000100000008010F006400040000" + "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" + } + } } } ``` -格式:指令名 -> 16进制字符串 +设备通过 `command_set` 字段指定使用的指令集: + +```json +{ + "id": "dev1", + "command_set": "set1" +} +``` #### 指令映射 (mappings) @@ -157,7 +239,7 @@ crontab crontab.txt ## 如何适配自己的设备 -### 场景1:添加 TCP 设备 +### 场景1:添加 TCP 设备(客户端TCP -> 主机TCP -> 设备TCP) ```json { @@ -165,7 +247,8 @@ crontab crontab.txt { "id": "dev_tcp", "name": "TCP设备", - "protocol": "tcp", + "listen_protocol": "tcp", + "device_protocol": "tcp", "upc_ip": "192.168.1.100", "upc_port": 502, "listen_port": 10081, @@ -175,7 +258,7 @@ crontab crontab.txt } ``` -### 场景2:添加 UDP 设备 +### 场景2:添加 UDP 设备(客户端UDP -> 主机UDP -> 设备UDP) ```json { @@ -183,7 +266,8 @@ crontab crontab.txt { "id": "dev_udp", "name": "UDP设备", - "protocol": "udp", + "listen_protocol": "udp", + "device_protocol": "udp", "upc_ip": "192.168.1.200", "upc_port": 502, "listen_port": 10082, @@ -193,18 +277,86 @@ crontab crontab.txt } ``` -### 场景3:添加新的控制指令 +### 场景3:协议转换(客户端TCP -> 主机UDP -> 设备UDP) ```json { - "commands": { - "open9": "000100000008010F006C00010001", - "close9": "000100000008010F006C00010000" - } + "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:禁用某个设备 +### 场景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 { diff --git a/config/devices.json b/config/devices.json index c174608..fe0d744 100644 --- a/config/devices.json +++ b/config/devices.json @@ -4,11 +4,60 @@ "python_path": "/usr/bin/python3", "base_dir": "/home/smart/pythonPJ/upc_resent" }, + "command_sets": { + "set1": { + "name": "标准8路控制", + "commands": { + "open1": "000100000008010F006400010001", + "close1": "000100000008010F006400010000", + "open2": "000100000008010F006500010001", + "close2": "000100000008010F006500010000", + "open3": "000100000008010F006600010001", + "close3": "000100000008010F006600010000", + "open4": "000100000008010F006700010001", + "close4": "000100000008010F006700010000", + "openall4": "000100000008010F00640004000F", + "closeall4": "000100000008010F006400040000", + "open5": "000100000008010F006800010001", + "close5": "000100000008010F006800010000", + "open6": "000100000008010F006900010001", + "close6": "000100000008010F006900010000", + "open7": "000100000008010F006A00010001", + "close7": "000100000008010F006A00010000", + "open8": "000100000008010F006B00010001", + "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": "设备73-TCP", - "protocol": "tcp", + "name": "设备1-8路控制", + "listen_protocol": "tcp", + "device_protocol": "tcp", + "command_set": "set1", "upc_ip": "192.168.8.73", "upc_port": 502, "listen_port": 10079, @@ -16,8 +65,10 @@ }, { "id": "dev2", - "name": "设备125-TCP", - "protocol": "tcp", + "name": "设备2-8路控制", + "listen_protocol": "tcp", + "device_protocol": "tcp", + "command_set": "set1", "upc_ip": "192.168.8.125", "upc_port": 502, "listen_port": 10129, @@ -25,34 +76,27 @@ }, { "id": "dev3", - "name": "设备UDP示例", - "protocol": "udp", + "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 } ], - "commands": { - "open1": "000100000008010F006400010001", - "close1": "000100000008010F006400010000", - "open2": "000100000008010F006500010001", - "close2": "000100000008010F006500010000", - "open3": "000100000008010F006600010001", - "close3": "000100000008010F006600010000", - "open4": "000100000008010F006700010001", - "close4": "000100000008010F006700010000", - "openall4": "000100000008010F00640004000F", - "closeall4": "000100000008010F006400040000", - "open5": "000100000008010F006800010001", - "close5": "000100000008010F006800010000", - "open6": "000100000008010F006900010001", - "close6": "000100000008010F006900010000", - "open7": "000100000008010F006A00010001", - "close7": "000100000008010F006A00010000", - "open8": "000100000008010F006B00010001", - "close8": "000100000008010F006B00010000" - }, "mappings": { "open": "openall4", "close": "closeall4", diff --git a/control.sh b/control.sh deleted file mode 100755 index b77937e..0000000 --- a/control.sh +++ /dev/null @@ -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 diff --git a/crontab.txt b/crontab.txt deleted file mode 100644 index 2b9f3a9..0000000 --- a/crontab.txt +++ /dev/null @@ -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 diff --git a/scripts/generate.py b/scripts/generate.py index 2c7c9f2..9181f86 100644 --- a/scripts/generate.py +++ b/scripts/generate.py @@ -60,10 +60,11 @@ def get_template_path(protocol, 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() - template_file = get_template_path(protocol, 'listener') + listen_protocol = device.get('listen_protocol', 'tcp').lower() + device_protocol = device.get('device_protocol', 'tcp').lower() + template_file = get_template_path(listen_protocol, 'listener') # 检查模板文件是否存在 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: 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" - for cmd, op in mappings.items(): + for cmd, op in device_mappings.items(): 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 += " }" + # 确定使用哪个 sender 脚本(每个设备独立的 sender) + sender_file = f"{global_config['base_dir']}/bin/sender_{device['id']}.py" + # 替换模板变量 content = template.format( device_id=device['id'], @@ -93,7 +113,9 @@ def generate_listener(device, global_config, mappings): upc_port=device['upc_port'], base_dir=global_config['base_dir'], 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") @@ -101,14 +123,15 @@ def generate_listener(device, global_config, mappings): f.write(content) os.chmod(output_file, 0o755) - print(f"[生成] {output_file} ({protocol.upper()})") + print(f"[生成] {output_file} (监听:{listen_protocol.upper()}, 设备:{device_protocol.upper()})") return output_file def generate_crond(device, global_config): """生成保活检查文件""" - protocol = device.get('protocol', 'tcp').lower() - template_file = get_template_path(protocol, 'crond') + listen_protocol = device.get('listen_protocol', 'tcp').lower() + device_protocol = device.get('device_protocol', 'tcp').lower() + template_file = get_template_path(listen_protocol, 'crond') # 检查模板文件是否存在 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'], listen_port=device['listen_port'], 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") @@ -132,14 +156,15 @@ def generate_crond(device, global_config): f.write(content) os.chmod(output_file, 0o755) - print(f"[生成] {output_file} ({protocol.upper()})") + print(f"[生成] {output_file} (监听:{listen_protocol.upper()}, 设备:{device_protocol.upper()})") return output_file -def generate_sender(commands, global_config, protocol='tcp'): - """生成发送模块文件""" - protocol = protocol.lower() - template_file = get_template_path(protocol, 'sender') +def generate_sender_for_device(device, command_set, global_config): + """为指定设备生成独立的发送模块文件""" + device_id = device['id'] + 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): @@ -150,6 +175,7 @@ def generate_sender(commands, global_config, protocol='tcp'): template = f.read() # 构建指令定义字典字符串 + commands = command_set.get('commands', {}) cmd_defs = "{\n" for cmd, hex_str in commands.items(): cmd_defs += f' "{cmd}": "{hex_str}",\n' @@ -159,31 +185,39 @@ def generate_sender(commands, global_config, protocol='tcp'): command_definitions=cmd_defs ) - # 根据协议生成不同的文件名 - output_file = os.path.join(global_config['base_dir'], 'bin', f'sender_{protocol}.py') + # 为每个设备生成独立的 sender 文件 + 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: f.write(content) 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 def generate_all_senders(config, global_config): - """为所有使用到的协议生成 sender 文件""" - commands = config['commands'] + """为所有设备生成独立的 sender 文件""" + command_sets = config.get('command_sets', {}) devices = config['devices'] - # 收集所有使用的协议类型 - protocols = set() - for device in devices: - if device.get('enabled', True): - protocol = device.get('protocol', 'tcp').lower() - protocols.add(protocol) - generated = [] - for protocol in protocols: - generated.append(generate_sender(commands, global_config, protocol)) + for device in devices: + 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 @@ -220,13 +254,16 @@ def generate_control_script(devices, global_config): device_list = [] for device in devices: 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({ 'id': device['id'], 'name': device['name'], - 'protocol': device.get('protocol', 'tcp').upper() + 'protocol': protocol_str }) - # 构建设备列表字符串 + # 构建设备列表字符串: ID 名称 协议 device_defs = "\n".join([ f" \"{d['id']}\" \"{d['name']}\" \"{d['protocol']}\"" for d in device_list @@ -253,7 +290,8 @@ def check_config(config): global_cfg = config.get('global', {}) devices = config.get('devices', []) - commands = config.get('commands', {}) + command_sets = config.get('command_sets', {}) + commands = config.get('commands', {}) # 兼容旧配置 mappings = config.get('mappings', {}) # 检查全局配置 @@ -269,6 +307,15 @@ def check_config(config): device_ids = [] 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: device_id = device.get('id') if not device_id: @@ -284,10 +331,23 @@ def check_config(config): if field not in device: errors.append(f"设备 {device_id} 缺少字段: {field}") - # 检查协议类型 - protocol = device.get('protocol', 'tcp').lower() - if protocol not in ['tcp', 'udp']: - errors.append(f"设备 {device_id} 的协议类型无效: {protocol} (必须是 tcp 或 udp)") + # 检查协议类型(支持新旧配置兼容) + listen_protocol = device.get('listen_protocol', device.get('protocol', 'tcp')).lower() + device_protocol = device.get('device_protocol', device.get('protocol', 'tcp')).lower() + + 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: port = device['listen_port'] @@ -295,27 +355,36 @@ def check_config(config): errors.append(f"设备 {device_id} 的监听端口 {port} 与其他设备冲突") listen_ports.append(port) - # 检查指令定义 - if not commands: - warnings.append("没有定义任何指令") + # 检查指令集 + if not command_sets and not commands: + warnings.append("没有定义任何指令集或指令") # 检查映射 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)): - warnings.append(f"映射 '{cmd}' -> '{op}' 的目标指令未在 commands 中定义") + 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}' 的目标指令未在任何指令集中定义") # 检查模板文件是否存在 - protocols = set() + listen_protocols = set() + device_protocols = set() for device in devices: 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) if not os.path.exists(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: print("\n[!] 配置错误:") @@ -381,9 +450,11 @@ def main(): # 2. 为每个设备生成监听服务和保活脚本 print("\n[生成设备服务]") + command_sets = config.get('command_sets', {}) + global_commands = config.get('commands', {}) for device in devices: 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)) # 3. 生成 crontab diff --git a/templates/control.sh.tpl b/templates/control.sh.tpl index 31a2349..b2234b8 100644 --- a/templates/control.sh.tpl +++ b/templates/control.sh.tpl @@ -5,7 +5,7 @@ BASE_DIR="{base_dir}" PYTHON_PATH="{python_path}" -# 设备列表: ID 名称 协议 +# 设备列表: ID 名称 监听协议 设备协议 DEVICES=( {device_defs} ) diff --git a/templates/listener_tcp.py.tpl b/templates/listener_tcp.py.tpl index 72aafe5..1e3cba5 100644 --- a/templates/listener_tcp.py.tpl +++ b/templates/listener_tcp.py.tpl @@ -14,7 +14,7 @@ TMS_SERVER_IP = '{tms_server_ip}' TMS_PORT = {listen_port} UPC_DEV_IP = '{upc_ip}' 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' PYTHON_PATH = '{python_path}' DEVICE_ID = '{device_id}' diff --git a/templates/listener_udp.py.tpl b/templates/listener_udp.py.tpl index aad547c..6f8b5de 100644 --- a/templates/listener_udp.py.tpl +++ b/templates/listener_udp.py.tpl @@ -14,7 +14,7 @@ TMS_SERVER_IP = '{tms_server_ip}' TMS_PORT = {listen_port} UPC_DEV_IP = '{upc_ip}' 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' PYTHON_PATH = '{python_path}' DEVICE_ID = '{device_id}'