first commit
This commit is contained in:
226
midware/drivers/tcp_uart_agent.py
Normal file
226
midware/drivers/tcp_uart_agent.py
Normal file
@@ -0,0 +1,226 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user