Files
Uni-Lab-OS/unilabos/devices/xrd_d7mate/xrd_d7mate.py
2025-11-15 02:23:09 +08:00

939 lines
37 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
XRD D7-Mate设备驱动
支持XRD D7-Mate设备的TCP通信协议包括自动模式控制、上样流程、数据获取、下样流程和高压电源控制等功能。
通信协议版本1.0.0
"""
import json
import socket
import struct
import time
from typing import Dict, List, Optional, Tuple, Any, Union
class XRDClient:
def __init__(self, host='127.0.0.1', port=6001, timeout=10.0):
"""
初始化XRD D7-Mate客户端
Args:
host (str): 设备IP地址
port (int): 通信端口默认6001
timeout (float): 超时时间,单位秒
"""
self.host = host
self.port = port
self.timeout = timeout
self.sock = None
self._ros_node = None # ROS节点引用由框架设置
def post_init(self, ros_node):
"""
ROS节点初始化后的回调方法保存ROS节点引用但不自动连接
Args:
ros_node: ROS节点实例
"""
self._ros_node = ros_node
ros_node.lab_logger().info(f"XRD D7-Mate设备已初始化将在需要时连接: {self.host}:{self.port}")
# 不自动连接,只有在调用具体功能时才建立连接
def connect(self):
"""
建立TCP连接到XRD D7-Mate设备
Raises:
ConnectionError: 连接失败时抛出
"""
try:
self.sock = socket.create_connection((self.host, self.port), timeout=self.timeout)
self.sock.settimeout(self.timeout)
except Exception as e:
raise ConnectionError(f"Failed to connect to {self.host}:{self.port} - {str(e)}")
def close(self):
"""
关闭与XRD D7-Mate设备的TCP连接
"""
if self.sock:
try:
self.sock.close()
except Exception:
pass # 忽略关闭时的错误
finally:
self.sock = None
def _ensure_connection(self) -> bool:
"""
确保连接存在,如果不存在则尝试建立连接
Returns:
bool: 连接是否成功建立
"""
if self.sock is None:
try:
self.connect()
return True
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"建立连接失败: {e}")
return False
return True
def _receive_with_length_prefix(self) -> dict:
"""
使用长度前缀协议接收数据
Returns:
dict: 解析后的JSON响应数据
Raises:
ConnectionError: 连接错误
TimeoutError: 超时错误
"""
try:
# 首先接收4字节的长度信息
length_data = bytearray()
while len(length_data) < 4:
chunk = self.sock.recv(4 - len(length_data))
if not chunk:
raise ConnectionError("Connection closed while receiving length prefix")
length_data.extend(chunk)
# 解析长度(大端序无符号整数)
data_length = struct.unpack('>I', length_data)[0]
if self._ros_node:
self._ros_node.lab_logger().info(f"接收到数据长度: {data_length} 字节")
# 根据长度接收实际数据
json_data = bytearray()
while len(json_data) < data_length:
remaining = data_length - len(json_data)
chunk = self.sock.recv(min(4096, remaining))
if not chunk:
raise ConnectionError("Connection closed while receiving JSON data")
json_data.extend(chunk)
# 解码JSON数据优先使用UTF-8失败时尝试GBK
try:
json_str = json_data.decode('utf-8')
except UnicodeDecodeError:
json_str = json_data.decode('gbk')
# 解析JSON
result = json.loads(json_str)
if self._ros_node:
self._ros_node.lab_logger().info(f"成功解析JSON响应: {result}")
return result
except socket.timeout:
if self._ros_node:
self._ros_node.lab_logger().warning(f"接收超时")
raise TimeoutError(f"recv() timed out after {self.timeout:.1f}s")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"接收数据失败: {e}")
raise ConnectionError(f"Failed to receive data: {str(e)}")
def _send_command(self, cmd: dict) -> dict:
"""
使用长度前缀协议发送命令到XRD D7-Mate设备并接收响应
Args:
cmd (dict): 要发送的命令字典
Returns:
dict: 设备响应的JSON数据
Raises:
ConnectionError: 连接错误
TimeoutError: 超时错误
"""
# 确保连接存在,如果不存在则建立连接
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info(f"为命令重新建立连接")
except Exception as e:
raise ConnectionError(f"Failed to establish connection: {str(e)}")
try:
# 序列化命令为JSON
json_str = json.dumps(cmd, ensure_ascii=False)
payload = json_str.encode('utf-8')
# 计算JSON数据长度并打包为4字节大端序无符号整数
length_prefix = struct.pack('>I', len(payload))
if self._ros_node:
self._ros_node.lab_logger().info(f"发送JSON命令到XRD D7-Mate: {json_str}")
self._ros_node.lab_logger().info(f"发送数据长度: {len(payload)} 字节")
# 发送长度前缀
self.sock.sendall(length_prefix)
# 发送JSON数据
self.sock.sendall(payload)
# 使用长度前缀协议接收响应
response = self._receive_with_length_prefix()
return response
except Exception as e:
# 如果是连接错误,尝试重新连接一次
if "远程主机强迫关闭了一个现有的连接" in str(e) or "10054" in str(e):
if self._ros_node:
self._ros_node.lab_logger().warning(f"连接被远程主机关闭,尝试重新连接: {e}")
try:
self.close()
self.connect()
# 重新发送命令
json_str = json.dumps(cmd, ensure_ascii=False)
payload = json_str.encode('utf-8')
if self._ros_node:
self._ros_node.lab_logger().info(f"重新发送JSON命令到XRD D7-Mate: {json_str}")
self.sock.sendall(payload)
# 重新接收响应
buffer = bytearray()
start = time.time()
while True:
try:
chunk = self.sock.recv(4096)
if not chunk:
break
buffer.extend(chunk)
# 尝试解码和解析JSON
try:
text = buffer.decode('utf-8', errors='strict')
text = text.strip()
if text.startswith('{'):
brace_count = 0
json_end = -1
for i, char in enumerate(text):
if char == '{':
brace_count += 1
elif char == '}':
brace_count -= 1
if brace_count == 0:
json_end = i + 1
break
if json_end > 0:
text = text[:json_end]
result = json.loads(text)
if self._ros_node:
self._ros_node.lab_logger().info(f"重连后成功解析JSON响应: {result}")
return result
except (UnicodeDecodeError, json.JSONDecodeError):
pass
except socket.timeout:
if self._ros_node:
self._ros_node.lab_logger().warning(f"重连后接收超时")
raise TimeoutError(f"recv() timed out after reconnection")
if time.time() - start > self.timeout * 2:
raise TimeoutError(f"No complete JSON received after reconnection")
except Exception as retry_e:
if self._ros_node:
self._ros_node.lab_logger().error(f"重连失败: {retry_e}")
raise ConnectionError(f"Connection retry failed: {str(retry_e)}")
if isinstance(e, (ConnectionError, TimeoutError)):
raise
else:
raise ConnectionError(f"Command send failed: {str(e)}")
# ==================== 自动模式控制 ====================
def start_auto_mode(self, status: bool) -> dict:
"""
启动或停止自动模式
Args:
status (bool): True-启动自动模式False-停止自动模式
Returns:
dict: 响应结果包含status、timestamp、message
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
# 按协议要求content 直接为布尔值使用传入的status参数
cmd = {
"command": "START_AUTO_MODE",
"content": {
"status": bool(True)
}
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送自动模式控制命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到自动模式控制响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"自动模式控制失败: {e}")
return {"status": False, "message": f"自动模式控制失败: {str(e)}"}
# ==================== 上样流程 ====================
def get_sample_request(self) -> dict:
"""
上样请求,检查是否允许上样
Returns:
dict: 响应结果包含status、timestamp、message
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "GET_SAMPLE_REQUEST",
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送上样请求命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到上样请求响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"上样请求失败: {e}")
return {"status": False, "message": f"上样请求失败: {str(e)}"}
def send_sample_ready(self, sample_id: str, start_theta: float, end_theta: float,
increment: float, exp_time: float) -> dict:
"""
送样完成后,发送样品信息和采集参数
Args:
sample_id (str): 样品标识符
start_theta (float): 起始角度≥5°
end_theta (float): 结束角度≥5.5°且必须大于start_theta
increment (float): 角度增量≥0.005
exp_time (float): 曝光时间0.1-5.0秒)
Returns:
dict: 响应结果包含status、timestamp、message等
"""
# 参数验证
if start_theta < 5.0:
return {"status": False, "message": "起始角度必须≥5°"}
if end_theta < 5.5:
return {"status": False, "message": "结束角度必须≥5.5°"}
if end_theta <= start_theta:
return {"status": False, "message": "结束角度必须大于起始角度"}
if increment < 0.005:
return {"status": False, "message": "角度增量必须≥0.005"}
if not (0.1 <= exp_time <= 5.0):
return {"status": False, "message": "曝光时间必须在0.1-5.0秒之间"}
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "SEND_SAMPLE_READY",
"content": {
"sample_id": sample_id,
"start_theta": start_theta,
"end_theta": end_theta,
"increment": increment,
"exp_time": exp_time
}
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送样品准备完成命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到样品准备完成响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"样品准备完成失败: {e}")
return {"status": False, "message": f"样品准备完成失败: {str(e)}"}
# ==================== 数据获取 ====================
def get_current_acquire_data(self) -> dict:
"""
获取当前正在采集的样品数据
Returns:
dict: 响应结果包含status、timestamp、sample_id、Energy、Intensity等
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "GET_CURRENT_ACQUIRE_DATA",
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送获取采集数据命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到获取采集数据响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"获取采集数据失败: {e}")
return {"status": False, "message": f"获取采集数据失败: {str(e)}"}
def get_sample_status(self) -> dict:
"""
获取工位样品状态及设备状态
Returns:
dict: 响应结果包含status、timestamp、Station等
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "GET_SAMPLE_STATUS",
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送获取样品状态命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到获取样品状态响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"获取样品状态失败: {e}")
return {"status": False, "message": f"获取样品状态失败: {str(e)}"}
# ==================== 下样流程 ====================
def get_sample_down(self, sample_station: int) -> dict:
"""
下样请求
Args:
sample_station (int): 下样工位1, 2, 3
Returns:
dict: 响应结果包含status、timestamp、sample_info等
"""
# 参数验证
if sample_station not in [1, 2, 3]:
return {"status": False, "message": "下样工位必须是1、2或3"}
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
# 按协议要求content 直接为整数工位号
cmd = {
"command": "GET_SAMPLE_DOWN",
"content": {
"Sample station":int(3)
}
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送下样请求命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到下样请求响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"下样请求失败: {e}")
return {"status": False, "message": f"下样请求失败: {str(e)}"}
def send_sample_down_ready(self) -> dict:
"""
下样完成命令
Returns:
dict: 响应结果包含status、timestamp、message
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "SEND_SAMPLE_DOWN_READY",
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送下样完成命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到下样完成响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"下样完成失败: {e}")
return {"status": False, "message": f"下样完成失败: {str(e)}"}
# ==================== 高压电源控制 ====================
def set_power_on(self) -> dict:
"""
高压电源开启
Returns:
dict: 响应结果包含status、timestamp、message
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "SET_POWER_ON",
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送高压电源开启命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到高压开启响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"高压开启失败: {e}")
return {"status": False, "message": f"高压开启失败: {str(e)}"}
def set_power_off(self) -> dict:
"""
高压电源关闭
Returns:
dict: 响应结果包含status、timestamp、message
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "SET_POWER_OFF",
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送高压电源关闭命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到高压关闭响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"高压关闭失败: {e}")
return {"status": False, "message": f"高压关闭失败: {str(e)}"}
def set_voltage_current(self, voltage: float, current: float) -> dict:
"""
设置高压电源电压和电流
Args:
voltage (float): 电压值kV
current (float): 电流值mA
Returns:
dict: 响应结果包含status、timestamp、message
"""
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备重新连接成功")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"XRD D7-Mate设备连接失败: {e}")
return {"status": False, "message": "设备连接异常"}
try:
cmd = {
"command": "SET_VOLTAGE_CURRENT",
"content": {
"voltage": voltage,
"current": current
}
}
if self._ros_node:
self._ros_node.lab_logger().info(f"发送设置电压电流命令: {cmd}")
response = self._send_command(cmd)
if self._ros_node:
self._ros_node.lab_logger().info(f"收到设置电压电流响应: {response}")
return response
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"设置电压电流失败: {e}")
return {"status": False, "message": f"设置电压电流失败: {str(e)}"}
def start(self, sample_id: str = "", start_theta: float = 10.0, end_theta: float = 80.0,
increment: float = 0.05, exp_time: float = 0.1, wait_minutes: float = 3.0,
string: str = "") -> dict:
"""
Start 主流程:
1) 启动自动模式;
2) 发送上样请求并等待允许;
3) 等待指定分钟后发送样品准备完成(携带采集参数);
4) 周期性轮询采集数据与工位状态;
5) 一旦任一下样位变为 True执行下样流程GET_SAMPLE_DOWN + SEND_SAMPLE_DOWN_READY
Args:
sample_id: 样品名称
start_theta: 起始角度≥5°
end_theta: 结束角度≥5.5°,且必须大于 start_theta
increment: 角度增量≥0.005
exp_time: 曝光时间0.1-5.0 秒)
wait_minutes: 在允许上样后、发送样品准备完成前的等待分钟数(默认 3 分钟)
string: 字符串格式的参数输入,如果提供则优先解析使用
Returns:
dict: {"return_info": str, "success": bool}
"""
try:
# 强制类型转换:除 sample_id 外的所有输入均转换为 float若为字符串
def _to_float(v, default):
try:
return float(v)
except (TypeError, ValueError):
return float(default)
if not isinstance(sample_id, str):
sample_id = str(sample_id)
if isinstance(start_theta, str):
start_theta = _to_float(start_theta, 10.0)
if isinstance(end_theta, str):
end_theta = _to_float(end_theta, 80.0)
if isinstance(increment, str):
increment = _to_float(increment, 0.05)
if isinstance(exp_time, str):
exp_time = _to_float(exp_time, 0.1)
if isinstance(wait_minutes, str):
wait_minutes = _to_float(wait_minutes, 3.0)
# 不再从 string 参数解析覆盖;保留参数但忽略字符串解析,统一使用结构化输入
# 确保设备连接
if not self.sock:
try:
self.connect()
if self._ros_node:
self._ros_node.lab_logger().info("XRD D7-Mate设备连接成功开始执行start流程")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"XRD D7-Mate设备连接失败: {e}")
return {"return_info": f"设备连接失败: {str(e)}", "success": False}
# 1) 启动自动模式
r_auto = self.start_auto_mode(True)
if not r_auto.get("status", False):
return {"return_info": f"启动自动模式失败: {r_auto.get('message', '未知')}", "success": False}
if self._ros_node:
self._ros_node.lab_logger().info(f"自动模式已启动: {r_auto}")
# 2) 上样请求
r_req = self.get_sample_request()
if not r_req.get("status", False):
return {"return_info": f"上样请求未允许: {r_req.get('message', '未知')}", "success": False}
if self._ros_node:
self._ros_node.lab_logger().info(f"上样已允许: {r_req}")
# 3) 等待指定分钟后发送样品准备完成
wait_seconds = max(0.0, float(wait_minutes)) * 60.0
if self._ros_node:
self._ros_node.lab_logger().info(f"等待 {wait_minutes} 分钟后发送样品准备完成")
time.sleep(wait_seconds)
r_ready = self.send_sample_ready(sample_id=sample_id,
start_theta=start_theta,
end_theta=end_theta,
increment=increment,
exp_time=exp_time)
if not r_ready.get("status", False):
return {"return_info": f"样品准备完成失败: {r_ready.get('message', '未知')}", "success": False}
if self._ros_node:
self._ros_node.lab_logger().info(f"样品准备完成已发送: {r_ready}")
# 4) 轮询采集数据与工位状态
polling_interval = 5.0 # 秒
down_station_idx: Optional[int] = None
while True:
try:
r_data = self.get_current_acquire_data()
if self._ros_node:
self._ros_node.lab_logger().info(f"采集中数据: {r_data}")
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"获取采集数据失败: {e}")
try:
r_status = self.get_sample_status()
if self._ros_node:
self._ros_node.lab_logger().info(f"工位状态: {r_status}")
station = r_status.get("Station", {})
if isinstance(station, dict):
for idx in (1, 2, 3):
key = f"DownStation{idx}"
val = station.get(key)
if isinstance(val, bool) and val:
down_station_idx = idx
break
if down_station_idx is not None:
break
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().warning(f"获取工位状态失败: {e}")
time.sleep(polling_interval)
if down_station_idx is None:
return {"return_info": "未检测到任一下样位 True流程未完成", "success": False}
# 5) 下样流程
r_down = self.get_sample_down(down_station_idx)
if not r_down.get("status", False):
return {"return_info": f"下样请求失败(工位 {down_station_idx}): {r_down.get('message', '未知')}", "success": False}
if self._ros_node:
self._ros_node.lab_logger().info(f"下样请求成功(工位 {down_station_idx}): {r_down}")
r_ready_down = self.send_sample_down_ready()
if not r_ready_down.get("status", False):
return {"return_info": f"下样完成发送失败: {r_ready_down.get('message', '未知')}", "success": False}
if self._ros_node:
self._ros_node.lab_logger().info(f"下样完成已发送: {r_ready_down}")
return {"return_info": f"Start流程完成工位 {down_station_idx} 已下样", "success": True}
except Exception as e:
if self._ros_node:
self._ros_node.lab_logger().error(f"Start流程异常: {e}")
return {"return_info": f"Start流程异常: {str(e)}", "success": False}
def _parse_start_params(self, params: Union[str, Dict[str, Any]]) -> Dict[str, Any]:
"""
解析UI输入参数为 Start 流程参数。
- 从UI字典中读取各个字段的字符串值
- 将数值字段从字符串转换为 float 类型
- 保留 sample_id 为字符串类型
返回:
dict: {sample_id, start_theta, end_theta, increment, exp_time, wait_minutes}
"""
# 如果传入为字典,则直接按键读取;否则给出警告并使用空字典
if isinstance(params, dict):
p = params
else:
p = {}
if self._ros_node:
self._ros_node.lab_logger().warning("start 参数应为结构化字典")
def _to_float(v, default):
"""将UI输入的字符串值转换为float处理空值和无效值"""
if v is None or v == '':
return float(default)
try:
# 处理字符串输入来自UI
if isinstance(v, str):
v = v.strip()
if v == '':
return float(default)
return float(v)
except (TypeError, ValueError):
return float(default)
# 从UI输入字典中读取参数
sample_id = p.get('sample_id') or p.get('sample_name') or '样品名称'
if not isinstance(sample_id, str):
sample_id = str(sample_id)
# 将UI字符串输入转换为float
result: Dict[str, Any] = {
'sample_id': sample_id,
'start_theta': _to_float(p.get('start_theta'), 10.0),
'end_theta': _to_float(p.get('end_theta'), 80.0),
'increment': _to_float(p.get('increment'), 0.05),
'exp_time': _to_float(p.get('exp_time'), 0.1),
'wait_minutes': _to_float(p.get('wait_minutes'), 3.0),
}
return result
def start_from_string(self, params: Union[str, Dict[str, Any]]) -> dict:
"""
从UI输入参数执行 Start 主流程。
接收来自用户界面的参数字典,其中数值字段为字符串格式,自动转换为正确的类型。
参数:
params: UI输入参数字典例如:
{
'sample_id': 'teste',
'start_theta': '10.0', # UI字符串输入
'end_theta': '25.0', # UI字符串输入
'increment': '0.05', # UI字符串输入
'exp_time': '0.10', # UI字符串输入
'wait_minutes': '0.5' # UI字符串输入
}
返回:
dict: 执行结果
"""
parsed = self._parse_start_params(params)
sample_id = parsed.get('sample_id', '样品名称')
start_theta = float(parsed.get('start_theta', 10.0))
end_theta = float(parsed.get('end_theta', 80.0))
increment = float(parsed.get('increment', 0.05))
exp_time = float(parsed.get('exp_time', 0.1))
wait_minutes = float(parsed.get('wait_minutes', 3.0))
return self.start(
sample_id=sample_id,
start_theta=start_theta,
end_theta=end_theta,
increment=increment,
exp_time=exp_time,
wait_minutes=wait_minutes,
)
# 测试函数
def test_xrd_client():
"""
测试XRD客户端功能
"""
client = XRDClient(host='127.0.0.1', port=6001)
try:
# 测试连接
client.connect()
print("连接成功")
# 测试启动自动模式
result = client.start_auto_mode(True)
print(f"启动自动模式: {result}")
# 测试上样请求
result = client.get_sample_request()
print(f"上样请求: {result}")
# 测试获取样品状态
result = client.get_sample_status()
print(f"样品状态: {result}")
# 测试高压开启
result = client.set_power_on()
print(f"高压开启: {result}")
except Exception as e:
print(f"测试失败: {e}")
finally:
client.close()
if __name__ == "__main__":
test_xrd_client()
# 为了兼容性,提供别名
XRD_D7Mate = XRDClient