import socket import struct import threading import time import logging from collections import deque logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') class TCPCommunicator: START_FLAG = b'\xF3\x12' END_FLAG = b'\xF3\x14' HEADER_SIZE = 10 # 命令类型 CMD_READ_AI = 1 CMD_WRITE_AO = 2 CMD_READ_DELTAT = 3 RESP_READ_AI = 11 RESP_WRITE_AO = 12 RESP_READ_DELTAT = 13 def __init__(self, host, port, reconnectInterval=3): self.host = host self.port = port self.reconnectInterval = reconnectInterval self.sock = None self.connected = False self.packetCounter = 0 self.lock = threading.Lock() self.stopEvent = threading.Event() # 回调函数 self.aiCallback = None self.deltatCallback = None self.aoCallback = None # 响应等待机制 self.pending_responses = {} self.response_lock = threading.Lock() # 新增就绪状态标志 self.isReady = False # 服务器是否完全就绪 self.connect() # 启动连接和接收线程 threading.Thread(target=self.connectThread, daemon=True).start() threading.Thread(target=self.receiveThread, daemon=True).start() def registerCallbacks(self, aiCallback=None, deltatCallback=None, aoCallback=None): self.aiCallback = aiCallback self.deltatCallback = deltatCallback self.aoCallback = aoCallback def connectThread(self): while not self.stopEvent.is_set(): if not self.connected: self.connect() # 重连后执行就绪检查 time.sleep(0.5) def connect(self): try: if self.sock: self.sock.close() self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.host, self.port)) self.connected = True self.packetCounter = 0 self.isReady = False # 重置就绪状态 # 连接后执行就绪检 logging.info(f"Connected to {self.host}:{self.port}") return True except Exception as e: logging.error(f"Connection failed: {e}") self.connected = False time.sleep(self.reconnectInterval) return False def sendCommand(self, cmdType, data=None, skipReadyCheck=False): # 增加就绪状态检查 # 增加连接状态检查,确保完全连接后再发送 if not self.connected or not self.sock: print("Not connected") if not self.connect(): return None try: with self.lock: body = b'' if cmdType == self.CMD_WRITE_AO and data: if len(data) != 36: raise ValueError("AO data requires 36 elements") body = struct.pack('<' + 'd'*36, *data) elif cmdType == self.CMD_READ_AI or cmdType == self.CMD_READ_DELTAT: body = struct.pack('H I I', cmdType, dataSize, current_counter) packet = self.START_FLAG + header + body + self.END_FLAG # 创建等待事件 response_event = threading.Event() with self.response_lock: self.pending_responses[current_counter] = (cmdType, response_event, deque()) self.sock.sendall(packet) self.packetCounter += 1 return current_counter except Exception as e: logging.error(f"Send error: {e}") self.connected = False return None def waitForResponse(self, packetNum, expectedType, timeout=5): """等待指定packetNum的响应""" try: with self.response_lock: if packetNum not in self.pending_responses: logging.error(f"Invalid packetNum: {packetNum}") return None _, event, result_queue = self.pending_responses[packetNum] # 等待响应或超时 if not event.wait(timeout): logging.warning(f"Response timeout for packet {packetNum}") return None with self.response_lock: # 检查结果是否有效 if not result_queue: return None result = result_queue.popleft() # 验证响应类型 if result[0] != expectedType + 10: # 响应类型 = 命令类型 + 10 logging.error(f"Response type mismatch. Expected:{expectedType+10}, Got:{result[0]}") return None return result[1] finally: with self.response_lock: if packetNum in self.pending_responses: del self.pending_responses[packetNum] def receiveThread(self): buffer = b'' while not self.stopEvent.is_set(): try: if not self.connected: time.sleep(0.5) continue data = self.sock.recv(4096) if not data: self.connected = False continue buffer += data while True: startPos = buffer.find(self.START_FLAG) if startPos == -1: break # 移除起始标志前的无效数据 if startPos > 0: buffer = buffer[startPos:] # 检查是否收到完整头部 if len(buffer) < len(self.START_FLAG) + self.HEADER_SIZE: break # 解析头部 header = buffer[len(self.START_FLAG):len(self.START_FLAG) + self.HEADER_SIZE] dataType, dataSize, packetNum = struct.unpack('>H I I', header) # 检查是否收到完整数据包 frameLen = len(self.START_FLAG) + self.HEADER_SIZE + dataSize + len(self.END_FLAG) if len(buffer) < frameLen: break # 提取完整数据帧 fullFrame = buffer[:frameLen] buffer = buffer[frameLen:] # 验证结束标志 if fullFrame[-len(self.END_FLAG):] != self.END_FLAG: logging.warning("Invalid end flag. Discarding packet.") continue # 处理数据包 body = fullFrame[len(self.START_FLAG) + self.HEADER_SIZE:-len(self.END_FLAG)] self.processData(dataType, body, packetNum) except Exception as e: logging.error(f"Receive error: {e}") self.connected = False def processData(self, dataType, body, packetNum): try: result = None # print(body, dataType) # AI响应处理 if dataType == self.RESP_READ_AI: # print(body) if len(body) != 36000: logging.error(f"Invalid AI response length: {len(body)}") return # print(1) l = [] for i in range(9): # print(i) start_idx = i * 4000 #end_idx_ai = start_idx + 64 #end_idx_di = start_idx + 72 # 解析8个AI值 (64字节) ai_chunk = body[start_idx:start_idx + 8] #print(ai_chunk) if i == 8: diValues = struct.unpack('> j) & 1 for j in range(16)] l.append(diBools) else: aiValues = struct.unpack('> j) & 1 for j in range(16)] # all_ai_values.append(ai_values) # all_di_states.append(di_bools) # print(l) result = (l[0:8], l[8]) # print(result, 111) if self.aiCallback: self.aiCallback(result[0], result[1], packetNum) # DeltaT响应处理 elif dataType == self.RESP_READ_DELTAT: if len(body) != 128: logging.error(f"Invalid DeltaT response length: {len(body)}") return #(body) deltatValues = struct.unpack('<16Q', body) result = deltatValues if self.deltatCallback: self.deltatCallback(deltatValues, packetNum) # AO响应处理 elif dataType == self.RESP_WRITE_AO: # print(len(body)) success = struct.unpack('