commit b64c39ecbd0da0acd12f4467fa43ef2b2fe633b6 Author: zj <1052308357@qq.com> Date: Sun Mar 22 17:17:44 2026 +0800 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..4887bd4 --- /dev/null +++ b/README.md @@ -0,0 +1,295 @@ +# 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发送模块模板 +├── scripts/ +│ └── generate.py # 代码生成器 +├── bin/ # 生成的监听服务文件(自动生成) +│ ├── sender_tcp.py # TCP发送模块 +│ ├── sender_udp.py # UDP发送模块 +│ └── {device_id}.py # 各设备监听服务 +├── crond/ # 生成的保活检查文件(自动生成) +├── log/ # 日志目录 +├── start.sh # 启动脚本(自动生成) +├── crontab.txt # 定时任务配置(自动生成) +└── cutlog.sh # 日志清理脚本 +``` + +## 快速开始 + +### 1. 配置设备 + +编辑 `config/devices.json`: + +```json +{ + "global": { + "tms_server_ip": "192.168.8.9", + "python_path": "/usr/bin/python3", + "base_dir": "/opt/upc_resent" + }, + "devices": [ + { + "id": "dev1", + "name": "设备1-TCP", + "protocol": "tcp", // 协议类型: tcp 或 udp + "upc_ip": "192.168.8.73", + "upc_port": 502, + "listen_port": 10079, + "enabled": true + }, + { + "id": "dev2", + "name": "设备2-UDP", + "protocol": "udp", // 使用UDP协议 + "upc_ip": "192.168.8.200", + "upc_port": 502, + "listen_port": 10080, + "enabled": true + } + ] +} +``` + +### 2. 生成代码 + +```bash +# 检查配置是否正确 +python3 scripts/generate.py --check + +# 生成代码 +python3 scripts/generate.py +``` + +### 3. 部署与启动 + +```bash +# 启动所有服务 +./start.sh + +# 配置定时任务(保活) +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"` | +| `protocol` | 协议类型:`tcp` 或 `udp` | `"tcp"` / `"udp"` | +| `upc_ip` | 控制设备的IP地址 | `"192.168.8.73"` | +| `upc_port` | 控制设备的端口(Modbus默认502) | `502` | +| `listen_port` | 本机监听端口(每个设备需不同) | `10079` | +| `enabled` | 是否启用该设备 | `true` / `false` | + +#### 指令定义 (commands) + +```json +{ + "commands": { + "open1": "000100000008010F006400010001", + "close1": "000100000008010F006400010000", + "openall4": "000100000008010F00640004000F", + "closeall4": "000100000008010F006400040000" + } +} +``` + +格式:指令名 -> 16进制字符串 + +#### 指令映射 (mappings) + +```json +{ + "mappings": { + "open": "openall4", + "close": "closeall4", + "guanggao-guan": "closeall4" + } +} +``` + +## TCP vs UDP 差异 + +| 特性 | TCP | UDP | +|-----|-----|-----| +| 连接方式 | 面向连接 | 无连接 | +| 可靠性 | 可靠传输 | 尽力传输 | +| 响应 | 有确认响应 | 可选响应(可能超时) | +| 适用场景 | 可靠性要求高 | 实时性要求高 | +| 保活检查 | 端口连通性检查 | 进程状态检查 | + +## 如何适配自己的设备 + +### 场景1:添加 TCP 设备 + +```json +{ + "devices": [ + { + "id": "dev_tcp", + "name": "TCP设备", + "protocol": "tcp", + "upc_ip": "192.168.1.100", + "upc_port": 502, + "listen_port": 10081, + "enabled": true + } + ] +} +``` + +### 场景2:添加 UDP 设备 + +```json +{ + "devices": [ + { + "id": "dev_udp", + "name": "UDP设备", + "protocol": "udp", + "upc_ip": "192.168.1.200", + "upc_port": 502, + "listen_port": 10082, + "enabled": true + } + ] +} +``` + +### 场景3:添加新的控制指令 + +```json +{ + "commands": { + "open9": "000100000008010F006C00010001", + "close9": "000100000008010F006C00010000" + } +} +``` + +### 场景4:禁用某个设备 + +```json +{ + "devices": [ + { + "id": "dev2", + "enabled": false + } + ] +} +``` + +## 测试指令发送 + +### TCP 设备测试 + +```bash +# 向TCP设备发送开灯指令 +echo "open1" | nc 192.168.8.9 10079 + +# 向TCP设备发送全关指令 +echo "close" | nc 192.168.8.9 10079 +``` + +### UDP 设备测试 + +```bash +# 向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=写多个线圈) + │ │ │ │ │ │ └──────────── 单元标识 + │ │ │ │ └───────────────── 协议标识 + │ │ │ └─────────────────── 长度 + │ └─────────────────────── 事务标识 + └────────────────────────── 事务标识 +``` + +## 代码生成器用法 + +```bash +# 检查配置是否正确 +python3 scripts/generate.py --check + +# 生成代码 +python3 scripts/generate.py + +# 查看帮助 +python3 scripts/generate.py --help +``` + +## 日志文件 + +| 文件 | 说明 | +|-----|------| +| `log/{device_id}.log` | 设备监听服务的日志 | +| `log/crond-{device_id}.log` | 保活检查的日志 | + +日志自动轮转:每天清理7天前的日志。 + +## 注意事项 + +1. **不要直接修改** `bin/` 和 `crond/` 下的文件,这些文件是自动生成的,重新生成时会覆盖。 + +2. **修改配置后必须重新运行** `python3 scripts/generate.py` 才能生效。 + +3. 确保每个设备的 `listen_port` 不冲突,无论 TCP 还是 UDP,端口都不能重复。 + +4. UDP 是无连接协议,设备可能不会返回响应,这是正常现象。 + +5. 部署到生产环境前,请将 `base_dir` 修改为实际的部署路径(如 `/opt/upc_resent`)。 diff --git a/bin/192.168.8.125.py b/bin/192.168.8.125.py new file mode 100644 index 0000000..9419b4e --- /dev/null +++ b/bin/192.168.8.125.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- + +import socket +import os +import time +import logging +import sys +# reload(sys) 和 sys.setdefaultencoding('utf-8') 在 Python 3 中不再需要 + +if __name__ == '__main__': + + #======修改以下参数====== + TmsServerIp='192.168.8.9' #TMS服务器IP,用于绑定服务,等待外部指令 + TmsPor='10129' #TMS服务器端口,用于绑定服务,等待外部指令 + upc_dev_ip="192.168.8.125" #自动化控制设备IP + upc_dev_port="502" #自动化控制设备端口 + # 注意:Senderfile 指向的 sender.py 脚本也需要是 Python 3 版本! + Senderfile='/opt/upc_resent/bin/sender.py' #发送程序位置 + Logfile='/opt/upc_resent/log/192.168.8.125.log' #日志文件位置 + pythonpath="/usr/bin/python3" # Python 3 执行文件的位置,可在操作系统中使用which python3命令获取 + #======================== + + BUFSIZE = 1024 + + # 配置日志,只执行一次 + logging.basicConfig(filename=Logfile,filemode="a", level=logging.DEBUG) + ISOTIMEFORMAT='%Y-%m-%d %X' + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.bind((TmsServerIp, int(TmsPor))) #启动TMS本地服务,等待外部设备发送指令 + sock.listen(500) + except OSError as e: # 在 Python 3 中使用 OSError 替代 socket.error + logstr="!!!!!ERROR!!!!!"+time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) )+" 无法绑定端口 %s:%s - %s" % (TmsServerIp, TmsPor, e) + logging.error(logstr) + print(logstr) # Python 3 print 函数 + sys.exit(1) # 绑定失败则退出 + + logging.info("服务启动,监听 %s:%s" % (TmsServerIp, TmsPor)) + + while True: + connection,address = sock.accept() + try: + connection.settimeout(5) #GDC系统长连接 + # 接收到的数据是 bytes,需要解码成 str 进行比较 + buf_bytes = connection.recv(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() # 解码成 str 并移除空白符 + + operation = "nodata" # 默认值,如果没有匹配的命令 + + datetime_str=time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) ) + logstr="==="+datetime_str+"收到客户端"+ address[0]+":"+str(address[1])+"发送的指令: '" +str(buf)+"'" + logging.info(logstr) + + # 根据接收到的外部命令,映射到单个内部操作指令 + if buf == 'open': + operation = 'openall4' + elif buf == 'close': + operation = 'closeall4' + elif buf == 'open1': + operation = 'open1' + elif buf == 'close1': + operation = 'close1' + elif buf == 'open2': + operation = 'open2' + elif buf == 'close2': + operation = 'close2' + elif buf == 'open3': + operation = 'open3' + elif buf == 'close3': + operation = 'close3' + elif buf == 'open4': + operation = 'open4' + elif buf == 'close4': + operation = 'close4' + elif buf == 'open5': # 新增 5-8 路的命令映射 + operation = 'open5' + elif buf == 'close5': + operation = 'close5' + elif buf == 'open6': + operation = 'open6' + elif buf == 'close6': + operation = 'close6' + elif buf == 'open7': + operation = 'open7' + elif buf == 'close7': + operation = 'close7' + elif buf == 'open8': + operation = 'open8' + elif buf == 'close8': + operation = 'close8' + elif buf == 'guanggao-guan': # 外部命令 'guanggao-guan' 映射到 'closeall4' (全关) + operation = 'closeall4' + # else: operation 保持为 "nodata" + + if operation == "nodata": + logstr="==="+datetime_str+" 未知指令: '" +str(buf)+"'" + logging.warning(logstr) + print(logstr) # Python 3 print 函数 + # 给客户端一个反馈,需要编码成 bytes + connection.send(('Unknown command: %s' % buf).encode('utf-8')) + else: + logstr="==="+datetime_str+" 映射到内部指令: '" +str(operation)+"'" + logging.info(logstr) + # 直接执行一次外部脚本 + os_command=str(pythonpath)+" " +Senderfile+ " " + str(upc_dev_ip)+" "+str(upc_dev_port)+" "+str(operation)+" "+str(Logfile) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) # 执行发送指令的脚本 + # 成功处理后给客户端反馈,需要编码成 bytes + connection.send(('Command %s processed as %s' % (buf, operation)).encode('utf-8')) + except socket.timeout: + datetime_str=time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) ) + logstr="==="+datetime_str+" 客户端连接超时: "+ address[0]+":"+str(address[1])+"===" + logging.warning(logstr) + print('time out') # Python 3 print 函数 + except Exception as e: # 捕获其他可能的异常 + datetime_str=time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) ) + logstr="==="+datetime_str+" 处理客户端 "+ address[0]+":"+str(address[1])+" 请求时发生错误: "+str(e)+"===" + logging.error(logstr, exc_info=True) # 记录异常详情 + print("Error processing request:", e) # Python 3 print 函数 + # 给客户端一个错误反馈,需要编码成 bytes + connection.send(('Error processing command: %s' % str(e)).encode('utf-8')) + finally: + connection.close() # 确保每次连接都被关闭 + diff --git a/bin/192.168.8.73.py b/bin/192.168.8.73.py new file mode 100644 index 0000000..0eb29d0 --- /dev/null +++ b/bin/192.168.8.73.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- + +import socket +import os +import time +import logging +import sys +# reload(sys) 和 sys.setdefaultencoding('utf-8') 在 Python 3 中不再需要 + +if __name__ == '__main__': + + #======修改以下参数====== + TmsServerIp='192.168.8.9' #TMS服务器IP,用于绑定服务,等待外部指令 + TmsPor='10079' #TMS服务器端口,用于绑定服务,等待外部指令 + upc_dev_ip="192.168.8.73" #自动化控制设备IP + upc_dev_port="502" #自动化控制设备端口 + # 注意:Senderfile 指向的 sender.py 脚本也需要是 Python 3 版本! + Senderfile='/opt/upc_resent/bin/sender.py' #发送程序位置 + Logfile='/opt/upc_resent/log/192.168.8.73.log' #日志文件位置 + pythonpath="/usr/bin/python3" # Python 3 执行文件的位置,可在操作系统中使用which python3命令获取 + #======================== + + BUFSIZE = 1024 + + # 配置日志,只执行一次 + logging.basicConfig(filename=Logfile,filemode="a", level=logging.DEBUG) + ISOTIMEFORMAT='%Y-%m-%d %X' + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.bind((TmsServerIp, int(TmsPor))) #启动TMS本地服务,等待外部设备发送指令 + sock.listen(500) + except OSError as e: # 在 Python 3 中使用 OSError 替代 socket.error + logstr="!!!!!ERROR!!!!!"+time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) )+" 无法绑定端口 %s:%s - %s" % (TmsServerIp, TmsPor, e) + logging.error(logstr) + print(logstr) # Python 3 print 函数 + sys.exit(1) # 绑定失败则退出 + + logging.info("服务启动,监听 %s:%s" % (TmsServerIp, TmsPor)) + + while True: + connection,address = sock.accept() + try: + connection.settimeout(5) #GDC系统长连接 + # 接收到的数据是 bytes,需要解码成 str 进行比较 + buf_bytes = connection.recv(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() # 解码成 str 并移除空白符 + + operation = "nodata" # 默认值,如果没有匹配的命令 + + datetime_str=time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) ) + logstr="==="+datetime_str+"收到客户端"+ address[0]+":"+str(address[1])+"发送的指令: '" +str(buf)+"'" + logging.info(logstr) + + # 根据接收到的外部命令,映射到单个内部操作指令 + if buf == 'open': + operation = 'openall4' + elif buf == 'close': + operation = 'closeall4' + elif buf == 'open1': + operation = 'open1' + elif buf == 'close1': + operation = 'close1' + elif buf == 'open2': + operation = 'open2' + elif buf == 'close2': + operation = 'close2' + elif buf == 'open3': + operation = 'open3' + elif buf == 'close3': + operation = 'close3' + elif buf == 'open4': + operation = 'open4' + elif buf == 'close4': + operation = 'close4' + elif buf == 'open5': # 新增 5-8 路的命令映射 + operation = 'open5' + elif buf == 'close5': + operation = 'close5' + elif buf == 'open6': + operation = 'open6' + elif buf == 'close6': + operation = 'close6' + elif buf == 'open7': + operation = 'open7' + elif buf == 'close7': + operation = 'close7' + elif buf == 'open8': + operation = 'open8' + elif buf == 'close8': + operation = 'close8' + elif buf == 'guanggao-guan': # 外部命令 'guanggao-guan' 映射到 'closeall4' (全关) + operation = 'closeall4' + # else: operation 保持为 "nodata" + + if operation == "nodata": + logstr="==="+datetime_str+" 未知指令: '" +str(buf)+"'" + logging.warning(logstr) + print(logstr) # Python 3 print 函数 + # 给客户端一个反馈,需要编码成 bytes + connection.send(('Unknown command: %s' % buf).encode('utf-8')) + else: + logstr="==="+datetime_str+" 映射到内部指令: '" +str(operation)+"'" + logging.info(logstr) + # 直接执行一次外部脚本 + os_command=str(pythonpath)+" " +Senderfile+ " " + str(upc_dev_ip)+" "+str(upc_dev_port)+" "+str(operation)+" "+str(Logfile) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) # 执行发送指令的脚本 + # 成功处理后给客户端反馈,需要编码成 bytes + connection.send(('Command %s processed as %s' % (buf, operation)).encode('utf-8')) + except socket.timeout: + datetime_str=time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) ) + logstr="==="+datetime_str+" 客户端连接超时: "+ address[0]+":"+str(address[1])+"===" + logging.warning(logstr) + print('time out') # Python 3 print 函数 + except Exception as e: # 捕获其他可能的异常 + datetime_str=time.strftime( ISOTIMEFORMAT, time.localtime( time.time() ) ) + logstr="==="+datetime_str+" 处理客户端 "+ address[0]+":"+str(address[1])+" 请求时发生错误: "+str(e)+"===" + logging.error(logstr, exc_info=True) # 记录异常详情 + print("Error processing request:", e) # Python 3 print 函数 + # 给客户端一个错误反馈,需要编码成 bytes + connection.send(('Error processing command: %s' % str(e)).encode('utf-8')) + finally: + connection.close() # 确保每次连接都被关闭 + diff --git a/bin/dev1.py b/bin/dev1.py new file mode 100755 index 0000000..b4c2a55 --- /dev/null +++ b/bin/dev1.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: 设备73-TCP (dev1) + +import socket +import os +import time +import logging +import sys + +# ====== 配置参数(从配置文件读取)====== +TMS_SERVER_IP = '192.168.8.9' +TMS_PORT = 10079 +UPC_DEV_IP = '192.168.8.73' +UPC_DEV_PORT = '502' +SENDER_FILE = '/home/smart/pythonPJ/upc_resent/bin/sender_tcp.py' +LOG_FILE = '/home/smart/pythonPJ/upc_resent/log/dev1.log' +PYTHON_PATH = '/usr/bin/python3' +DEVICE_ID = 'dev1' +# ========================= + +BUFSIZE = 1024 + +# 配置日志 +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 命令映射表 +COMMAND_MAPPINGS = { + "open": "openall4", + "close": "closeall4", + "guanggao-guan": "closeall4", + "open1": "open1", + "close1": "close1", + "open2": "open2", + "close2": "close2", + "open3": "open3", + "close3": "close3", + "open4": "open4", + "close4": "close4", + "open5": "open5", + "close5": "close5", + "open6": "open6", + "close6": "close6", + "open7": "open7", + "close7": "close7", + "open8": "open8", + "close8": "close8", + } + + +def process_command(buf): + """处理接收到的命令,返回对应的操作指令""" + buf = buf.strip() + if buf in COMMAND_MAPPINGS: + return COMMAND_MAPPINGS[buf] + return "nodata" + + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.bind((TMS_SERVER_IP, int(TMS_PORT))) + sock.listen(500) + except OSError as e: + logstr = "!!!!!ERROR!!!!!" + time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + \ + " 无法绑定端口 %s:%s - %s" % (TMS_SERVER_IP, TMS_PORT, e) + logging.error(logstr) + print(logstr) + sys.exit(1) + + logging.info("服务启动,监听 %s:%s" % (TMS_SERVER_IP, TMS_PORT)) + print(f"[{DEVICE_ID}] 服务启动,监听 {TMS_SERVER_IP}:{TMS_PORT}") + + while True: + connection, address = sock.accept() + try: + connection.settimeout(5) + buf_bytes = connection.recv(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 收到客户端 " + address[0] + ":" + str(address[1]) + \ + " 发送的指令: '" + str(buf) + "'" + logging.info(logstr) + print(logstr) + + operation = process_command(buf) + + if operation == "nodata": + logstr = "===" + datetime_str + " 未知指令: '" + str(buf) + "'" + logging.warning(logstr) + print(logstr) + connection.send(('Unknown command: %s' % buf).encode('utf-8')) + else: + logstr = "===" + datetime_str + " 映射到内部指令: '" + str(operation) + "'" + logging.info(logstr) + print(logstr) + + os_command = str(PYTHON_PATH) + " " + SENDER_FILE + " " + \ + str(UPC_DEV_IP) + " " + str(UPC_DEV_PORT) + " " + \ + str(operation) + " " + str(LOG_FILE) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) + connection.send(('Command %s processed as %s' % (buf, operation)).encode('utf-8')) + except socket.timeout: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 客户端连接超时: " + address[0] + ":" + str(address[1]) + "===" + logging.warning(logstr) + print('time out') + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 处理客户端 " + address[0] + ":" + str(address[1]) + \ + " 请求时发生错误: " + str(e) + "===" + logging.error(logstr, exc_info=True) + print("Error processing request:", e) + connection.send(('Error processing command: %s' % str(e)).encode('utf-8')) + finally: + connection.close() + + +if __name__ == '__main__': + main() diff --git a/bin/dev2.py b/bin/dev2.py new file mode 100755 index 0000000..c23c488 --- /dev/null +++ b/bin/dev2.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: 设备125-TCP (dev2) + +import socket +import os +import time +import logging +import sys + +# ====== 配置参数(从配置文件读取)====== +TMS_SERVER_IP = '192.168.8.9' +TMS_PORT = 10129 +UPC_DEV_IP = '192.168.8.125' +UPC_DEV_PORT = '502' +SENDER_FILE = '/home/smart/pythonPJ/upc_resent/bin/sender_tcp.py' +LOG_FILE = '/home/smart/pythonPJ/upc_resent/log/dev2.log' +PYTHON_PATH = '/usr/bin/python3' +DEVICE_ID = 'dev2' +# ========================= + +BUFSIZE = 1024 + +# 配置日志 +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 命令映射表 +COMMAND_MAPPINGS = { + "open": "openall4", + "close": "closeall4", + "guanggao-guan": "closeall4", + "open1": "open1", + "close1": "close1", + "open2": "open2", + "close2": "close2", + "open3": "open3", + "close3": "close3", + "open4": "open4", + "close4": "close4", + "open5": "open5", + "close5": "close5", + "open6": "open6", + "close6": "close6", + "open7": "open7", + "close7": "close7", + "open8": "open8", + "close8": "close8", + } + + +def process_command(buf): + """处理接收到的命令,返回对应的操作指令""" + buf = buf.strip() + if buf in COMMAND_MAPPINGS: + return COMMAND_MAPPINGS[buf] + return "nodata" + + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.bind((TMS_SERVER_IP, int(TMS_PORT))) + sock.listen(500) + except OSError as e: + logstr = "!!!!!ERROR!!!!!" + time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + \ + " 无法绑定端口 %s:%s - %s" % (TMS_SERVER_IP, TMS_PORT, e) + logging.error(logstr) + print(logstr) + sys.exit(1) + + logging.info("服务启动,监听 %s:%s" % (TMS_SERVER_IP, TMS_PORT)) + print(f"[{DEVICE_ID}] 服务启动,监听 {TMS_SERVER_IP}:{TMS_PORT}") + + while True: + connection, address = sock.accept() + try: + connection.settimeout(5) + buf_bytes = connection.recv(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 收到客户端 " + address[0] + ":" + str(address[1]) + \ + " 发送的指令: '" + str(buf) + "'" + logging.info(logstr) + print(logstr) + + operation = process_command(buf) + + if operation == "nodata": + logstr = "===" + datetime_str + " 未知指令: '" + str(buf) + "'" + logging.warning(logstr) + print(logstr) + connection.send(('Unknown command: %s' % buf).encode('utf-8')) + else: + logstr = "===" + datetime_str + " 映射到内部指令: '" + str(operation) + "'" + logging.info(logstr) + print(logstr) + + os_command = str(PYTHON_PATH) + " " + SENDER_FILE + " " + \ + str(UPC_DEV_IP) + " " + str(UPC_DEV_PORT) + " " + \ + str(operation) + " " + str(LOG_FILE) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) + connection.send(('Command %s processed as %s' % (buf, operation)).encode('utf-8')) + except socket.timeout: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 客户端连接超时: " + address[0] + ":" + str(address[1]) + "===" + logging.warning(logstr) + print('time out') + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 处理客户端 " + address[0] + ":" + str(address[1]) + \ + " 请求时发生错误: " + str(e) + "===" + logging.error(logstr, exc_info=True) + print("Error processing request:", e) + connection.send(('Error processing command: %s' % str(e)).encode('utf-8')) + finally: + connection.close() + + +if __name__ == '__main__': + main() diff --git a/bin/dev3.py b/bin/dev3.py new file mode 100755 index 0000000..0db408f --- /dev/null +++ b/bin/dev3.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: 设备UDP示例 (dev3) - UDP Protocol + +import socket +import os +import time +import logging +import sys + +# ====== 配置参数(从配置文件读取)====== +TMS_SERVER_IP = '192.168.8.9' +TMS_PORT = 10080 +UPC_DEV_IP = '192.168.8.200' +UPC_DEV_PORT = 502 +SENDER_FILE = '/home/smart/pythonPJ/upc_resent/bin/sender_udp.py' +LOG_FILE = '/home/smart/pythonPJ/upc_resent/log/dev3.log' +PYTHON_PATH = '/usr/bin/python3' +DEVICE_ID = 'dev3' +# ========================= + +BUFSIZE = 1024 + +# 配置日志 +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 命令映射表 +COMMAND_MAPPINGS = { + "open": "openall4", + "close": "closeall4", + "guanggao-guan": "closeall4", + "open1": "open1", + "close1": "close1", + "open2": "open2", + "close2": "close2", + "open3": "open3", + "close3": "close3", + "open4": "open4", + "close4": "close4", + "open5": "open5", + "close5": "close5", + "open6": "open6", + "close6": "close6", + "open7": "open7", + "close7": "close7", + "open8": "open8", + "close8": "close8", + } + + +def process_command(buf): + """处理接收到的命令,返回对应的操作指令""" + buf = buf.strip() + if buf in COMMAND_MAPPINGS: + return COMMAND_MAPPINGS[buf] + return "nodata" + + +def main(): + # 创建 UDP socket + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + sock.bind((TMS_SERVER_IP, int(TMS_PORT))) + except OSError as e: + logstr = "!!!!!ERROR!!!!!" + time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + \ + " 无法绑定UDP端口 %s:%s - %s" % (TMS_SERVER_IP, TMS_PORT, e) + logging.error(logstr) + print(logstr) + sys.exit(1) + + logging.info("UDP服务启动,监听 %s:%s" % (TMS_SERVER_IP, TMS_PORT)) + print(f"[{DEVICE_ID}] UDP服务启动,监听 {TMS_SERVER_IP}:{TMS_PORT}") + + while True: + try: + # UDP 使用 recvfrom 接收数据,同时获取客户端地址 + buf_bytes, client_addr = sock.recvfrom(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 收到UDP客户端 " + client_addr[0] + ":" + str(client_addr[1]) + \ + " 发送的指令: '" + str(buf) + "'" + logging.info(logstr) + print(logstr) + + operation = process_command(buf) + + if operation == "nodata": + logstr = "===" + datetime_str + " 未知指令: '" + str(buf) + "'" + logging.warning(logstr) + print(logstr) + # 发送UDP响应 + sock.sendto(('Unknown command: %s' % buf).encode('utf-8'), client_addr) + else: + logstr = "===" + datetime_str + " 映射到内部指令: '" + str(operation) + "'" + logging.info(logstr) + print(logstr) + + # 执行发送指令的脚本 + os_command = str(PYTHON_PATH) + " " + SENDER_FILE + " " + \ + str(UPC_DEV_IP) + " " + str(UPC_DEV_PORT) + " " + \ + str(operation) + " " + str(LOG_FILE) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) + + # 发送UDP响应 + sock.sendto(('Command %s processed as %s' % (buf, operation)).encode('utf-8'), client_addr) + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 处理UDP请求时发生错误: " + str(e) + "===" + logging.error(logstr, exc_info=True) + print("Error processing UDP request:", e) + + +if __name__ == '__main__': + main() diff --git a/bin/sender.py b/bin/sender.py new file mode 100755 index 0000000..7f8c673 --- /dev/null +++ b/bin/sender.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! + +import sys +import socket +import time +import logging +import telnetlib + + +def tcp_command_send(upc_addr, upc_command, data): + """负责发送数据和接收一次响应""" + timeout = 2 + socket.setdefaulttimeout(timeout) + try: + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + logging.debug(f"尝试连接到 {upc_addr[0]}:{upc_addr[1]}...") + client.connect(upc_addr) + logging.debug(f"成功连接到 {upc_addr[0]}:{upc_addr[1]}。") + + logging.info(f"正在发送命令 '{upc_command}' 到 {upc_addr}: {data!r} (原始字节)") + client.send(data) + logging.debug(f"数据发送完成。") + + recv_buffer_size = 1024 + response_bytes = b'' + try: + logging.debug(f"尝试从 {upc_addr} 接收响应数据...") + response_bytes = client.recv(recv_buffer_size) + if response_bytes: + logging.info(f"从 {upc_addr} 收到原始响应字节: {response_bytes!r}") + try: + response_str = response_bytes.decode('utf-8', errors='ignore') + logging.info(f"从 {upc_addr} 收到解码响应字符串: '{response_str}'") + except Exception as decode_e: + logging.warning(f"解码从 {upc_addr} 收到的响应时发生错误: {decode_e}") + else: + logging.info(f"从 {upc_addr} 未收到响应数据 (连接可能已关闭或设备未发送)。") + except socket.timeout: + logging.warning(f"从 {upc_addr} 接收响应超时。") + except OSError as recv_e: + logging.error(f"从 {upc_addr} 接收响应时发生网络错误: {recv_e}") + + client.close() + logging.debug(f"连接关闭。") + return True + except OSError as e: + logging.error(f"命令 '{upc_command}' 发送至 {upc_addr} 失败: {e}") + return False + + +# 指令定义表(从配置文件读取) +COMMAND_DEFINITIONS = { + "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", + "getstat": "fe010000000429C6", + "getstat01": "fe010100000429C6", +} + + +def upc_send_command(upc_addr, upc_command, log_file): + """将命令发送给终端设备""" + data = None + + # 查找指令定义 + if upc_command in COMMAND_DEFINITIONS: + hex_str = COMMAND_DEFINITIONS[upc_command] + data = bytes.fromhex(hex_str) + + if data is None: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} 未知命令: {upc_command},没有对应的数据可发送。" + logging.warning(logstr) + print(logstr) + else: + i = 1 + while i <= 3: # 尝试发送3次 + if tcp_command_send(upc_addr, upc_command, data): + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令第{i}次发送成功" + logging.info(logstr) + return + else: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令第{i}次发送失败,3秒后进行第{i+1}次尝试" + logging.warning(logstr) + time.sleep(3) + i = i + 1 + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令在3次尝试后发送均失败!" + logging.error(logstr) + + +if __name__ == '__main__': + ISOTIMEFORMAT = '%Y-%m-%d %X' + + UPC_DEV_IP = sys.argv[1] + UPC_DEV_PORT = sys.argv[2] + upc_command = sys.argv[3] + LOG_FILE = sys.argv[4] + + logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) + UPC_ADDR = (UPC_DEV_IP, int(UPC_DEV_PORT)) + + # 验证端口是否能Telnet通 + try: + logging.debug(f"尝试通过 Telnet 检查端口 {UPC_ADDR[0]}:{UPC_ADDR[1]} 可访问性...") + tn = telnetlib.Telnet(UPC_DEV_IP, UPC_DEV_PORT) + tn.close() + logging.debug(f"端口 {UPC_ADDR[0]}:{UPC_ADDR[1]} Telnet 检查通过。") + except OSError as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"!!!!!ERROR!!!!!{datetime_str} {UPC_ADDR} 端口无法访问,请检查设备或网络连接: {e}!!!!!" + logging.error(logstr) + print(logstr) + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"!!!!!ERROR!!!!!{datetime_str} {UPC_ADDR} Telnet检查时发生未知错误: {e}!!!!!" + logging.error(logstr, exc_info=True) + print(logstr) + else: + upc_send_command(UPC_ADDR, upc_command, LOG_FILE) diff --git a/bin/sender_tcp.py b/bin/sender_tcp.py new file mode 100755 index 0000000..7f8c673 --- /dev/null +++ b/bin/sender_tcp.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! + +import sys +import socket +import time +import logging +import telnetlib + + +def tcp_command_send(upc_addr, upc_command, data): + """负责发送数据和接收一次响应""" + timeout = 2 + socket.setdefaulttimeout(timeout) + try: + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + logging.debug(f"尝试连接到 {upc_addr[0]}:{upc_addr[1]}...") + client.connect(upc_addr) + logging.debug(f"成功连接到 {upc_addr[0]}:{upc_addr[1]}。") + + logging.info(f"正在发送命令 '{upc_command}' 到 {upc_addr}: {data!r} (原始字节)") + client.send(data) + logging.debug(f"数据发送完成。") + + recv_buffer_size = 1024 + response_bytes = b'' + try: + logging.debug(f"尝试从 {upc_addr} 接收响应数据...") + response_bytes = client.recv(recv_buffer_size) + if response_bytes: + logging.info(f"从 {upc_addr} 收到原始响应字节: {response_bytes!r}") + try: + response_str = response_bytes.decode('utf-8', errors='ignore') + logging.info(f"从 {upc_addr} 收到解码响应字符串: '{response_str}'") + except Exception as decode_e: + logging.warning(f"解码从 {upc_addr} 收到的响应时发生错误: {decode_e}") + else: + logging.info(f"从 {upc_addr} 未收到响应数据 (连接可能已关闭或设备未发送)。") + except socket.timeout: + logging.warning(f"从 {upc_addr} 接收响应超时。") + except OSError as recv_e: + logging.error(f"从 {upc_addr} 接收响应时发生网络错误: {recv_e}") + + client.close() + logging.debug(f"连接关闭。") + return True + except OSError as e: + logging.error(f"命令 '{upc_command}' 发送至 {upc_addr} 失败: {e}") + return False + + +# 指令定义表(从配置文件读取) +COMMAND_DEFINITIONS = { + "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", + "getstat": "fe010000000429C6", + "getstat01": "fe010100000429C6", +} + + +def upc_send_command(upc_addr, upc_command, log_file): + """将命令发送给终端设备""" + data = None + + # 查找指令定义 + if upc_command in COMMAND_DEFINITIONS: + hex_str = COMMAND_DEFINITIONS[upc_command] + data = bytes.fromhex(hex_str) + + if data is None: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} 未知命令: {upc_command},没有对应的数据可发送。" + logging.warning(logstr) + print(logstr) + else: + i = 1 + while i <= 3: # 尝试发送3次 + if tcp_command_send(upc_addr, upc_command, data): + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令第{i}次发送成功" + logging.info(logstr) + return + else: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令第{i}次发送失败,3秒后进行第{i+1}次尝试" + logging.warning(logstr) + time.sleep(3) + i = i + 1 + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令在3次尝试后发送均失败!" + logging.error(logstr) + + +if __name__ == '__main__': + ISOTIMEFORMAT = '%Y-%m-%d %X' + + UPC_DEV_IP = sys.argv[1] + UPC_DEV_PORT = sys.argv[2] + upc_command = sys.argv[3] + LOG_FILE = sys.argv[4] + + logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) + UPC_ADDR = (UPC_DEV_IP, int(UPC_DEV_PORT)) + + # 验证端口是否能Telnet通 + try: + logging.debug(f"尝试通过 Telnet 检查端口 {UPC_ADDR[0]}:{UPC_ADDR[1]} 可访问性...") + tn = telnetlib.Telnet(UPC_DEV_IP, UPC_DEV_PORT) + tn.close() + logging.debug(f"端口 {UPC_ADDR[0]}:{UPC_ADDR[1]} Telnet 检查通过。") + except OSError as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"!!!!!ERROR!!!!!{datetime_str} {UPC_ADDR} 端口无法访问,请检查设备或网络连接: {e}!!!!!" + logging.error(logstr) + print(logstr) + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"!!!!!ERROR!!!!!{datetime_str} {UPC_ADDR} Telnet检查时发生未知错误: {e}!!!!!" + logging.error(logstr, exc_info=True) + print(logstr) + else: + upc_send_command(UPC_ADDR, upc_command, LOG_FILE) diff --git a/bin/sender_udp.py b/bin/sender_udp.py new file mode 100755 index 0000000..09297aa --- /dev/null +++ b/bin/sender_udp.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! + +import sys +import socket +import time +import logging + + +def udp_command_send(upc_addr, upc_command, data, timeout=2): + """通过UDP发送数据并接收响应""" + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(timeout) + + try: + logging.debug(f"尝试发送UDP数据到 {upc_addr[0]}:{upc_addr[1]}...") + + logging.info(f"正在发送UDP命令 '{upc_command}' 到 {upc_addr}: {data!r} (原始字节)") + sock.sendto(data, upc_addr) + logging.debug(f"UDP数据发送完成。") + + # 尝试接收响应 + recv_buffer_size = 1024 + try: + logging.debug(f"等待从 {upc_addr} 接收UDP响应...") + response_bytes, addr = sock.recvfrom(recv_buffer_size) + if response_bytes: + logging.info(f"从 {addr} 收到UDP原始响应字节: {response_bytes!r}") + try: + response_str = response_bytes.decode('utf-8', errors='ignore') + logging.info(f"从 {addr} 收到UDP解码响应字符串: '{response_str}'") + except Exception as decode_e: + logging.warning(f"解码UDP响应时发生错误: {decode_e}") + else: + logging.info(f"未收到UDP响应数据。") + except socket.timeout: + logging.warning(f"接收UDP响应超时(设备可能不返回响应,这通常是正常的)。") + except OSError as recv_e: + logging.error(f"接收UDP响应时发生网络错误: {recv_e}") + + sock.close() + logging.debug(f"UDP socket关闭。") + return True + except OSError as e: + logging.error(f"UDP命令 '{upc_command}' 发送至 {upc_addr} 失败: {e}") + return False + finally: + try: + sock.close() + except: + pass + + +# 指令定义表(从配置文件读取) +COMMAND_DEFINITIONS = { + "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", + "getstat": "fe010000000429C6", + "getstat01": "fe010100000429C6", +} + + +def upc_send_command(upc_addr, upc_command, log_file): + """将命令发送给终端设备""" + data = None + + # 查找指令定义 + if upc_command in COMMAND_DEFINITIONS: + hex_str = COMMAND_DEFINITIONS[upc_command] + data = bytes.fromhex(hex_str) + + if data is None: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} 未知命令: {upc_command},没有对应的数据可发送。" + logging.warning(logstr) + print(logstr) + else: + i = 1 + while i <= 3: # 尝试发送3次 + if udp_command_send(upc_addr, upc_command, data): + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令第{i}次发送成功" + logging.info(logstr) + return + else: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令第{i}次发送失败,3秒后进行第{i+1}次尝试" + logging.warning(logstr) + time.sleep(3) + i = i + 1 + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{datetime_str} {upc_command}命令在3次尝试后发送均失败!" + logging.error(logstr) + + +if __name__ == '__main__': + ISOTIMEFORMAT = '%Y-%m-%d %X' + + UPC_DEV_IP = sys.argv[1] + UPC_DEV_PORT = int(sys.argv[2]) + upc_command = sys.argv[3] + LOG_FILE = sys.argv[4] + + logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) + UPC_ADDR = (UPC_DEV_IP, UPC_DEV_PORT) + + # UDP 是无连接的,无需像TCP那样预先检查端口连通性 + # 直接发送数据 + logging.info(f"UDP模式:准备发送命令到 {UPC_ADDR}") + upc_send_command(UPC_ADDR, upc_command, LOG_FILE) diff --git a/config/devices.json b/config/devices.json new file mode 100644 index 0000000..7b23a84 --- /dev/null +++ b/config/devices.json @@ -0,0 +1,63 @@ +{ + "global": { + "tms_server_ip": "192.168.8.9", + "python_path": "/usr/bin/python3", + "base_dir": "/home/smart/pythonPJ/upc_resent" + }, + "devices": [ + { + "id": "dev1", + "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": { + "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", + "getstat": "fe010000000429C6", + "getstat01": "fe010100000429C6" + }, + "mappings": { + "open": "openall4", + "close": "closeall4", + "guanggao-guan": "closeall4" + } +} diff --git a/crond/upcrond-192.168.8.125.py b/crond/upcrond-192.168.8.125.py new file mode 100644 index 0000000..51ea9c3 --- /dev/null +++ b/crond/upcrond-192.168.8.125.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- + +import time +import logging +import os +import socket +import sys # sys 模块用于 sys.exit(),虽然这里没有直接使用,但通常在脚本中需要 + +# --- 配置日志 --- +# 确保日志文件路径正确 +LOG_FILE = '/opt/upc_resent/log/upc-192.168.8.125.log' +# 定义时间格式,将被 logging.basicConfig 的 datefmt 使用 +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 配置日志,确保只执行一次 +# format 中包含了时间戳 %(asctime)s,所以不需要手动在日志字符串中添加 datetime +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) +# --- 日志配置结束 --- + + +def IsOpen(ip, port): + """ + 检查指定IP和端口是否可达。 + 如果端口不可达,则记录警告并尝试重启另一个Python脚本。 + """ + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) # 设置连接超时为1秒,避免无限期阻塞 + try: + logging.debug(f"尝试连接到 {ip}:{port}...") + s.connect((ip, int(port))) + s.shutdown(socket.SHUT_RDWR) # 使用 SHUT_RDWR 代替数字 2,更具可读性 + + print(f"端口 {port} is open") # Python 3 print 函数 + logging.info(f"端口 {port} is open") + return True + except (OSError, socket.timeout) as e: # 捕获具体的网络错误和超时 + print(f"端口 {port} is down") # Python 3 print 函数 + logging.warning(f"端口 {port} 检查失败: {e},尝试重启程序") + + # --- 重启命令 --- + # 确保这里的 python3 路径和脚本路径正确 + # 这里的脚本名 "192.168.8.125.py" 看起来是硬编码的,请确认是否正确 + restart_command = "/usr/bin/python3 /opt/upc_resent/bin/192.168.8.125.py &" + logging.info(f"执行重启命令: {restart_command}") + os.system(restart_command) + # --- 重启命令结束 --- + + return False + except ValueError: # 捕获 int(port) 转换失败的情况 + print(f"错误: 端口 '{port}' 不是有效的数字。") + logging.error(f"端口 '{port}' 不是有效的数字。") + return False + finally: + s.close() # 确保无论成功或失败,socket 都会被关闭 + + +if __name__ == '__main__': + # --- 目标设备IP和端口 --- + # 这些值在原脚本中是硬编码的,如果需要从命令行参数获取,请修改 + target_ip = '192.168.8.9' + target_port = 10129 + # --- 目标设备IP和端口结束 --- + + logging.info(f"启动端口检查程序,检查 {target_ip}:{target_port}") + IsOpen(target_ip, target_port) + + diff --git a/crond/upcrond-192.168.8.73.py b/crond/upcrond-192.168.8.73.py new file mode 100644 index 0000000..ba1f171 --- /dev/null +++ b/crond/upcrond-192.168.8.73.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- + +import time +import logging +import os +import socket +import sys # sys 模块用于 sys.exit(),虽然这里没有直接使用,但通常在脚本中需要 + +# --- 配置日志 --- +# 确保日志文件路径正确 +LOG_FILE = '/opt/upc_resent/log/upc-192.168.8.73.log' +# 定义时间格式,将被 logging.basicConfig 的 datefmt 使用 +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 配置日志,确保只执行一次 +# format 中包含了时间戳 %(asctime)s,所以不需要手动在日志字符串中添加 datetime +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) +# --- 日志配置结束 --- + + +def IsOpen(ip, port): + """ + 检查指定IP和端口是否可达。 + 如果端口不可达,则记录警告并尝试重启另一个Python脚本。 + """ + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) # 设置连接超时为1秒,避免无限期阻塞 + try: + logging.debug(f"尝试连接到 {ip}:{port}...") + s.connect((ip, int(port))) + s.shutdown(socket.SHUT_RDWR) # 使用 SHUT_RDWR 代替数字 2,更具可读性 + + print(f"端口 {port} is open") # Python 3 print 函数 + logging.info(f"端口 {port} is open") + return True + except (OSError, socket.timeout) as e: # 捕获具体的网络错误和超时 + print(f"端口 {port} is down") # Python 3 print 函数 + logging.warning(f"端口 {port} 检查失败: {e},尝试重启程序") + + # --- 重启命令 --- + # 确保这里的 python3 路径和脚本路径正确 + # 这里的脚本名 "192.168.8.125.py" 看起来是硬编码的,请确认是否正确 + restart_command = "/usr/bin/python3 /opt/upc_resent/bin/192.168.8.73.py &" + logging.info(f"执行重启命令: {restart_command}") + os.system(restart_command) + # --- 重启命令结束 --- + + return False + except ValueError: # 捕获 int(port) 转换失败的情况 + print(f"错误: 端口 '{port}' 不是有效的数字。") + logging.error(f"端口 '{port}' 不是有效的数字。") + return False + finally: + s.close() # 确保无论成功或失败,socket 都会被关闭 + + +if __name__ == '__main__': + # --- 目标设备IP和端口 --- + # 这些值在原脚本中是硬编码的,如果需要从命令行参数获取,请修改 + target_ip = '192.168.8.9' + target_port = 10079 + # --- 目标设备IP和端口结束 --- + + logging.info(f"启动端口检查程序,检查 {target_ip}:{target_port}") + IsOpen(target_ip, target_port) + + diff --git a/crond/upcrond-dev1.py b/crond/upcrond-dev1.py new file mode 100755 index 0000000..d134d61 --- /dev/null +++ b/crond/upcrond-dev1.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: 设备73-TCP (dev1) + +import time +import logging +import os +import socket +import sys + +# ====== 配置参数(从配置文件读取)====== +TARGET_IP = '192.168.8.9' +TARGET_PORT = 10079 +LOG_FILE = '/home/smart/pythonPJ/upc_resent/log/crond-dev1.log' +RESTART_COMMAND = "/usr/bin/python3 /home/smart/pythonPJ/upc_resent/bin/dev1.py &" +DEVICE_ID = 'dev1' +# ========================= + +ISOTIMEFORMAT = '%Y-%m-%d %X' + +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) + + +def is_port_open(ip, port): + """检查指定IP和端口是否可达""" + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + try: + logging.debug(f"尝试连接到 {ip}:{port}...") + s.connect((ip, int(port))) + s.shutdown(socket.SHUT_RDWR) + print(f"[{DEVICE_ID}] 端口 {port} is open") + logging.info(f"端口 {port} is open") + return True + except (OSError, socket.timeout) as e: + print(f"[{DEVICE_ID}] 端口 {port} is down") + logging.warning(f"端口 {port} 检查失败: {e},尝试重启程序") + logging.info(f"执行重启命令: {RESTART_COMMAND}") + os.system(RESTART_COMMAND) + return False + except ValueError: + print(f"错误: 端口 '{port}' 不是有效的数字。") + logging.error(f"端口 '{port}' 不是有效的数字。") + return False + finally: + s.close() + + +if __name__ == '__main__': + logging.info(f"[{DEVICE_ID}] 启动端口检查程序,检查 {TARGET_IP}:{TARGET_PORT}") + print(f"[{DEVICE_ID}] 启动端口检查程序,检查 {TARGET_IP}:{TARGET_PORT}") + is_port_open(TARGET_IP, TARGET_PORT) diff --git a/crond/upcrond-dev2.py b/crond/upcrond-dev2.py new file mode 100755 index 0000000..6dad76e --- /dev/null +++ b/crond/upcrond-dev2.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: 设备125-TCP (dev2) + +import time +import logging +import os +import socket +import sys + +# ====== 配置参数(从配置文件读取)====== +TARGET_IP = '192.168.8.9' +TARGET_PORT = 10129 +LOG_FILE = '/home/smart/pythonPJ/upc_resent/log/crond-dev2.log' +RESTART_COMMAND = "/usr/bin/python3 /home/smart/pythonPJ/upc_resent/bin/dev2.py &" +DEVICE_ID = 'dev2' +# ========================= + +ISOTIMEFORMAT = '%Y-%m-%d %X' + +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) + + +def is_port_open(ip, port): + """检查指定IP和端口是否可达""" + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + try: + logging.debug(f"尝试连接到 {ip}:{port}...") + s.connect((ip, int(port))) + s.shutdown(socket.SHUT_RDWR) + print(f"[{DEVICE_ID}] 端口 {port} is open") + logging.info(f"端口 {port} is open") + return True + except (OSError, socket.timeout) as e: + print(f"[{DEVICE_ID}] 端口 {port} is down") + logging.warning(f"端口 {port} 检查失败: {e},尝试重启程序") + logging.info(f"执行重启命令: {RESTART_COMMAND}") + os.system(RESTART_COMMAND) + return False + except ValueError: + print(f"错误: 端口 '{port}' 不是有效的数字。") + logging.error(f"端口 '{port}' 不是有效的数字。") + return False + finally: + s.close() + + +if __name__ == '__main__': + logging.info(f"[{DEVICE_ID}] 启动端口检查程序,检查 {TARGET_IP}:{TARGET_PORT}") + print(f"[{DEVICE_ID}] 启动端口检查程序,检查 {TARGET_IP}:{TARGET_PORT}") + is_port_open(TARGET_IP, TARGET_PORT) diff --git a/crond/upcrond-dev3.py b/crond/upcrond-dev3.py new file mode 100755 index 0000000..464e478 --- /dev/null +++ b/crond/upcrond-dev3.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: 设备UDP示例 (dev3) - UDP Protocol + +import time +import logging +import os +import socket +import sys +import subprocess + +# ====== 配置参数(从配置文件读取)====== +TARGET_IP = '192.168.8.9' +TARGET_PORT = 10080 +LOG_FILE = '/home/smart/pythonPJ/upc_resent/log/crond-dev3.log' +RESTART_COMMAND = "/usr/bin/python3 /home/smart/pythonPJ/upc_resent/bin/dev3.py &" +DEVICE_ID = 'dev3' +# ========================= + +ISOTIMEFORMAT = '%Y-%m-%d %X' + +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) + + +def is_udp_port_open(ip, port): + """ + 检查UDP端口是否可用。 + UDP是无连接的,这里通过检查端口是否被占用或发送测试数据来判断。 + """ + try: + # 尝试绑定到该端口,如果成功说明端口未被占用(服务未启动) + # 如果失败说明端口已被占用(服务正在运行) + test_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + test_sock.settimeout(1) + test_sock.bind((ip, int(port))) + test_sock.close() + + # 如果绑定成功,说明服务未启动 + print(f"[{DEVICE_ID}] UDP端口 {port} 未被占用,服务可能未运行") + logging.warning(f"UDP端口 {port} 未被占用,服务可能未运行,尝试重启程序") + logging.info(f"执行重启命令: {RESTART_COMMAND}") + os.system(RESTART_COMMAND) + return False + except OSError as e: + # 绑定失败,说明端口已被占用,服务正在运行 + if "Address already in use" in str(e) or "98" in str(e): + print(f"[{DEVICE_ID}] UDP端口 {port} is open (已被占用,服务运行中)") + logging.info(f"UDP端口 {port} is open (服务运行中)") + return True + else: + print(f"[{DEVICE_ID}] 检查UDP端口 {port} 时发生错误: {e}") + logging.error(f"检查UDP端口 {port} 时发生错误: {e}") + return False + + +def check_process_running(): + """通过进程名检查服务是否正在运行""" + try: + # 检查是否有对应设备的Python进程在运行 + cmd = f"ps aux | grep '{DEVICE_ID}.py' | grep -v grep" + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode == 0 and result.stdout.strip(): + print(f"[{DEVICE_ID}] 进程检查: 服务正在运行") + logging.info(f"进程检查: 服务正在运行") + return True + else: + print(f"[{DEVICE_ID}] 进程检查: 服务未运行") + logging.warning(f"进程检查: 服务未运行,尝试重启") + logging.info(f"执行重启命令: {RESTART_COMMAND}") + os.system(RESTART_COMMAND) + return False + except Exception as e: + print(f"[{DEVICE_ID}] 进程检查失败: {e}") + logging.error(f"进程检查失败: {e}") + return False + + +if __name__ == '__main__': + logging.info(f"[{DEVICE_ID}] 启动UDP端口检查程序,检查 {TARGET_IP}:{TARGET_PORT}") + print(f"[{DEVICE_ID}] 启动UDP端口检查程序,检查 {TARGET_IP}:{TARGET_PORT}") + + # 对于UDP,优先使用进程检查方式 + check_process_running() diff --git a/crontab.txt b/crontab.txt new file mode 100644 index 0000000..2b9f3a9 --- /dev/null +++ b/crontab.txt @@ -0,0 +1,4 @@ +* */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/cutlog.sh b/cutlog.sh new file mode 100644 index 0000000..5ad8de6 --- /dev/null +++ b/cutlog.sh @@ -0,0 +1,8 @@ +#!/bin/bash +declare logs_path="/opt/upc_resent/log"; +declare need_delete_path=${logs_path}/$(date -d "7 days ago" "+%Y_%m_%d"); +declare yestoday_log_path=${logs_path}/$(date -d "yesterday" "+%Y_%m_%d"); +rm -rf ${need_delete_path} +rm -rf ${yestoday_log_path} +mv ${logs_path}/today ${yestoday_log_path} +mkdir -p ${logs_path}/today diff --git a/log/192.168.8.125.log b/log/192.168.8.125.log new file mode 100644 index 0000000..30d2642 Binary files /dev/null and b/log/192.168.8.125.log differ diff --git a/log/192.168.8.73.log b/log/192.168.8.73.log new file mode 100644 index 0000000..55d401c --- /dev/null +++ b/log/192.168.8.73.log @@ -0,0 +1,5 @@ +INFO:root:服务启动,监听 192.168.8.9:10079 +INFO:root:===2026-03-21 20:14:40收到客户端192.168.9.71:56925发送的指令: 'close1' +INFO:root:===2026-03-21 20:14:40 映射到内部指令: 'close1' +INFO:root:执行外部命令: /usr/bin/python3 /opt/upc_resent/bin/sender.py 192.168.8.73 502 close1 /opt/upc_resent/log/192.168.8.73.log +INFO:root:2026-03-21 20:14:40 close1命令第1次发送成功 diff --git a/log/upc-192.168.8.125.log b/log/upc-192.168.8.125.log new file mode 100644 index 0000000..8af09bf --- /dev/null +++ b/log/upc-192.168.8.125.log @@ -0,0 +1,3 @@ +2026-03-21 21:06:19 - INFO - 启动端口检查程序,检查 192.168.8.9:10129 +2026-03-21 21:06:19 - DEBUG - 尝试连接到 192.168.8.9:10129... +2026-03-21 21:06:19 - INFO - 端口 10129 is open diff --git a/scripts/generate.py b/scripts/generate.py new file mode 100644 index 0000000..5b50118 --- /dev/null +++ b/scripts/generate.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +""" +代码生成器:根据配置文件自动生成 Python 执行文件 +支持 TCP 和 UDP 协议 + +用法: python scripts/generate.py [--check] + --check: 仅检查配置,不生成文件 +""" + +import json +import os +import sys +import argparse + + +def load_config(config_file='config/devices.json'): + """加载配置文件""" + with open(config_file, 'r', encoding='utf-8') as f: + return json.load(f) + + +def ensure_dirs(base_dir): + """确保必要的目录存在""" + dirs = [ + os.path.join(base_dir, 'bin'), + os.path.join(base_dir, 'crond'), + os.path.join(base_dir, 'log') + ] + for d in dirs: + os.makedirs(d, exist_ok=True) + + +def dict_to_python_string(d): + """将字典转换为 Python 代码字符串""" + lines = ['{'] + for k, v in d.items(): + if isinstance(v, str): + lines.append(f' "{k}": "{v}",') + else: + lines.append(f' "{k}": {v},') + lines.append('}') + return '\n'.join(lines) + + +def get_template_path(protocol, template_type): + """ + 根据协议类型和模板类型获取模板文件路径 + protocol: 'tcp' 或 'udp' + template_type: 'listener', 'crond', 'sender' + """ + protocol = protocol.lower() + if template_type == 'listener': + return f'templates/listener_{protocol}.py.tpl' + elif template_type == 'crond': + return f'templates/crond_{protocol}.py.tpl' + elif template_type == 'sender': + return f'templates/sender_{protocol}.py.tpl' + else: + raise ValueError(f"未知的模板类型: {template_type}") + + +def generate_listener(device, global_config, mappings): + """生成监听服务文件""" + protocol = device.get('protocol', 'tcp').lower() + template_file = get_template_path(protocol, 'listener') + + # 检查模板文件是否存在 + if not os.path.exists(template_file): + print(f"[!] 警告: 模板文件不存在: {template_file},使用TCP模板作为后备") + template_file = 'templates/listener_tcp.py.tpl' + + with open(template_file, 'r', encoding='utf-8') as f: + template = f.read() + + # 构建命令映射字典字符串 + mappings_str = "{\n" + for cmd, op in 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 += " }" + + # 替换模板变量 + content = template.format( + device_id=device['id'], + device_name=device['name'], + tms_server_ip=global_config['tms_server_ip'], + listen_port=device['listen_port'], + upc_ip=device['upc_ip'], + upc_port=device['upc_port'], + base_dir=global_config['base_dir'], + python_path=global_config['python_path'], + command_mappings=mappings_str + ) + + output_file = os.path.join(global_config['base_dir'], 'bin', f"{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()})") + return output_file + + +def generate_crond(device, global_config): + """生成保活检查文件""" + protocol = device.get('protocol', 'tcp').lower() + template_file = get_template_path(protocol, 'crond') + + # 检查模板文件是否存在 + if not os.path.exists(template_file): + print(f"[!] 警告: 模板文件不存在: {template_file},使用TCP模板作为后备") + template_file = 'templates/crond_tcp.py.tpl' + + with open(template_file, 'r', encoding='utf-8') as f: + template = f.read() + + content = template.format( + device_id=device['id'], + device_name=device['name'], + 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'] + ) + + output_file = os.path.join(global_config['base_dir'], 'crond', f"upcrond-{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()})") + return output_file + + +def generate_sender(commands, global_config, protocol='tcp'): + """生成发送模块文件""" + protocol = protocol.lower() + template_file = get_template_path(protocol, 'sender') + + # 检查模板文件是否存在 + if not os.path.exists(template_file): + print(f"[!] 警告: 模板文件不存在: {template_file},使用TCP模板作为后备") + template_file = 'templates/sender_tcp.py.tpl' + + with open(template_file, 'r', encoding='utf-8') as f: + template = f.read() + + # 构建指令定义字典字符串 + cmd_defs = "{\n" + for cmd, hex_str in commands.items(): + cmd_defs += f' "{cmd}": "{hex_str}",\n' + cmd_defs += "}" + + content = template.format( + command_definitions=cmd_defs + ) + + # 根据协议生成不同的文件名 + output_file = os.path.join(global_config['base_dir'], 'bin', f'sender_{protocol}.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()})") + return output_file + + +def generate_all_senders(config, global_config): + """为所有使用到的协议生成 sender 文件""" + commands = config['commands'] + 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)) + + return generated + + +def generate_crontab(devices, global_config): + """生成 crontab 配置""" + lines = [] + + for device in devices: + if not device.get('enabled', True): + continue + # 每分钟执行一次保活检查 + lines.append(f"* */1 * * * {global_config['python_path']} {global_config['base_dir']}/crond/upcrond-{device['id']}.py") + + # 添加日志清理任务 + lines.append(f"0 0 * * * {global_config['base_dir']}/cutlog.sh") + + content = '\n'.join(lines) + '\n' + output_file = 'crontab.txt' + with open(output_file, 'w', encoding='utf-8') as f: + f.write(content) + + print(f"[生成] {output_file}") + return output_file + + +def generate_start_script(devices, global_config): + """生成启动脚本""" + lines = ['#!/bin/bash'] + lines.append('# Auto-generated start script') + lines.append('') + lines.append('# 启动所有监听服务') + + for device in devices: + if not device.get('enabled', True): + lines.append(f"# {device['name']} ({device['id']}) - 已禁用") + continue + protocol = device.get('protocol', 'tcp').upper() + script_path = f"{global_config['base_dir']}/bin/{device['id']}.py" + lines.append(f"echo '启动 {device['name']} ({device['id']}) - {protocol}...'") + lines.append(f"su root -c \"{global_config['python_path']} {script_path} &\"") + + lines.append('') + lines.append('echo "所有服务已启动"') + + content = '\n'.join(lines) + '\n' + output_file = 'start.sh' + with open(output_file, 'w', encoding='utf-8') as f: + f.write(content) + + os.chmod(output_file, 0o755) + print(f"[生成] {output_file}") + return output_file + + +def check_config(config): + """检查配置是否合法""" + errors = [] + warnings = [] + + global_cfg = config.get('global', {}) + devices = config.get('devices', []) + commands = config.get('commands', {}) + mappings = config.get('mappings', {}) + + # 检查全局配置 + required_globals = ['tms_server_ip', 'python_path', 'base_dir'] + for key in required_globals: + if key not in global_cfg: + errors.append(f"缺少全局配置: {key}") + + # 检查设备配置 + if not devices: + errors.append("没有配置任何设备") + + device_ids = [] + listen_ports = [] + + for device in devices: + device_id = device.get('id') + if not device_id: + errors.append("设备缺少 id 字段") + continue + + if device_id in device_ids: + errors.append(f"设备 id 重复: {device_id}") + device_ids.append(device_id) + + required_device_fields = ['name', 'upc_ip', 'upc_port', 'listen_port'] + for field in required_device_fields: + 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)") + + if 'listen_port' in device: + port = device['listen_port'] + if port in listen_ports: + errors.append(f"设备 {device_id} 的监听端口 {port} 与其他设备冲突") + listen_ports.append(port) + + # 检查指令定义 + if 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 中定义") + + # 检查模板文件是否存在 + protocols = set() + for device in devices: + if device.get('enabled', True): + protocols.add(device.get('protocol', 'tcp').lower()) + + for protocol in protocols: + for template_type in ['listener', 'crond', 'sender']: + template_file = get_template_path(protocol, template_type) + if not os.path.exists(template_file): + warnings.append(f"模板文件不存在: {template_file}") + + # 输出检查结果 + if errors: + print("\n[!] 配置错误:") + for e in errors: + print(f" - {e}") + + if warnings: + print("\n[*] 配置警告:") + for w in warnings: + print(f" - {w}") + + if not errors and not warnings: + print("\n[✓] 配置检查通过") + return True + + return len(errors) == 0 + + +def main(): + parser = argparse.ArgumentParser(description='UPC Resent 代码生成器 (支持 TCP/UDP)') + parser.add_argument('--check', action='store_true', help='仅检查配置,不生成文件') + args = parser.parse_args() + + print("=" * 50) + print("UPC Resent 代码生成器 (支持 TCP/UDP)") + print("=" * 50) + + # 加载配置 + try: + config = load_config() + print("\n[✓] 配置文件加载成功") + except Exception as e: + print(f"\n[!] 加载配置文件失败: {e}") + sys.exit(1) + + # 检查配置 + if not check_config(config): + print("\n[!] 配置检查失败,请修正后重试") + sys.exit(1) + + if args.check: + print("\n[✓] 仅检查模式,不生成文件") + sys.exit(0) + + # 提取配置 + global_config = config['global'] + devices = [d for d in config['devices'] if d.get('enabled', True)] + + # 确保目录存在 + ensure_dirs(global_config['base_dir']) + print(f"[✓] 目录检查完成: {global_config['base_dir']}") + + print("\n" + "-" * 50) + print("开始生成文件...") + print("-" * 50) + + # 生成文件 + generated_files = [] + + # 1. 为所有使用到的协议生成 sender.py + print("\n[生成发送模块]") + generated_files.extend(generate_all_senders(config, global_config)) + + # 2. 为每个设备生成监听服务和保活脚本 + print("\n[生成设备服务]") + 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_crond(device, global_config)) + + # 3. 生成 crontab + print("\n[生成配置文件]") + generated_files.append(generate_crontab(config['devices'], global_config)) + + # 4. 生成启动脚本 + generated_files.append(generate_start_script(devices, global_config)) + + print("\n" + "=" * 50) + print(f"[✓] 代码生成完成!共生成 {len(generated_files)} 个文件") + print("=" * 50) + print(f"\n部署目录: {global_config['base_dir']}") + print("\n使用方法:") + print(f" 1. 启动服务: ./{generated_files[-1]}") + print(f" 2. 配置 crontab: crontab crontab.txt") + print("\n支持的协议:") + protocols = set(d.get('protocol', 'tcp').upper() for d in devices) + for p in protocols: + print(f" - {p}") + + +if __name__ == '__main__': + main() diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..ea7bb98 --- /dev/null +++ b/start.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Auto-generated start script + +# 启动所有监听服务 +echo '启动 设备73-TCP (dev1) - TCP...' +su root -c "/usr/bin/python3 /home/smart/pythonPJ/upc_resent/bin/dev1.py &" +echo '启动 设备125-TCP (dev2) - TCP...' +su root -c "/usr/bin/python3 /home/smart/pythonPJ/upc_resent/bin/dev2.py &" +echo '启动 设备UDP示例 (dev3) - UDP...' +su root -c "/usr/bin/python3 /home/smart/pythonPJ/upc_resent/bin/dev3.py &" + +echo "所有服务已启动" diff --git a/templates/crond_tcp.py.tpl b/templates/crond_tcp.py.tpl new file mode 100644 index 0000000..103d0f0 --- /dev/null +++ b/templates/crond_tcp.py.tpl @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: {device_name} ({device_id}) + +import time +import logging +import os +import socket +import sys + +# ====== 配置参数(从配置文件读取)====== +TARGET_IP = '{tms_server_ip}' +TARGET_PORT = {listen_port} +LOG_FILE = '{base_dir}/log/crond-{device_id}.log' +RESTART_COMMAND = "{python_path} {base_dir}/bin/{device_id}.py &" +DEVICE_ID = '{device_id}' +# ========================= + +ISOTIMEFORMAT = '%Y-%m-%d %X' + +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) + + +def is_port_open(ip, port): + """检查指定IP和端口是否可达""" + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + try: + logging.debug(f"尝试连接到 {{ip}}:{{port}}...") + s.connect((ip, int(port))) + s.shutdown(socket.SHUT_RDWR) + print(f"[{{DEVICE_ID}}] 端口 {{port}} is open") + logging.info(f"端口 {{port}} is open") + return True + except (OSError, socket.timeout) as e: + print(f"[{{DEVICE_ID}}] 端口 {{port}} is down") + logging.warning(f"端口 {{port}} 检查失败: {{e}},尝试重启程序") + logging.info(f"执行重启命令: {{RESTART_COMMAND}}") + os.system(RESTART_COMMAND) + return False + except ValueError: + print(f"错误: 端口 '{{port}}' 不是有效的数字。") + logging.error(f"端口 '{{port}}' 不是有效的数字。") + return False + finally: + s.close() + + +if __name__ == '__main__': + logging.info(f"[{{DEVICE_ID}}] 启动端口检查程序,检查 {{TARGET_IP}}:{{TARGET_PORT}}") + print(f"[{{DEVICE_ID}}] 启动端口检查程序,检查 {{TARGET_IP}}:{{TARGET_PORT}}") + is_port_open(TARGET_IP, TARGET_PORT) diff --git a/templates/crond_udp.py.tpl b/templates/crond_udp.py.tpl new file mode 100644 index 0000000..61eb9f4 --- /dev/null +++ b/templates/crond_udp.py.tpl @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: {device_name} ({device_id}) - UDP Protocol + +import time +import logging +import os +import socket +import sys +import subprocess + +# ====== 配置参数(从配置文件读取)====== +TARGET_IP = '{tms_server_ip}' +TARGET_PORT = {listen_port} +LOG_FILE = '{base_dir}/log/crond-{device_id}.log' +RESTART_COMMAND = "{python_path} {base_dir}/bin/{device_id}.py &" +DEVICE_ID = '{device_id}' +# ========================= + +ISOTIMEFORMAT = '%Y-%m-%d %X' + +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', datefmt=ISOTIMEFORMAT) + + +def is_udp_port_open(ip, port): + """ + 检查UDP端口是否可用。 + UDP是无连接的,这里通过检查端口是否被占用或发送测试数据来判断。 + """ + try: + # 尝试绑定到该端口,如果成功说明端口未被占用(服务未启动) + # 如果失败说明端口已被占用(服务正在运行) + test_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + test_sock.settimeout(1) + test_sock.bind((ip, int(port))) + test_sock.close() + + # 如果绑定成功,说明服务未启动 + print(f"[{{DEVICE_ID}}] UDP端口 {{port}} 未被占用,服务可能未运行") + logging.warning(f"UDP端口 {{port}} 未被占用,服务可能未运行,尝试重启程序") + logging.info(f"执行重启命令: {{RESTART_COMMAND}}") + os.system(RESTART_COMMAND) + return False + except OSError as e: + # 绑定失败,说明端口已被占用,服务正在运行 + if "Address already in use" in str(e) or "98" in str(e): + print(f"[{{DEVICE_ID}}] UDP端口 {{port}} is open (已被占用,服务运行中)") + logging.info(f"UDP端口 {{port}} is open (服务运行中)") + return True + else: + print(f"[{{DEVICE_ID}}] 检查UDP端口 {{port}} 时发生错误: {{e}}") + logging.error(f"检查UDP端口 {{port}} 时发生错误: {{e}}") + return False + + +def check_process_running(): + """通过进程名检查服务是否正在运行""" + try: + # 检查是否有对应设备的Python进程在运行 + cmd = f"ps aux | grep '{{DEVICE_ID}}.py' | grep -v grep" + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + if result.returncode == 0 and result.stdout.strip(): + print(f"[{{DEVICE_ID}}] 进程检查: 服务正在运行") + logging.info(f"进程检查: 服务正在运行") + return True + else: + print(f"[{{DEVICE_ID}}] 进程检查: 服务未运行") + logging.warning(f"进程检查: 服务未运行,尝试重启") + logging.info(f"执行重启命令: {{RESTART_COMMAND}}") + os.system(RESTART_COMMAND) + return False + except Exception as e: + print(f"[{{DEVICE_ID}}] 进程检查失败: {{e}}") + logging.error(f"进程检查失败: {{e}}") + return False + + +if __name__ == '__main__': + logging.info(f"[{{DEVICE_ID}}] 启动UDP端口检查程序,检查 {{TARGET_IP}}:{{TARGET_PORT}}") + print(f"[{{DEVICE_ID}}] 启动UDP端口检查程序,检查 {{TARGET_IP}}:{{TARGET_PORT}}") + + # 对于UDP,优先使用进程检查方式 + check_process_running() diff --git a/templates/listener_tcp.py.tpl b/templates/listener_tcp.py.tpl new file mode 100644 index 0000000..72aafe5 --- /dev/null +++ b/templates/listener_tcp.py.tpl @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: {device_name} ({device_id}) + +import socket +import os +import time +import logging +import sys + +# ====== 配置参数(从配置文件读取)====== +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' +LOG_FILE = '{base_dir}/log/{device_id}.log' +PYTHON_PATH = '{python_path}' +DEVICE_ID = '{device_id}' +# ========================= + +BUFSIZE = 1024 + +# 配置日志 +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 命令映射表 +COMMAND_MAPPINGS = {command_mappings} + + +def process_command(buf): + """处理接收到的命令,返回对应的操作指令""" + buf = buf.strip() + if buf in COMMAND_MAPPINGS: + return COMMAND_MAPPINGS[buf] + return "nodata" + + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + sock.bind((TMS_SERVER_IP, int(TMS_PORT))) + sock.listen(500) + except OSError as e: + logstr = "!!!!!ERROR!!!!!" + time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + \ + " 无法绑定端口 %s:%s - %s" % (TMS_SERVER_IP, TMS_PORT, e) + logging.error(logstr) + print(logstr) + sys.exit(1) + + logging.info("服务启动,监听 %s:%s" % (TMS_SERVER_IP, TMS_PORT)) + print(f"[{{DEVICE_ID}}] 服务启动,监听 {{TMS_SERVER_IP}}:{{TMS_PORT}}") + + while True: + connection, address = sock.accept() + try: + connection.settimeout(5) + buf_bytes = connection.recv(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 收到客户端 " + address[0] + ":" + str(address[1]) + \ + " 发送的指令: '" + str(buf) + "'" + logging.info(logstr) + print(logstr) + + operation = process_command(buf) + + if operation == "nodata": + logstr = "===" + datetime_str + " 未知指令: '" + str(buf) + "'" + logging.warning(logstr) + print(logstr) + connection.send(('Unknown command: %s' % buf).encode('utf-8')) + else: + logstr = "===" + datetime_str + " 映射到内部指令: '" + str(operation) + "'" + logging.info(logstr) + print(logstr) + + os_command = str(PYTHON_PATH) + " " + SENDER_FILE + " " + \ + str(UPC_DEV_IP) + " " + str(UPC_DEV_PORT) + " " + \ + str(operation) + " " + str(LOG_FILE) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) + connection.send(('Command %s processed as %s' % (buf, operation)).encode('utf-8')) + except socket.timeout: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 客户端连接超时: " + address[0] + ":" + str(address[1]) + "===" + logging.warning(logstr) + print('time out') + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 处理客户端 " + address[0] + ":" + str(address[1]) + \ + " 请求时发生错误: " + str(e) + "===" + logging.error(logstr, exc_info=True) + print("Error processing request:", e) + connection.send(('Error processing command: %s' % str(e)).encode('utf-8')) + finally: + connection.close() + + +if __name__ == '__main__': + main() diff --git a/templates/listener_udp.py.tpl b/templates/listener_udp.py.tpl new file mode 100644 index 0000000..aad547c --- /dev/null +++ b/templates/listener_udp.py.tpl @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! +# Device: {device_name} ({device_id}) - UDP Protocol + +import socket +import os +import time +import logging +import sys + +# ====== 配置参数(从配置文件读取)====== +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' +LOG_FILE = '{base_dir}/log/{device_id}.log' +PYTHON_PATH = '{python_path}' +DEVICE_ID = '{device_id}' +# ========================= + +BUFSIZE = 1024 + +# 配置日志 +logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) +ISOTIMEFORMAT = '%Y-%m-%d %X' + +# 命令映射表 +COMMAND_MAPPINGS = {command_mappings} + + +def process_command(buf): + """处理接收到的命令,返回对应的操作指令""" + buf = buf.strip() + if buf in COMMAND_MAPPINGS: + return COMMAND_MAPPINGS[buf] + return "nodata" + + +def main(): + # 创建 UDP socket + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + sock.bind((TMS_SERVER_IP, int(TMS_PORT))) + except OSError as e: + logstr = "!!!!!ERROR!!!!!" + time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + \ + " 无法绑定UDP端口 %s:%s - %s" % (TMS_SERVER_IP, TMS_PORT, e) + logging.error(logstr) + print(logstr) + sys.exit(1) + + logging.info("UDP服务启动,监听 %s:%s" % (TMS_SERVER_IP, TMS_PORT)) + print(f"[{{DEVICE_ID}}] UDP服务启动,监听 {{TMS_SERVER_IP}}:{{TMS_PORT}}") + + while True: + try: + # UDP 使用 recvfrom 接收数据,同时获取客户端地址 + buf_bytes, client_addr = sock.recvfrom(BUFSIZE) + buf = buf_bytes.decode('utf-8').strip() + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 收到UDP客户端 " + client_addr[0] + ":" + str(client_addr[1]) + \ + " 发送的指令: '" + str(buf) + "'" + logging.info(logstr) + print(logstr) + + operation = process_command(buf) + + if operation == "nodata": + logstr = "===" + datetime_str + " 未知指令: '" + str(buf) + "'" + logging.warning(logstr) + print(logstr) + # 发送UDP响应 + sock.sendto(('Unknown command: %s' % buf).encode('utf-8'), client_addr) + else: + logstr = "===" + datetime_str + " 映射到内部指令: '" + str(operation) + "'" + logging.info(logstr) + print(logstr) + + # 执行发送指令的脚本 + os_command = str(PYTHON_PATH) + " " + SENDER_FILE + " " + \ + str(UPC_DEV_IP) + " " + str(UPC_DEV_PORT) + " " + \ + str(operation) + " " + str(LOG_FILE) + logging.info("执行外部命令: %s" % os_command) + os.system(os_command) + + # 发送UDP响应 + sock.sendto(('Command %s processed as %s' % (buf, operation)).encode('utf-8'), client_addr) + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = "===" + datetime_str + " 处理UDP请求时发生错误: " + str(e) + "===" + logging.error(logstr, exc_info=True) + print("Error processing UDP request:", e) + + +if __name__ == '__main__': + main() diff --git a/templates/sender_tcp.py.tpl b/templates/sender_tcp.py.tpl new file mode 100644 index 0000000..ab5bb44 --- /dev/null +++ b/templates/sender_tcp.py.tpl @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! + +import sys +import socket +import time +import logging +import telnetlib + + +def tcp_command_send(upc_addr, upc_command, data): + """负责发送数据和接收一次响应""" + timeout = 2 + socket.setdefaulttimeout(timeout) + try: + client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + logging.debug(f"尝试连接到 {{upc_addr[0]}}:{{upc_addr[1]}}...") + client.connect(upc_addr) + logging.debug(f"成功连接到 {{upc_addr[0]}}:{{upc_addr[1]}}。") + + logging.info(f"正在发送命令 '{{upc_command}}' 到 {{upc_addr}}: {{data!r}} (原始字节)") + client.send(data) + logging.debug(f"数据发送完成。") + + recv_buffer_size = 1024 + response_bytes = b'' + try: + logging.debug(f"尝试从 {{upc_addr}} 接收响应数据...") + response_bytes = client.recv(recv_buffer_size) + if response_bytes: + logging.info(f"从 {{upc_addr}} 收到原始响应字节: {{response_bytes!r}}") + try: + response_str = response_bytes.decode('utf-8', errors='ignore') + logging.info(f"从 {{upc_addr}} 收到解码响应字符串: '{{response_str}}'") + except Exception as decode_e: + logging.warning(f"解码从 {{upc_addr}} 收到的响应时发生错误: {{decode_e}}") + else: + logging.info(f"从 {{upc_addr}} 未收到响应数据 (连接可能已关闭或设备未发送)。") + except socket.timeout: + logging.warning(f"从 {{upc_addr}} 接收响应超时。") + except OSError as recv_e: + logging.error(f"从 {{upc_addr}} 接收响应时发生网络错误: {{recv_e}}") + + client.close() + logging.debug(f"连接关闭。") + return True + except OSError as e: + logging.error(f"命令 '{{upc_command}}' 发送至 {{upc_addr}} 失败: {{e}}") + return False + + +# 指令定义表(从配置文件读取) +COMMAND_DEFINITIONS = {command_definitions} + + +def upc_send_command(upc_addr, upc_command, log_file): + """将命令发送给终端设备""" + data = None + + # 查找指令定义 + if upc_command in COMMAND_DEFINITIONS: + hex_str = COMMAND_DEFINITIONS[upc_command] + data = bytes.fromhex(hex_str) + + if data is None: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} 未知命令: {{upc_command}},没有对应的数据可发送。" + logging.warning(logstr) + print(logstr) + else: + i = 1 + while i <= 3: # 尝试发送3次 + if tcp_command_send(upc_addr, upc_command, data): + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} {{upc_command}}命令第{{i}}次发送成功" + logging.info(logstr) + return + else: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} {{upc_command}}命令第{{i}}次发送失败,3秒后进行第{{i+1}}次尝试" + logging.warning(logstr) + time.sleep(3) + i = i + 1 + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} {{upc_command}}命令在3次尝试后发送均失败!" + logging.error(logstr) + + +if __name__ == '__main__': + ISOTIMEFORMAT = '%Y-%m-%d %X' + + UPC_DEV_IP = sys.argv[1] + UPC_DEV_PORT = sys.argv[2] + upc_command = sys.argv[3] + LOG_FILE = sys.argv[4] + + logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) + UPC_ADDR = (UPC_DEV_IP, int(UPC_DEV_PORT)) + + # 验证端口是否能Telnet通 + try: + logging.debug(f"尝试通过 Telnet 检查端口 {{UPC_ADDR[0]}}:{{UPC_ADDR[1]}} 可访问性...") + tn = telnetlib.Telnet(UPC_DEV_IP, UPC_DEV_PORT) + tn.close() + logging.debug(f"端口 {{UPC_ADDR[0]}}:{{UPC_ADDR[1]}} Telnet 检查通过。") + except OSError as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"!!!!!ERROR!!!!!{{datetime_str}} {{UPC_ADDR}} 端口无法访问,请检查设备或网络连接: {{e}}!!!!!" + logging.error(logstr) + print(logstr) + except Exception as e: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"!!!!!ERROR!!!!!{{datetime_str}} {{UPC_ADDR}} Telnet检查时发生未知错误: {{e}}!!!!!" + logging.error(logstr, exc_info=True) + print(logstr) + else: + upc_send_command(UPC_ADDR, upc_command, LOG_FILE) diff --git a/templates/sender_udp.py.tpl b/templates/sender_udp.py.tpl new file mode 100644 index 0000000..7ea0a77 --- /dev/null +++ b/templates/sender_udp.py.tpl @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +# Auto-generated from template. DO NOT EDIT DIRECTLY! + +import sys +import socket +import time +import logging + + +def udp_command_send(upc_addr, upc_command, data, timeout=2): + """通过UDP发送数据并接收响应""" + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(timeout) + + try: + logging.debug(f"尝试发送UDP数据到 {{upc_addr[0]}}:{{upc_addr[1]}}...") + + logging.info(f"正在发送UDP命令 '{{upc_command}}' 到 {{upc_addr}}: {{data!r}} (原始字节)") + sock.sendto(data, upc_addr) + logging.debug(f"UDP数据发送完成。") + + # 尝试接收响应 + recv_buffer_size = 1024 + try: + logging.debug(f"等待从 {{upc_addr}} 接收UDP响应...") + response_bytes, addr = sock.recvfrom(recv_buffer_size) + if response_bytes: + logging.info(f"从 {{addr}} 收到UDP原始响应字节: {{response_bytes!r}}") + try: + response_str = response_bytes.decode('utf-8', errors='ignore') + logging.info(f"从 {{addr}} 收到UDP解码响应字符串: '{{response_str}}'") + except Exception as decode_e: + logging.warning(f"解码UDP响应时发生错误: {{decode_e}}") + else: + logging.info(f"未收到UDP响应数据。") + except socket.timeout: + logging.warning(f"接收UDP响应超时(设备可能不返回响应,这通常是正常的)。") + except OSError as recv_e: + logging.error(f"接收UDP响应时发生网络错误: {{recv_e}}") + + sock.close() + logging.debug(f"UDP socket关闭。") + return True + except OSError as e: + logging.error(f"UDP命令 '{{upc_command}}' 发送至 {{upc_addr}} 失败: {{e}}") + return False + finally: + try: + sock.close() + except: + pass + + +# 指令定义表(从配置文件读取) +COMMAND_DEFINITIONS = {command_definitions} + + +def upc_send_command(upc_addr, upc_command, log_file): + """将命令发送给终端设备""" + data = None + + # 查找指令定义 + if upc_command in COMMAND_DEFINITIONS: + hex_str = COMMAND_DEFINITIONS[upc_command] + data = bytes.fromhex(hex_str) + + if data is None: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} 未知命令: {{upc_command}},没有对应的数据可发送。" + logging.warning(logstr) + print(logstr) + else: + i = 1 + while i <= 3: # 尝试发送3次 + if udp_command_send(upc_addr, upc_command, data): + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} {{upc_command}}命令第{{i}}次发送成功" + logging.info(logstr) + return + else: + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} {{upc_command}}命令第{{i}}次发送失败,3秒后进行第{{i+1}}次尝试" + logging.warning(logstr) + time.sleep(3) + i = i + 1 + + datetime_str = time.strftime(ISOTIMEFORMAT, time.localtime(time.time())) + logstr = f"{{datetime_str}} {{upc_command}}命令在3次尝试后发送均失败!" + logging.error(logstr) + + +if __name__ == '__main__': + ISOTIMEFORMAT = '%Y-%m-%d %X' + + UPC_DEV_IP = sys.argv[1] + UPC_DEV_PORT = int(sys.argv[2]) + upc_command = sys.argv[3] + LOG_FILE = sys.argv[4] + + logging.basicConfig(filename=LOG_FILE, filemode="a", level=logging.DEBUG) + UPC_ADDR = (UPC_DEV_IP, UPC_DEV_PORT) + + # UDP 是无连接的,无需像TCP那样预先检查端口连通性 + # 直接发送数据 + logging.info(f"UDP模式:准备发送命令到 {{UPC_ADDR}}") + upc_send_command(UPC_ADDR, upc_command, LOG_FILE) diff --git a/upcstart.sh b/upcstart.sh new file mode 100644 index 0000000..fa029f3 --- /dev/null +++ b/upcstart.sh @@ -0,0 +1,3 @@ +#!/bin/bash +su root -c "/usr/bin/python /opt/upc_resent/bin/192.168.8.73.py &" +su root -c "/usr/bin/python /opt/upc_resent/bin/192.168.8.125.py &"