mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 05:45:10 +00:00
* Update README and MQTTClient for installation instructions and code improvements * feat: 支持local_config启动 add: 增加对crt path的说明,为传入config.py的相对路径 move: web component * add: registry description * add 3d visualization * 完成在main中启动设备可视化 完成在main中启动设备可视化,并输出物料ID:mesh的对应关系resource_model 添加物料模型管理类,遍历物料与resource_model,完成TF数据收集 * 完成TF发布 * 修改模型方向,在yaml中添加变换属性 * 添加物料tf变化时,发送topic到前端 另外修改了物料初始化的方法,防止在tf还未发布时提前建立物料模型与发布话题 * 添加关节发布节点与物料可视化节点进入unilab * 使用json启动plr与3D模型仿真 * feat: node_info_update srv fix: OTDeck cant create * close #12 feat: slave node registry * feat: show machine name fix: host node registry not uploaded * feat: add hplc registry * feat: add hplc registry * fix: hplc status typo * fix: devices/ * 完成启动OT并联动rviz * add 3d visualization * 完成在main中启动设备可视化 完成在main中启动设备可视化,并输出物料ID:mesh的对应关系resource_model 添加物料模型管理类,遍历物料与resource_model,完成TF数据收集 * 完成TF发布 * 修改模型方向,在yaml中添加变换属性 * 添加物料tf变化时,发送topic到前端 另外修改了物料初始化的方法,防止在tf还未发布时提前建立物料模型与发布话题 * 添加关节发布节点与物料可视化节点进入unilab * 使用json启动plr与3D模型仿真 * 完成启动OT并联动rviz * fix: device.class possible null * fix: HPLC additions with online service * fix: slave mode spin not working * fix: slave mode spin not working * 修复rviz位置问题, 修复rviz位置问题, 在无tf变动时减缓发送频率 在backend中添加物料跟随方法 * feat: 多ProtocolNode 允许子设备ID相同 feat: 上报发现的ActionClient feat: Host重启动,通过discover机制要求slaveNode重新注册,实现信息及时上报 * feat: 支持env设置config * fix: running logic * fix: running logic * fix: missing ot * 在main中直接初始化republisher和物料的mesh节点 * 将joint_republisher和resource_mesh_manager添加进 main_slave_run.py中 * Device visualization (#14) * add 3d visualization * 完成在main中启动设备可视化 完成在main中启动设备可视化,并输出物料ID:mesh的对应关系resource_model 添加物料模型管理类,遍历物料与resource_model,完成TF数据收集 * 完成TF发布 * 修改模型方向,在yaml中添加变换属性 * 添加物料tf变化时,发送topic到前端 另外修改了物料初始化的方法,防止在tf还未发布时提前建立物料模型与发布话题 * 添加关节发布节点与物料可视化节点进入unilab * 使用json启动plr与3D模型仿真 * 完成启动OT并联动rviz * add 3d visualization * 完成在main中启动设备可视化 完成在main中启动设备可视化,并输出物料ID:mesh的对应关系resource_model 添加物料模型管理类,遍历物料与resource_model,完成TF数据收集 * 完成TF发布 * 修改模型方向,在yaml中添加变换属性 * 添加物料tf变化时,发送topic到前端 另外修改了物料初始化的方法,防止在tf还未发布时提前建立物料模型与发布话题 * 添加关节发布节点与物料可视化节点进入unilab * 使用json启动plr与3D模型仿真 * 完成启动OT并联动rviz * 修复rviz位置问题, 修复rviz位置问题, 在无tf变动时减缓发送频率 在backend中添加物料跟随方法 * fix: running logic * fix: running logic * fix: missing ot * 在main中直接初始化republisher和物料的mesh节点 * 将joint_republisher和resource_mesh_manager添加进 main_slave_run.py中 --------- Co-authored-by: zhangshixiang <@zhangshixiang> Co-authored-by: wznln <18435084+Xuwznln@users.noreply.github.com> * fix: missing hostname in devices_names fix: upload_file for model file * fix: missing paho-mqtt package bump version to 0.9.0 * fix startup add ResourceCreateFromOuter.action * fix type hint * update actions * update actions * host node add_resource_from_outer fix cmake list * pass device config to device class * add: bind_parent_ids to resource create action fix: message convert string * fix: host node should not be re_discovered * feat: resource tracker support dict * feat: add more necessary params * feat: fix boolean null in registry action data * feat: add outer resource * 编写mesh添加action * feat: append resource * add action * feat: vis 2d for plr * fix * fix: browser on rviz * fix: cloud bridge error fallback to local * fix: salve auto run rviz * 初始化两个plate * Device visualization (#22) * add 3d visualization * 完成在main中启动设备可视化 完成在main中启动设备可视化,并输出物料ID:mesh的对应关系resource_model 添加物料模型管理类,遍历物料与resource_model,完成TF数据收集 * 完成TF发布 * 修改模型方向,在yaml中添加变换属性 * 添加物料tf变化时,发送topic到前端 另外修改了物料初始化的方法,防止在tf还未发布时提前建立物料模型与发布话题 * 添加关节发布节点与物料可视化节点进入unilab * 使用json启动plr与3D模型仿真 * 完成启动OT并联动rviz * add 3d visualization * 完成在main中启动设备可视化 完成在main中启动设备可视化,并输出物料ID:mesh的对应关系resource_model 添加物料模型管理类,遍历物料与resource_model,完成TF数据收集 * 完成TF发布 * 修改模型方向,在yaml中添加变换属性 * 添加物料tf变化时,发送topic到前端 另外修改了物料初始化的方法,防止在tf还未发布时提前建立物料模型与发布话题 * 添加关节发布节点与物料可视化节点进入unilab * 使用json启动plr与3D模型仿真 * 完成启动OT并联动rviz * 修复rviz位置问题, 修复rviz位置问题, 在无tf变动时减缓发送频率 在backend中添加物料跟随方法 * fix: running logic * fix: running logic * fix: missing ot * 在main中直接初始化republisher和物料的mesh节点 * 将joint_republisher和resource_mesh_manager添加进 main_slave_run.py中 * 编写mesh添加action * add action * fix * fix: browser on rviz * fix: cloud bridge error fallback to local * fix: salve auto run rviz * 初始化两个plate --------- Co-authored-by: zhangshixiang <@zhangshixiang> Co-authored-by: wznln <18435084+Xuwznln@users.noreply.github.com> * fix: multi channel * fix: aspirate * fix: aspirate * fix: aspirate * fix: aspirate * 提交 * fix: jobadd * fix: jobadd * fix: msg converter * tijiao * add resource creat easy action * identify debug msg * mq client id * unify liquid_handler definition * Update virtual_device.yaml * 更正了stir和heater的连接方式 * 区分了虚拟仪器中的八通阀和电磁阀,添加了两个阀门的驱动 * 修改了add protocol * 修复了阀门更新版的bug * 修复了添加protocol前缀导致的不能启动的bug * Fix handles * bump version to 0.9.6 * add resource edge upload * update container registry and handles * add virtual_separator virtual_rotavap fix transfer_pump * fix container value add parent_name to edge device id * 大图的问题都修复好了,添加了gassource和vacuum pump的驱动以及注册表 * default resource upload mode is false * 添加了icon的文件名在注册表里面 * 修改了json图中link的格式 * fix resource and edge upload * fix device ports * Fix edge id * 移除device的父节点关联 * separate registry sync and resource_add * 默认不进行注册表报送,通过命令unilabos-register或者增加启动参数 * 完善tip * protocol node不再嵌套显示 * bump version to 0.9.7 新增一个测试PumpTransferProtocol的teststation,亲测可以运行,将八通阀们和转移泵与pump_protocol适配 * protocol node 执行action不应携带自身device id * 添加了一套简易双八通阀工作站JSON,亲测能跑 * 修复了很多protocol,亲测能跑 * 添加了run column和filter through的protocol,亲测能跑 * fix mock_reactor * 修改了大图和小图的json,但是在前端上没看到改变 --------- Co-authored-by: Harvey Que <Q-Query@outlook.com> Co-authored-by: wznln <18435084+Xuwznln@users.noreply.github.com> Co-authored-by: zhangshixiang <@zhangshixiang> Co-authored-by: q434343 <73513873+q434343@users.noreply.github.com> Co-authored-by: Junhan Chang <changjh@pku.edu.cn>
185 lines
6.6 KiB
Python
185 lines
6.6 KiB
Python
import asyncio
|
||
import logging
|
||
from typing import Dict, Any, Optional
|
||
|
||
|
||
class VirtualSeparator:
|
||
"""Virtual separator device for SeparateProtocol testing"""
|
||
|
||
def __init__(self, device_id: Optional[str] = None, config: Optional[Dict[str, Any]] = None, **kwargs):
|
||
# 处理可能的不同调用方式
|
||
if device_id is None and "id" in kwargs:
|
||
device_id = kwargs.pop("id")
|
||
if config is None and "config" in kwargs:
|
||
config = kwargs.pop("config")
|
||
|
||
# 设置默认值
|
||
self.device_id = device_id or "unknown_separator"
|
||
self.config = config or {}
|
||
|
||
self.logger = logging.getLogger(f"VirtualSeparator.{self.device_id}")
|
||
self.data = {}
|
||
|
||
# 添加调试信息
|
||
print(f"=== VirtualSeparator {self.device_id} is being created! ===")
|
||
print(f"=== Config: {self.config} ===")
|
||
print(f"=== Kwargs: {kwargs} ===")
|
||
|
||
# 从config或kwargs中获取配置参数
|
||
self.port = self.config.get("port") or kwargs.get("port", "VIRTUAL")
|
||
self._volume = self.config.get("volume") or kwargs.get("volume", 250.0)
|
||
self._has_phases = self.config.get("has_phases") or kwargs.get("has_phases", True)
|
||
|
||
# 处理其他kwargs参数,但跳过已知的配置参数
|
||
skip_keys = {"port", "volume", "has_phases"}
|
||
for key, value in kwargs.items():
|
||
if key not in skip_keys and not hasattr(self, key):
|
||
setattr(self, key, value)
|
||
|
||
async def initialize(self) -> bool:
|
||
"""Initialize virtual separator"""
|
||
print(f"=== VirtualSeparator {self.device_id} initialize() called! ===")
|
||
self.logger.info(f"Initializing virtual separator {self.device_id}")
|
||
self.data.update(
|
||
{
|
||
"status": "Ready",
|
||
"separator_state": "Ready",
|
||
"volume": self._volume,
|
||
"has_phases": self._has_phases,
|
||
"phase_separation": False,
|
||
"stir_speed": 0.0,
|
||
"settling_time": 0.0,
|
||
"progress": 0.0,
|
||
"message": "",
|
||
}
|
||
)
|
||
return True
|
||
|
||
async def cleanup(self) -> bool:
|
||
"""Cleanup virtual separator"""
|
||
self.logger.info(f"Cleaning up virtual separator {self.device_id}")
|
||
return True
|
||
|
||
async def separate(
|
||
self,
|
||
purpose: str,
|
||
product_phase: str,
|
||
from_vessel: str,
|
||
separation_vessel: str,
|
||
to_vessel: str,
|
||
waste_phase_to_vessel: str = "",
|
||
solvent: str = "",
|
||
solvent_volume: float = 50.0,
|
||
through: str = "",
|
||
repeats: int = 1,
|
||
stir_time: float = 30.0,
|
||
stir_speed: float = 300.0,
|
||
settling_time: float = 300.0,
|
||
) -> bool:
|
||
"""Execute separate action - matches Separate action"""
|
||
self.logger.info(f"Separate: purpose={purpose}, product_phase={product_phase}, from_vessel={from_vessel}")
|
||
|
||
# 验证参数
|
||
if product_phase not in ["top", "bottom"]:
|
||
self.logger.error(f"Invalid product_phase {product_phase}, must be 'top' or 'bottom'")
|
||
self.data["message"] = f"产物相位 {product_phase} 无效,必须是 'top' 或 'bottom'"
|
||
return False
|
||
|
||
if purpose not in ["wash", "extract"]:
|
||
self.logger.error(f"Invalid purpose {purpose}, must be 'wash' or 'extract'")
|
||
self.data["message"] = f"分离目的 {purpose} 无效,必须是 'wash' 或 'extract'"
|
||
return False
|
||
|
||
# 开始分离
|
||
self.data.update(
|
||
{
|
||
"status": "Running",
|
||
"separator_state": "Separating",
|
||
"purpose": purpose,
|
||
"product_phase": product_phase,
|
||
"from_vessel": from_vessel,
|
||
"separation_vessel": separation_vessel,
|
||
"to_vessel": to_vessel,
|
||
"waste_phase_to_vessel": waste_phase_to_vessel,
|
||
"solvent": solvent,
|
||
"solvent_volume": solvent_volume,
|
||
"repeats": repeats,
|
||
"stir_speed": stir_speed,
|
||
"settling_time": settling_time,
|
||
"phase_separation": True,
|
||
"progress": 0.0,
|
||
"message": f"正在分离: {from_vessel} -> {to_vessel}",
|
||
}
|
||
)
|
||
|
||
# 模拟分离过程
|
||
total_time = (stir_time + settling_time) * repeats
|
||
simulation_time = min(total_time / 60.0, 15.0) # 最多模拟15秒
|
||
|
||
for repeat in range(repeats):
|
||
# 搅拌阶段
|
||
for progress in range(0, 51, 10):
|
||
await asyncio.sleep(simulation_time / (repeats * 10))
|
||
overall_progress = ((repeat * 100) + (progress * 0.5)) / repeats
|
||
self.data["progress"] = overall_progress
|
||
self.data["message"] = f"第{repeat+1}次分离 - 搅拌中 ({progress}%)"
|
||
|
||
# 静置分相阶段
|
||
for progress in range(50, 101, 10):
|
||
await asyncio.sleep(simulation_time / (repeats * 10))
|
||
overall_progress = ((repeat * 100) + (progress * 0.5)) / repeats
|
||
self.data["progress"] = overall_progress
|
||
self.data["message"] = f"第{repeat+1}次分离 - 静置分相中 ({progress}%)"
|
||
|
||
# 分离完成
|
||
self.data.update(
|
||
{
|
||
"status": "Ready",
|
||
"separator_state": "Ready",
|
||
"phase_separation": False,
|
||
"stir_speed": 0.0,
|
||
"progress": 100.0,
|
||
"message": f"分离完成: {repeats}次分离操作",
|
||
}
|
||
)
|
||
|
||
self.logger.info(f"Separation completed: {repeats} cycles from {from_vessel} to {to_vessel}")
|
||
return True
|
||
|
||
# 状态属性
|
||
@property
|
||
def status(self) -> str:
|
||
return self.data.get("status", "Unknown")
|
||
|
||
@property
|
||
def separator_state(self) -> str:
|
||
return self.data.get("separator_state", "Unknown")
|
||
|
||
@property
|
||
def volume(self) -> float:
|
||
return self.data.get("volume", self._volume)
|
||
|
||
@property
|
||
def has_phases(self) -> bool:
|
||
return self.data.get("has_phases", self._has_phases)
|
||
|
||
@property
|
||
def phase_separation(self) -> bool:
|
||
return self.data.get("phase_separation", False)
|
||
|
||
@property
|
||
def stir_speed(self) -> float:
|
||
return self.data.get("stir_speed", 0.0)
|
||
|
||
@property
|
||
def settling_time(self) -> float:
|
||
return self.data.get("settling_time", 0.0)
|
||
|
||
@property
|
||
def progress(self) -> float:
|
||
return self.data.get("progress", 0.0)
|
||
|
||
@property
|
||
def message(self) -> str:
|
||
return self.data.get("message", "")
|