You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
9.1 KiB
Python

4 months ago
import socket
import struct
import threading
import time
import logging
import random
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
class Box1Simulator:
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):
self.host = host
self.port = port
self.server_socket = None
self.stop_event = threading.Event()
# 模拟设备状态
self.ai_values = [0.0] * 8
self.di_states = [0] * 16
self.ao_values = [0.0] * 36
self.deltat_values = [0] * 16
# 初始化模拟值
self.reset_simulated_values()
# 启动服务
threading.Thread(target=self.run_server, daemon=True).start()
logging.info(f"Box1 simulator started on {host}:{port}")
def reset_simulated_values(self):
"""重置模拟值到初始状态"""
4 months ago
# 模拟AI值 (4-20mA范围单位A)
self.ai_values = [random.uniform(0.004, 0.020) for _ in range(8)]
4 months ago
# 模拟DI状态 (随机0或1)
self.di_states = [random.randint(0, 1) for _ in range(16)]
# 模拟AO值 (初始全0)
self.ao_values = [0.0] * 36
# 模拟DeltaT值 (时间戳差)
self.deltat_values = [random.randint(1000, 5000) for _ in range(16)]
logging.info("Simulated values reset")
def run_server(self):
"""运行TCP服务器"""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen(5)
while not self.stop_event.is_set():
try:
client_sock, addr = self.server_socket.accept()
logging.info(f"New connection from {addr}")
threading.Thread(target=self.handle_client, args=(client_sock,), daemon=True).start()
except socket.timeout:
pass
except Exception as e:
logging.error(f"Server error: {e}")
break
def handle_client(self, client_sock):
"""处理客户端连接"""
buffer = b''
try:
while not self.stop_event.is_set():
data = client_sock.recv(4096)
if not data:
break
buffer += data
while True:
start_pos = buffer.find(self.START_FLAG)
if start_pos == -1:
break
if start_pos > 0:
buffer = buffer[start_pos:]
if len(buffer) < len(self.START_FLAG) + self.HEADER_SIZE:
break
header = buffer[len(self.START_FLAG):len(self.START_FLAG) + self.HEADER_SIZE]
try:
cmd_type, data_size, packet_num = struct.unpack('>H I I', header)
except struct.error:
logging.warning("Invalid header format")
buffer = buffer[len(self.START_FLAG):]
continue
frame_len = len(self.START_FLAG) + self.HEADER_SIZE + data_size + len(self.END_FLAG)
if len(buffer) < frame_len:
break
full_frame = buffer[:frame_len]
buffer = buffer[frame_len:]
if full_frame[-len(self.END_FLAG):] != self.END_FLAG:
logging.warning("Invalid end flag")
continue
body = full_frame[len(self.START_FLAG) + self.HEADER_SIZE:-len(self.END_FLAG)]
# 处理客户端命令
response = self.process_command(cmd_type, body, packet_num)
if response:
client_sock.sendall(response)
except Exception as e:
logging.error(f"Client handling error: {e}")
finally:
client_sock.close()
logging.info("Client disconnected")
def process_command(self, cmd_type, body, packet_num):
"""处理客户端命令并生成响应"""
try:
# 读取AI命令
if cmd_type == self.CMD_READ_AI:
return self.create_ai_response(packet_num)
# 写入AO命令
elif cmd_type == self.CMD_WRITE_AO:
if len(body) != 288: # 36 doubles * 8 bytes
logging.error(f"Invalid AO data length: {len(body)}")
return None
4 months ago
# 修改:使用小端字节序解包数据
ao_data = struct.unpack('<' + 'd'*36, body)
4 months ago
self.ao_values = list(ao_data)
logging.info(f"接收到的AO: {ao_data}... (total {len(ao_data)} values)")
return self.create_ao_response(True, packet_num)
# 读取DeltaT命令
elif cmd_type == self.CMD_READ_DELTAT:
# 更新一些DeltaT值以模拟变化
for i in range(4):
self.deltat_values[i] += random.randint(1, 10)
print("deltatT为", self.deltat_values)
return self.create_deltat_response(packet_num)
else:
logging.warning(f"Unknown command type: {cmd_type}")
return None
except Exception as e:
logging.error(f"Command processing error: {e}")
return None
def create_ai_response(self, packet_num):
"""创建AI响应数据包"""
4 months ago
# 模拟值在4-20mA范围内轻微波动单位A
4 months ago
for i in range(8):
4 months ago
self.ai_values[i] += random.uniform(-0.001, 0.001)
# 限制在4-20mA范围内 (0.004-0.020A)
self.ai_values[i] = max(0.004, min(0.020, self.ai_values[i]))
# print(self.ai_values)
4 months ago
# 随机翻转一些DI状态
for i in range(4):
if random.random() < 0.2: # 20%几率翻转
self.di_states[i] = 1 - self.di_states[i]
4 months ago
# print(self.di_states, 1111)
4 months ago
4 months ago
# 修改生成500组AI/DI数据
full_body = b''
4 months ago
for i in range(8):
for _ in range(500):
# 修复:移除解包操作符(*),直接传入浮点数
ai_bytes = struct.pack('<d', self.ai_values[i])
4 months ago
# 打包DI状态为一个64位整数
4 months ago
# 添加一组数据到响应体
full_body += ai_bytes
for _ in range(500):
4 months ago
di_state_int = 0
for i, state in enumerate(self.di_states):
di_state_int |= (state << i)
4 months ago
di_bytes = struct.pack('<d', di_state_int)
# print(di_bytes, di_state_int)
full_body += di_bytes
# print(len(full_body))
4 months ago
4 months ago
# 使用logging记录发送的AI数据
4 months ago
# logging.info(f"发送的AI为: AI={self.ai_values}, DI={self.di_states} (共500组)")
4 months ago
4 months ago
return self.create_packet(self.RESP_READ_AI, full_body, packet_num)
4 months ago
def create_ao_response(self, success, packet_num):
"""创建AO写入响应数据包"""
# 使用0.0表示成功1.0表示失败
result = 0.0 if success else 1.0
4 months ago
# 修改:使用小端字节序
body = struct.pack('<d', result)
4 months ago
return self.create_packet(self.RESP_WRITE_AO, body, packet_num)
def create_deltat_response(self, packet_num):
"""创建DeltaT响应数据包"""
4 months ago
# 修改使用小端字节序打包16个64位无符号整数
body = struct.pack('<' + '16Q', *self.deltat_values)
# 使用logging记录发送的DeltaT数据
logging.info(f"发送的DeltaT为: {self.deltat_values}")
4 months ago
return self.create_packet(self.RESP_READ_DELTAT, body, packet_num)
def create_packet(self, cmd_type, body, packet_num):
"""创建完整的数据包"""
data_size = len(body)
header = struct.pack('>H I I', cmd_type, data_size, packet_num)
return self.START_FLAG + header + body + self.END_FLAG
def shutdown(self):
"""停止服务器"""
self.stop_event.set()
if self.server_socket:
self.server_socket.close()
logging.info("Box1 simulator stopped")
if __name__ == "__main__":
# 创建模拟器实例
simulator = Box1Simulator("localhost", 8000)
try:
# 运行模拟器直到手动停止
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nStopping simulator...")
simulator.shutdown()