first commit

This commit is contained in:
2026-03-12 17:03:56 +08:00
commit aa4d4c7d7c
48 changed files with 10958 additions and 0 deletions

Binary file not shown.

23
test/control.py Normal file
View File

@@ -0,0 +1,23 @@
import socket
from protocol import build_frame
SERVER_IP = '127.0.0.1'
SERVER_PORT = 9000
TARGET_DEVICE = 10000052
# 控制 2、3 开5 关
payload = bytes([
0x02, 0x01,
0x03, 0x01,
0x05, 0x00
])
frame = build_frame(0xA0, TARGET_DEVICE, payload)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((SERVER_IP, SERVER_PORT))
# CTRL + device_id + frame
msg = b'CTRL' + TARGET_DEVICE.to_bytes(4, 'little') + frame
s.sendall(msg)
print("[CONTROL] send command")

29
test/device.py Normal file
View File

@@ -0,0 +1,29 @@
import socket
import time
from protocol import build_frame
SERVER_IP = '127.0.0.1'
SERVER_PORT = 9000
DEVICE_ID = 10000052 # 每个设备改这个
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((SERVER_IP, SERVER_PORT))
print(f"[DEVICE {DEVICE_ID}] connected")
while True:
# 发送心跳
hb = build_frame(0x00, DEVICE_ID)
s.sendall(hb)
# 接收控制指令
s.settimeout(1)
try:
data = s.recv(1024)
if data and data[8] == 0xA0:
print(f"[DEVICE {DEVICE_ID}] recv A0:", data.hex(' '))
# 这里执行你的 IO / GPIO
except socket.timeout:
pass
time.sleep(3)

22
test/protocol.py Normal file
View File

@@ -0,0 +1,22 @@
def sum16(data: bytes) -> int:
return sum(data) & 0xFFFF
def build_frame(cmd: int, device_id: int, payload: bytes = b"") -> bytes:
frame = bytearray()
frame += b'\x23\xA9' # 帧头
frame += b'\x00\x00' # 长度占位
frame += device_id.to_bytes(4, 'little')
frame += bytes([cmd])
frame += payload
length = len(frame) + 2 # 加上校验和
frame[2:4] = length.to_bytes(2, 'big')
s = sum16(frame)
frame += s.to_bytes(2, 'big')
return bytes(frame)
def parse_device_id(frame: bytes) -> int:
return int.from_bytes(frame[4:8], 'little')

41
test/server.py Normal file
View File

@@ -0,0 +1,41 @@
import socket
import threading
HOST = "127.0.0.1"
PORT = 9000
devices = {} # device_id -> socket
def handle_client(conn):
while True:
data = conn.recv(1024)
if not data:
break
# 前 4 字节当 device_id小端
device_id = int.from_bytes(data[4:8], "little")
print("[SERVER] recv for device:", device_id, data.hex(" "))
if device_id in devices:
devices[device_id].send(data)
print("[SERVER] forwarded to device", device_id)
def accept_loop():
s = socket.socket()
s.bind((HOST, PORT))
s.listen()
print("[SERVER] listening")
while True:
conn, _ = s.accept()
# 第一次 recv 认为是设备注册
first = conn.recv(1024)
if first.startswith(b"DEVICE"):
device_id = int(first.split(b":")[1])
devices[device_id] = conn
print(f"[SERVER] device {device_id} registered")
else:
threading.Thread(target=handle_client, args=(conn,), daemon=True).start()
accept_loop()

149
test/test_play_data.py Normal file
View File

@@ -0,0 +1,149 @@
import time
import requests
import pandas as pd
from io import BytesIO
class CheckStation:
def __init__(self):
self.station_num = 0
self.last_data = None
def get_measure_data(self):
# 模拟获取测量数据
pass
def add_transition_point(self):
# 添加转点逻辑
print("添加转点")
return True
def get_excel_from_url(self, url):
"""
从URL获取Excel文件并解析为字典
Excel只有一列数据A列每行是站点值
Args:
url: Excel文件的URL地址
Returns:
dict: 解析后的站点数据字典 {行号: 值}失败返回None
"""
try:
print(f"正在从URL获取数据: {url}")
response = requests.get(url, timeout=30)
response.raise_for_status() # 检查请求是否成功
# 使用pandas读取Excel数据指定没有表头只读第一个sheet
excel_data = pd.read_excel(
BytesIO(response.content),
header=None, # 没有表头
sheet_name=0, # 只读取第一个sheet
dtype=str # 全部作为字符串读取
)
station_dict = {}
# 解析Excel数据使用行号+1作为站点编号A列的值作为站点值
print("解析Excel数据使用行号作为站点编号...")
for index, row in excel_data.iterrows():
station_num = index + 1 # 行号从1开始作为站点编号
station_value = str(row[0]).strip() if pd.notna(row[0]) else ""
if station_value: # 只保存非空值
station_dict[station_num] = station_value
print(f"成功解析Excel{len(station_dict)}条数据")
return station_dict
except requests.exceptions.RequestException as e:
print(f"请求URL失败: {e}")
return None
except Exception as e:
print(f"解析Excel失败: {e}")
return None
def check_station_exists(self, station_data: dict, station_num: int) -> str:
"""
根据站点编号检查该站点的值是否以Z开头
Args:
station_data: 站点数据字典 {编号: 值}
station_num: 要检查的站点编号
Returns:
str: 如果站点存在且以Z开头返回"add",否则返回"pass"
"""
if station_num not in station_data:
print(f"站点{station_num}不存在")
return "error"
value = station_data[station_num]
str_value = str(value).strip()
is_z = str_value.upper().startswith('Z')
result = "add" if is_z else "pass"
print(f"站点{station_num}: {value} -> {result}")
return result
def run(self):
last_station_num = 0
url = f"https://database.yuxindazhineng.com/team-bucket/69378c5b4f42d83d9504560d/前测点表/20260309/CDWZQ-2标-龙家沟左线大桥-0-11号墩-平原.xlsx"
station_data = self.get_excel_from_url(url)
print(station_data)
station_quantity = len(station_data)
over_station_num = 0
over_station_list = []
while over_station_num < station_quantity:
try:
# 键盘输出线路编号
station_num_input = input("请输入线路编号:")
if not station_num_input.isdigit(): # 检查输入是否为数字
print("输入错误:请输入一个整数")
continue
station_num = int(station_num_input) # 转为整数
if station_num in over_station_list:
print("已处理该站点,跳过")
continue
if last_station_num == station_num:
print("输入与上次相同,跳过处理")
continue
last_station_num = station_num
result = self.check_station_exists(station_data, station_num)
if result == "error":
print("处理错误:站点不存在")
# 错误处理逻辑,比如记录日志、发送警报等
elif result == "add":
print("执行添加操作")
# 添加转点
if not self.add_transition_point():
print("添加转点失败")
# 可以决定是否继续循环
continue
over_station_num += 1
else: # result == "pass"
print("跳过处理")
over_station_num += 1
over_station_list.append(station_num)
# 可以添加适当的延时避免CPU占用过高
# time.sleep(1)
except KeyboardInterrupt:
print("程序被用户中断")
break
except Exception as e:
print(f"发生错误: {e}")
time.sleep(20)
# 错误处理,可以继续循环或退出
print(f"已处理{over_station_num}个站点")
if __name__ == "__main__":
monitor = StationMonitor()
monitor.run()