Files
virtual_simulation_midware/midware/drivers/tcp_uart_agent.py

226 lines
8.2 KiB
Python
Raw Normal View History

2026-06-16 15:40:19 +08:00
from queue import Full
import socket
import threading
import time
from typing import Dict, Optional
from loguru import logger
import telnetlib #2026/6/2
# 全局TCP客户端管理每个UART一个实例
tcp_clients: Dict[str, "TCPUARTClient"] = {}
# HEAD = b"\xeb\x90" #地测帧头
# TAIL = b"\x57\x16" #地测帧尾
# MIN_FRAME = 23 ##地测长度最小值
# MAX_FRAME = 1000 ##地测帧长度最大值
class TCPUARTClient:
def __init__(self, dev_name: str, local_port: int, remote_port: int, remote_ip="127.0.0.1"):
self.dev_name = dev_name
self.local_port = local_port
self.remote_port = remote_port
self.remote_ip = remote_ip
self.socket = None
self.running = False
self.thread = None
self.HEAD = b"\xeb\x90" #地测帧头
self.TAIL = b"\x57\x16" #地测帧尾
self.MIN_LEN = 23 ##地测长度最小值
self.MAX_LEN = 1000 ##地测帧长度最大值
self.tcp_buf = b''
#self.tn = None
def connect(self):
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 关键点禁止Nagle算法模仿网络调试助手强制使用纯raw socket连接但是导致TCP收不到数据 2026/6/2
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #停用
#self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) #telnet
self.socket.bind(("127.0.0.1", self.local_port))
self.socket.connect((self.remote_ip, self.remote_port))
self.socket.setblocking(False)
self.running = True
print(f"[TCP-UART] {self.dev_name} 连接成功: local={self.local_port} remote={self.remote_port}")
#telnet
#self.tn = telnetlib.Telnet("127.0.0.1",{self.remote_port},timeout=5)
return True
except Exception as e:
print(f"[TCP-UART] {self.dev_name} 连接失败: {e}")
return False
'''
# 去除转义FF的函数 2026/6/2,可能有安全问题
def unescape_ff(raw:bytes)->bytes:
buf = bytearray()
i = 0
n = len(raw)
while i<n:
buf.append(raw[i])
#当前字节FF下一个字节FF跳过下一个字节只保留一个
if raw[i] == 0xFF and i+1<n and raw[i+1]==0xFF:
i +=1
i +=1
return bytes(buf)
'''
# 去除转义FF的函数 2026/6/2
def unescape_ff(self, raw:bytes)->bytes:
out = bytearray()
idx=0
length = len(raw)
while idx<length:
cur = raw[idx]
out.append(cur)
if cur == 0xff and (idx + 1 < length) and raw[idx+1]==0xff:
idx+=1
idx+=1
return bytes(out)
def receive_loop(self):
from midware.core.bus_udp_decoupler import bus_recv_queues
from midware.config.base_config import DEVICE_CONFIG_DICT
while self.running:
try:
#data = self.tn.read_some()
data = self.socket.recv(8192)#4096修改为16384 2026/5/12 tmy 65536->8192 2026/5/15
if not data:
time.sleep(0.001)
continue
self.tcp_buf += data #新数据追加到缓存尾部2026/6/2
dev_name = None
protocol = None
for name, cfg in DEVICE_CONFIG_DICT.items():
if cfg.get("tcp_bus_port") == self.remote_port:
dev_name = name
protocol = cfg.get("protocol")
break
if not dev_name or not protocol:
continue
#循环拆包大多数情况是只跑0~1次循环 2026/6/2
while True:
if len(self.tcp_buf)>self.MAX_LEN * 2:
self.tcp_buf = self.tcp_buf[-1000:]
h_pos = self.tcp_buf.find(self.HEAD)
if h_pos ==-1:
break
#截断帧头前面垃圾
if h_pos>0:
self.tcp_buf = self.tcp_buf[h_pos:]
#缓存不足最小帧,直接退出
if(len(self.tcp_buf)<self.MIN_LEN):
break
#从帧头向后查找结束符号
t_pos = self.tcp_buf.find(self.TAIL,2)
if t_pos == -1:
break
frame = self.tcp_buf[:t_pos+2]
#logger.info(f'去除转义FF帧前的有问题帧内容{frame.hex()}')
if(self.MIN_LEN<= len(frame) <= self.MAX_LEN):
#去除转义FF
#logger.info(f'去除转义FF帧内容')
real_data = self.unescape_ff(frame)
#logger.info(f'去除转义FF帧内容{real_data.hex()}')
#入队
try:
q = bus_recv_queues.get(protocol)
if q:
try:
q.put_nowait({
"dev_name": dev_name,
"raw_data": real_data,
"protocol": protocol,
})
logger.debug(f'接受TCP重定向数据存入bus_recv_queues内容{real_data.hex()}')
except Full as e:
print("队列已满",e)
except Exception as e:
print("其他异常",repr(e))
except:
pass
#切掉已经解析的帧
self.tcp_buf = self.tcp_buf[(t_pos+2) :]
'''
# 匹配设备名
dev_name = None
protocol = None
for name, cfg in DEVICE_CONFIG_DICT.items():
if cfg.get("tcp_bus_port") == self.remote_port:
dev_name = name
protocol = cfg.get("protocol")
break
if not dev_name or not protocol:
continue
logger.info(f'接受TCP重定向数据存入bus_recv_queues内容{data.hex()}')
# 推入现有接收队列(完全兼容你原有架构)
q = bus_recv_queues.get(protocol)
if q:
try:
q.put_nowait({
"dev_name": dev_name,
"raw_data": data,
"protocol": protocol,
})
except:
pass
'''
except BlockingIOError:
time.sleep(0.001)
except Exception as e:
print(f"[TCP-UART] {self.dev_name} 接收异常: {e}")
break
self.running = False
def start(self):
if self.connect():
self.thread = threading.Thread(target=self.receive_loop, daemon=True)
self.thread.start()
tcp_clients[self.dev_name] = self
def stop(self):
self.running = False
if self.socket:
self.socket.close()
# ======================
# 批量启动所有UART TCP客户端
# ======================
def start_all_tcp_uart_clients():
from midware.config.base_config import DEVICE_CONFIG_DICT
for dev_name, cfg in DEVICE_CONFIG_DICT.items():
tcp_local = cfg.get("tcp_local_port", 1)
tcp_bus = cfg.get("tcp_bus_port", 1)
# 只启动端口 !=1 的UART设备
if tcp_local != 1 and tcp_bus != 1:
client = TCPUARTClient(
dev_name=dev_name,
local_port=tcp_local,
remote_port=tcp_bus
)
client.start()
time.sleep(0.1)