mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 13:01:12 +00:00
* add biomek.py demo implementation * 更新LiquidHandlerBiomek类,添加资源创建功能,优化协议创建方法,修复部分代码格式问题,更新YAML配置以支持新功能。 * Test * fix biomek success type * Convert LH action to biomek. * Update biomek.py * 注册表上报handle和schema (param input) * 修复biomek缺少的字段 * delete 's' * Remove warnings * Update biomek.py * Biomek test * Update biomek.py * 新增transfer_biomek的msg * New transfer_biomek * Updated transfer_biomek * 更新transfer_biomek的msg * 更新transfer_biomek的msg * 支持Biomek创建 * new action * fix key name typo * New parameter for biomek to run. * Refine * Update * new actions * new actions * 1 * registry * fix biomek startup add action handles * fix handles not as default entry * biomek_test.py biomek_test.py是最新的版本,运行它会生成complete_biomek_protocol.json * Update biomek.py * biomek_test.py * fix liquid_handler.biomek handles * host node新增resource add时间统计 create_resource新增handle bump version to 0.9.2 * 修正物料上传时间 改用biomek_test 增加ResultInfoEncoder 支持返回结果上传 * 正确发送return_info结果 * 同步执行状态信息 * 取消raiseValueError提示 * Update biomek_test.py * 0608 DONE * 同步了Biomek.py 现在应可用 * biomek switch back to non-test * temp disable initialize resource * 37-biomek-i5i7 (#40) * add biomek.py demo implementation * 更新LiquidHandlerBiomek类,添加资源创建功能,优化协议创建方法,修复部分代码格式问题,更新YAML配置以支持新功能。 * Test * fix biomek success type * Convert LH action to biomek. * Update biomek.py * 注册表上报handle和schema (param input) * 修复biomek缺少的字段 * delete 's' * Remove warnings * Update biomek.py * Biomek test * Update biomek.py * 新增transfer_biomek的msg * New transfer_biomek * Updated transfer_biomek * 更新transfer_biomek的msg * 更新transfer_biomek的msg * 支持Biomek创建 * new action * fix key name typo * New parameter for biomek to run. * Refine * Update * new actions * new actions * 1 * registry * fix biomek startup add action handles * fix handles not as default entry * biomek_test.py biomek_test.py是最新的版本,运行它会生成complete_biomek_protocol.json * Update biomek.py * biomek_test.py * fix liquid_handler.biomek handles * host node新增resource add时间统计 create_resource新增handle bump version to 0.9.2 * 修正物料上传时间 改用biomek_test 增加ResultInfoEncoder 支持返回结果上传 * 正确发送return_info结果 * 同步执行状态信息 * 取消raiseValueError提示 * Update biomek_test.py * 0608 DONE * 同步了Biomek.py 现在应可用 * biomek switch back to non-test * temp disable initialize resource * Refine biomek * Refine copy issue * Refine --------- Co-authored-by: Junhan Chang <changjh@pku.edu.cn> Co-authored-by: Guangxin Zhang <guangxin.zhang.bio@gmail.com> Co-authored-by: qxw138 <qxw@stu.pku.edu.cn> * Device visualization (#39) * 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 * 提取lh的joint发布 * unify liquid_handler definition * 修改物料跟随与物料添加逻辑 修改物料跟随与物料添加逻辑 将joint_publisher类移出lh的backends,但仍需要对lh的backends进行一些改写 * Revert "修改物料跟随与物料添加逻辑" This reverts commit498c997ad7. * Reapply "修改物料跟随与物料添加逻辑" This reverts commit3a60d2ae81. * Revert "Merge remote-tracking branch 'upstream/dev' into device_visualization" This reverts commitfa727220af, reversing changes made to498c997ad7. * 修改物料放下时的方法,如果选择 修改物料放下时的方法, 如果选择drop_trash,则删除物料显示 如果选择drop,则让其解除连接 * add biomek.py demo implementation * 更新LiquidHandlerBiomek类,添加资源创建功能,优化协议创建方法,修复部分代码格式问题,更新YAML配置以支持新功能。 * Test * fix biomek success type * Convert LH action to biomek. * Update biomek.py * 注册表上报handle和schema (param input) * 修复biomek缺少的字段 * delete 's' * Remove warnings * Update biomek.py * Biomek test * Update biomek.py * 新增transfer_biomek的msg * New transfer_biomek * Updated transfer_biomek * 更新transfer_biomek的msg * 更新transfer_biomek的msg * 支持Biomek创建 * new action * fix key name typo * New parameter for biomek to run. * Refine * Update * new actions * new actions * 1 * registry * fix biomek startup add action handles * fix handles not as default entry * unilab添加moveit启动 1,整合所有moveit节点到一个move_group中,并整合所有的controller依次激活 2,添加pymoveit2的节点,使用json可直接启动 3,修改机械臂规划方式,添加约束,让冗余关节不会进行过多移动 * biomek_test.py biomek_test.py是最新的版本,运行它会生成complete_biomek_protocol.json * Update biomek.py * biomek_test.py * fix liquid_handler.biomek handles * 修改物体attach时,多次赋值当前时间导致卡顿问题, * Revert "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit56d45b94f5. * Reapply "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit07d9db20c3. * 添加缺少物料:"plate_well_G12", * host node新增resource add时间统计 create_resource新增handle bump version to 0.9.2 * 修正物料上传时间 改用biomek_test 增加ResultInfoEncoder 支持返回结果上传 * 正确发送return_info结果 * 同步执行状态信息 * 取消raiseValueError提示 * Update biomek_test.py * 0608 DONE * 同步了Biomek.py 现在应可用 * biomek switch back to non-test * temp disable initialize resource * add * fix tip resource data * liquid states * change to debug level * Revert "change to debug level" This reverts commit5d9953c3e5. * Reapply "change to debug level" This reverts commit2487bb6ffc. * fix tip resource data * add full device * add moveit yaml * 修复moveit 增加post_init阶段,给予ros_node反向 * remove necessary node * fix moveit action client * remove necessary imports * Update moveit_interface.py * fix handler_key uppercase * json add liquids * fix setup * add * change to "sources" and "targets" for lh * bump version * remove parent's parent link --------- 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: Junhan Chang <changjh@pku.edu.cn> Co-authored-by: Guangxin Zhang <guangxin.zhang.bio@gmail.com> Co-authored-by: qxw138 <qxw@stu.pku.edu.cn> * Device visualization (#41) * 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 * 提取lh的joint发布 * unify liquid_handler definition * 修改物料跟随与物料添加逻辑 修改物料跟随与物料添加逻辑 将joint_publisher类移出lh的backends,但仍需要对lh的backends进行一些改写 * Revert "修改物料跟随与物料添加逻辑" This reverts commit498c997ad7. * Reapply "修改物料跟随与物料添加逻辑" This reverts commit3a60d2ae81. * Revert "Merge remote-tracking branch 'upstream/dev' into device_visualization" This reverts commitfa727220af, reversing changes made to498c997ad7. * 修改物料放下时的方法,如果选择 修改物料放下时的方法, 如果选择drop_trash,则删除物料显示 如果选择drop,则让其解除连接 * add biomek.py demo implementation * 更新LiquidHandlerBiomek类,添加资源创建功能,优化协议创建方法,修复部分代码格式问题,更新YAML配置以支持新功能。 * Test * fix biomek success type * Convert LH action to biomek. * Update biomek.py * 注册表上报handle和schema (param input) * 修复biomek缺少的字段 * delete 's' * Remove warnings * Update biomek.py * Biomek test * Update biomek.py * 新增transfer_biomek的msg * New transfer_biomek * Updated transfer_biomek * 更新transfer_biomek的msg * 更新transfer_biomek的msg * 支持Biomek创建 * new action * fix key name typo * New parameter for biomek to run. * Refine * Update * new actions * new actions * 1 * registry * fix biomek startup add action handles * fix handles not as default entry * unilab添加moveit启动 1,整合所有moveit节点到一个move_group中,并整合所有的controller依次激活 2,添加pymoveit2的节点,使用json可直接启动 3,修改机械臂规划方式,添加约束,让冗余关节不会进行过多移动 * biomek_test.py biomek_test.py是最新的版本,运行它会生成complete_biomek_protocol.json * Update biomek.py * biomek_test.py * fix liquid_handler.biomek handles * 修改物体attach时,多次赋值当前时间导致卡顿问题, * Revert "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit56d45b94f5. * Reapply "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit07d9db20c3. * 添加缺少物料:"plate_well_G12", * host node新增resource add时间统计 create_resource新增handle bump version to 0.9.2 * 修正物料上传时间 改用biomek_test 增加ResultInfoEncoder 支持返回结果上传 * 正确发送return_info结果 * 同步执行状态信息 * 取消raiseValueError提示 * Update biomek_test.py * 0608 DONE * 同步了Biomek.py 现在应可用 * biomek switch back to non-test * temp disable initialize resource * add * fix tip resource data * liquid states * change to debug level * Revert "change to debug level" This reverts commit5d9953c3e5. * Reapply "change to debug level" This reverts commit2487bb6ffc. * fix tip resource data * add full device * add moveit yaml * 修复moveit 增加post_init阶段,给予ros_node反向 * remove necessary node * fix moveit action client * remove necessary imports * Update moveit_interface.py * fix handler_key uppercase * json add liquids * fix setup * add * change to "sources" and "targets" for lh * bump version * remove parent's parent link * change arm's name * change name --------- Co-authored-by: Harvey Que <Q-Query@outlook.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> Co-authored-by: Guangxin Zhang <guangxin.zhang.bio@gmail.com> Co-authored-by: qxw138 <qxw@stu.pku.edu.cn> * fix move it * fix move it * create_resource * bump ver modify slot type * 增加modbus支持 调整protocol node以更好支持多种类型的read和write * 调整protocol node以更好支持多种类型的read和write * 补充日志 * Device visualization (#42) * 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 * 提取lh的joint发布 * unify liquid_handler definition * 修改物料跟随与物料添加逻辑 修改物料跟随与物料添加逻辑 将joint_publisher类移出lh的backends,但仍需要对lh的backends进行一些改写 * Revert "修改物料跟随与物料添加逻辑" This reverts commit498c997ad7. * Reapply "修改物料跟随与物料添加逻辑" This reverts commit3a60d2ae81. * Revert "Merge remote-tracking branch 'upstream/dev' into device_visualization" This reverts commitfa727220af, reversing changes made to498c997ad7. * 修改物料放下时的方法,如果选择 修改物料放下时的方法, 如果选择drop_trash,则删除物料显示 如果选择drop,则让其解除连接 * unilab添加moveit启动 1,整合所有moveit节点到一个move_group中,并整合所有的controller依次激活 2,添加pymoveit2的节点,使用json可直接启动 3,修改机械臂规划方式,添加约束,让冗余关节不会进行过多移动 * 修改物体attach时,多次赋值当前时间导致卡顿问题, * Revert "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit56d45b94f5. * Reapply "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit07d9db20c3. * 添加缺少物料:"plate_well_G12", * add * fix tip resource data * liquid states * change to debug level * Revert "change to debug level" This reverts commit5d9953c3e5. * Reapply "change to debug level" This reverts commit2487bb6ffc. * fix tip resource data * add full device * add moveit yaml * 修复moveit 增加post_init阶段,给予ros_node反向 * remove necessary node * fix moveit action client * remove necessary imports * Update moveit_interface.py * fix handler_key uppercase * json add liquids * fix setup * add * change to "sources" and "targets" for lh * bump version * remove parent's parent link * change arm's name * change name * fix ik error --------- Co-authored-by: Harvey Que <Q-Query@outlook.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> * Add Mock Device for Organic Synthesis\添加有机合成的虚拟仪器和Protocol (#43) * Add Device MockChiller Add device MockChiller * Add Device MockFilter * Add Device MockPump * Add Device MockRotavap * Add Device MockSeparator * Add Device MockStirrer * Add Device MockHeater * Add Device MockVacuum * Add Device MockSolenoidValve * Add Device Mock \_init_.py * 规范模拟设备代码与注册表信息 * 更改Mock大写文件夹名 * 删除大写目录 * Edited Mock device json * Match mock device with action * Edit mock device yaml * Add new action * Add Virtual Device, Action, YAML, Protocol for Organic Syn * 单独分类测试的protocol文件夹 * 更名Action --------- Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com> * bump version & protocol fix * hotfix: Add macos_sdk_config (#46) Co-authored-by: quehh <scienceol@outlook.com> * include device_mesh when pip install * 测试自动构建 * try build fix * try build * test artifacts * hotfix: Add .certs in .gitignore * create container * container 添加和更新完成 * Device registry port (#49) * 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> * 更新workstation注册表 * 添加了两个protocol的检索功能 (#51) * 添加了两个protocol的检索liquid type功能 * fix workstation registry * 修复了没连接的几个仪器的link,添加了container的icon * 修改了json和注册表,现在大图全部的device都链接上了 * 修复了小图的json图,线全部连上了 * add work_station protocol handles (ports) * fix workstation action handle --------- Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com> Co-authored-by: Junhan Chang <changjh@dp.tech> * 新增注册表补全功能,修复Protocol执行失败 * 支持通过导入方式补全注册表,新增工作流unilabos_device_id字段 * 修复不启用注册表补充就无法启动的bug * 修复部分识别error * 修复静态方法识别get status,注册表支持python类型 * status types对于嵌套类型返回的对象,暂时处理成字符串,无法直接进行转换 * 支持通过list[int],list[float]进行Int64MultiArray,Float64MultiArray的替换 * 成功动态导入的不再需要使用静态导入 * Fix handle names (#55) * fix handle names * improve evacuateAndRefill gas source finding * add camera and dependency (#56) * 修复auto-的Action在protocol node下错误注册 * 匹配init param schema格式 * Add channel_sources config in conda_build_config.yaml (#58) * 修复任务执行传参 * Create 5 new protocols & bump version 0.9.8 (#59) * 添加了5个缺失的protocol,验证了可以运行 * bump version to 0.9.8 * 修复新增的Action的字段缺失 --------- Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com> * 转换到ros消息时,要进行基础类型转换 * Update work_station.yaml (#60) * Update work_station.yaml * Checklist里面有XDL跟protocol之间没对齐的问题,工作量有点大找时间写完 * Create prcxi.py * Update prcxi.py * Update Prcxi * 更新中析仪器,以及启动示例 * 修改moveit_interface,并在mqtt上报时发送一个时间戳,方便网页端对数据的筛选 (#62) * 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 * 提取lh的joint发布 * unify liquid_handler definition * 修改物料跟随与物料添加逻辑 修改物料跟随与物料添加逻辑 将joint_publisher类移出lh的backends,但仍需要对lh的backends进行一些改写 * Revert "修改物料跟随与物料添加逻辑" This reverts commit498c997ad7. * Reapply "修改物料跟随与物料添加逻辑" This reverts commit3a60d2ae81. * Revert "Merge remote-tracking branch 'upstream/dev' into device_visualization" This reverts commitfa727220af, reversing changes made to498c997ad7. * 修改物料放下时的方法,如果选择 修改物料放下时的方法, 如果选择drop_trash,则删除物料显示 如果选择drop,则让其解除连接 * unilab添加moveit启动 1,整合所有moveit节点到一个move_group中,并整合所有的controller依次激活 2,添加pymoveit2的节点,使用json可直接启动 3,修改机械臂规划方式,添加约束,让冗余关节不会进行过多移动 * 修改物体attach时,多次赋值当前时间导致卡顿问题, * Revert "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit56d45b94f5. * Reapply "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit07d9db20c3. * 添加缺少物料:"plate_well_G12", * add * fix tip resource data * liquid states * change to debug level * Revert "change to debug level" This reverts commit5d9953c3e5. * Reapply "change to debug level" This reverts commit2487bb6ffc. * fix tip resource data * add full device * add moveit yaml * 修复moveit 增加post_init阶段,给予ros_node反向 * remove necessary node * fix moveit action client * remove necessary imports * Update moveit_interface.py * fix handler_key uppercase * json add liquids * fix setup * add * change to "sources" and "targets" for lh * bump version * remove parent's parent link * change arm's name * change name * fix ik error * 修改moveit_interface,并在mqtt上报时发送一个时间戳 --------- 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: Junhan Chang <changjh@pku.edu.cn> * 更新实例 * 更新实例 * 更新实例 * 修正prcxi启动 * 更新PRCXI配置,修改主机地址和设置状态,并添加示例用法 * add pickup tips for prcxi * 任意执行错误都应该返回failed * 任意执行错误都应该返回failed * Add plateT6 to PRCXI configuration and enhance error handling in liquid handling * prcxi blending * assert blending_times > 0 * update prcxi * update prcxi registry * Update prcxi.py to fit the function in unilabos. * 不生成已配置action的动作,增加prcxi的debug模式 * 增加注册表版本参数,支持将auto-指令人工检查后非auto,不生成人工已检查的指令,取消不必要的description生成 * 增加注册表版本参数,支持将auto-指令人工检查后非auto,不生成人工已检查的指令,取消不必要的description生成 * Update prcxi.py * 修复了部分的protocol因为XDL更新导致的问题 (#61) * 修复了部分的protocol因为XDL更新导致的问题 但是pumptransfer,add,dissolve,separate还没修,后续还需要写virtual固体加料器 * 补充了四个action * 添加了固体加样器,丰富了json,修改了add protocol * bump version to 0.9.9 * fix bugs from new actions * protocol完整修复版本& bump version to 0.9.10 * 修补了一些单位处理,bump version to 0.9.11 * 优化了全protocol的运行时间,除了pumptransfer相关的还没 * 补充了剩下的几个protocol --------- Co-authored-by: Junhan Chang <changjh@dp.tech> Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com> * 修复action移除时的报错,更新注册表 * Update prcxi.py * Update prcxi.py * 新增simulator * Update prcxi.py * Update trash * Update prcxi.py * Update prcxi.py * Update for discard tips * Update prcxi.py * Update PRCXI * 更新axis等参数 * Update 9320 * get_well_container&get_tip_rack * update * Update 9320 * update * deck * 更新注册表&增加资源,parent应为resources字段 * Update 9320 * update * 新增set liquid方法 * 新增set liquid方法 * action to resource & 0.9.12 (#64) * action to resource & 0.9.12 * stir和adjustph的中的bug修不好 * modify prcxi * 0.9.12 update registry * update * update * registry upadte * Update * update * container_for_nothing * mix * registry fix * registry fix * registry fix * Update * Update prcxi.py * SET TIP RACK * bump version * update registry version & category * update set tip rack * yaml dump支持ordered dict,支持config_info * fix devices * fix resource check serialize * fix: Protocol node resource run (#65) * stir和adjustph的中的bug修不好 * fix sub-resource query in protocol node compiling * add resource placeholder to vessels * add the rest yaml * Update work_station.yaml --------- Co-authored-by: KCFeng425 <2100011801@stu.pku.edu.cn> * 采用http报送resource * 采用http报送resource * update * Update .gitignore * bump version to 0.10.0 * default param simulator * slim * Update * Update for prcxi * Update * Update * Refactor PRCXI9300Deck initialization and update plate configurations - Changed deck name from "PRCXI_Deck" to "PRCXI_Deck_9300". - Updated plate4 initialization to use get_well_container instead of get_tip_rack. - Modified plate4 material details with new UUID, code, and name. - Renamed output JSON file to "deck_9300_new.json". - Uncommented and adjusted liquid handling operations for clarity and future use. * test * update * Update prcxi_9300.json This one is good * update * fix protocol_node communication transfer * 修复注册表handles类型错误的问题 * 物料添加失败应该直接raise ValueError,不要等待 * 更正注册表中的数字类型 * Delete unnecessary files. * 新增lab_id直接传入 * fix vessel_id param passing in protocols * 新增dll预载,保证部分设备可正常使用unilabos_msgs * 修复可能的web template找不到的问题 新增联网获取json启动 删除非-g传入启动json的方式 兼容传参参数名短横线与下划线 * 修复可能的web template找不到的问题 新增联网获取json启动 删除非-g传入启动json的方式 兼容传参参数名短横线与下划线 更新版本到0.10.1 修复Upload Registry镜像不匹配 * 新增用户引导 * Device visualization (#67) * 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 * 提取lh的joint发布 * unify liquid_handler definition * 修改物料跟随与物料添加逻辑 修改物料跟随与物料添加逻辑 将joint_publisher类移出lh的backends,但仍需要对lh的backends进行一些改写 * Revert "修改物料跟随与物料添加逻辑" This reverts commit498c997ad7. * Reapply "修改物料跟随与物料添加逻辑" This reverts commit3a60d2ae81. * Revert "Merge remote-tracking branch 'upstream/dev' into device_visualization" This reverts commitfa727220af, reversing changes made to498c997ad7. * 修改物料放下时的方法,如果选择 修改物料放下时的方法, 如果选择drop_trash,则删除物料显示 如果选择drop,则让其解除连接 * unilab添加moveit启动 1,整合所有moveit节点到一个move_group中,并整合所有的controller依次激活 2,添加pymoveit2的节点,使用json可直接启动 3,修改机械臂规划方式,添加约束,让冗余关节不会进行过多移动 * 修改物体attach时,多次赋值当前时间导致卡顿问题, * Revert "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit56d45b94f5. * Reapply "修改物体attach时,多次赋值当前时间导致卡顿问题," This reverts commit07d9db20c3. * 添加缺少物料:"plate_well_G12", * add * fix tip resource data * liquid states * change to debug level * Revert "change to debug level" This reverts commit5d9953c3e5. * Reapply "change to debug level" This reverts commit2487bb6ffc. * fix tip resource data * add full device * add moveit yaml * 修复moveit 增加post_init阶段,给予ros_node反向 * remove necessary node * fix moveit action client * remove necessary imports * Update moveit_interface.py * fix handler_key uppercase * json add liquids * fix setup * add * change to "sources" and "targets" for lh * bump version * remove parent's parent link * change arm's name * change name * fix ik error * 修改moveit_interface,并在mqtt上报时发送一个时间戳 * 添加机械臂和移液站 * 添加 * 添加硬件 * update * 添加 --------- 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: Junhan Chang <changjh@pku.edu.cn> * 更新注册表 --------- Co-authored-by: Junhan Chang <changjh@pku.edu.cn> Co-authored-by: Guangxin Zhang <guangxin.zhang.bio@gmail.com> Co-authored-by: qxw138 <qxw@stu.pku.edu.cn> Co-authored-by: q434343 <73513873+q434343@users.noreply.github.com> Co-authored-by: Harvey Que <Q-Query@outlook.com> Co-authored-by: Kongchang Feng <2100011801@stu.pku.edu.cn> Co-authored-by: hh. <103566763+Mile-Away@users.noreply.github.com> Co-authored-by: quehh <scienceol@outlook.com> Co-authored-by: Harvey Que <quehaohui@dp.tech> Co-authored-by: Junhan Chang <changjh@dp.tech> Co-authored-by: ZiWei <131428629+ZiWei09@users.noreply.github.com>
1854 lines
78 KiB
Python
1854 lines
78 KiB
Python
import numpy as np
|
||
import networkx as nx
|
||
import asyncio
|
||
import time as time_module # 🔧 重命名time模块
|
||
from typing import List, Dict, Any
|
||
import logging
|
||
import sys
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
def debug_print(message):
|
||
"""强制输出调试信息"""
|
||
timestamp = time_module.strftime("%H:%M:%S")
|
||
output = f"[{timestamp}] {message}"
|
||
print(output, flush=True)
|
||
sys.stdout.flush()
|
||
# 同时写入日志
|
||
logger.info(output)
|
||
|
||
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
||
"""
|
||
从容器节点的数据中获取液体体积
|
||
"""
|
||
debug_print(f"🔍 开始读取容器 '{vessel}' 的液体体积...")
|
||
|
||
if vessel not in G.nodes():
|
||
logger.error(f"❌ 容器 '{vessel}' 不存在于系统图中")
|
||
debug_print(f" - 系统中的容器: {list(G.nodes())}")
|
||
return 0.0
|
||
|
||
vessel_data = G.nodes[vessel].get('data', {})
|
||
debug_print(f"📋 容器 '{vessel}' 的数据结构: {vessel_data}")
|
||
|
||
total_volume = 0.0
|
||
|
||
# 方法1:检查 'liquid' 字段(列表格式)
|
||
debug_print("🔍 方法1: 检查 'liquid' 字段...")
|
||
if 'liquid' in vessel_data:
|
||
liquids = vessel_data['liquid']
|
||
debug_print(f" - liquid 字段类型: {type(liquids)}")
|
||
debug_print(f" - liquid 字段内容: {liquids}")
|
||
|
||
if isinstance(liquids, list):
|
||
debug_print(f" - liquid 是列表,包含 {len(liquids)} 个元素")
|
||
for i, liquid in enumerate(liquids):
|
||
debug_print(f" 液体 {i+1}: {liquid}")
|
||
if isinstance(liquid, dict):
|
||
volume_keys = ['liquid_volume', 'volume', 'amount', 'quantity']
|
||
for key in volume_keys:
|
||
if key in liquid:
|
||
try:
|
||
vol = float(liquid[key])
|
||
total_volume += vol
|
||
debug_print(f" ✅ 从 '{key}' 读取体积: {vol}mL")
|
||
break
|
||
except (ValueError, TypeError) as e:
|
||
logger.warning(f" ⚠️ 无法转换 '{key}': {liquid[key]} -> {str(e)}")
|
||
continue
|
||
else:
|
||
debug_print(f" - liquid 不是列表: {type(liquids)}")
|
||
else:
|
||
debug_print(" - 没有 'liquid' 字段")
|
||
|
||
# 方法2:检查直接的体积字段
|
||
debug_print("🔍 方法2: 检查直接体积字段...")
|
||
volume_keys = ['total_volume', 'volume', 'liquid_volume', 'amount', 'current_volume']
|
||
for key in volume_keys:
|
||
if key in vessel_data:
|
||
try:
|
||
vol = float(vessel_data[key])
|
||
total_volume = max(total_volume, vol) # 取最大值
|
||
debug_print(f" ✅ 从容器数据 '{key}' 读取体积: {vol}mL")
|
||
break
|
||
except (ValueError, TypeError) as e:
|
||
logger.warning(f" ⚠️ 无法转换 '{key}': {vessel_data[key]} -> {str(e)}")
|
||
continue
|
||
|
||
# 方法3:检查 'state' 或 'status' 字段
|
||
debug_print("🔍 方法3: 检查 'state' 字段...")
|
||
if 'state' in vessel_data and isinstance(vessel_data['state'], dict):
|
||
state = vessel_data['state']
|
||
debug_print(f" - state 字段内容: {state}")
|
||
if 'volume' in state:
|
||
try:
|
||
vol = float(state['volume'])
|
||
total_volume = max(total_volume, vol)
|
||
debug_print(f" ✅ 从容器状态读取体积: {vol}mL")
|
||
except (ValueError, TypeError) as e:
|
||
logger.warning(f" ⚠️ 无法转换 state.volume: {state['volume']} -> {str(e)}")
|
||
else:
|
||
debug_print(" - 没有 'state' 字段或不是字典")
|
||
|
||
debug_print(f"📊 容器 '{vessel}' 最终检测体积: {total_volume}mL")
|
||
return total_volume
|
||
|
||
def is_integrated_pump(node_name):
|
||
return "pump" in node_name and "valve" in node_name
|
||
|
||
|
||
def find_connected_pump(G, valve_node):
|
||
"""
|
||
查找与阀门相连的泵节点 - 修复版本
|
||
🔧 修复:区分电磁阀和多通阀,电磁阀不参与泵查找
|
||
"""
|
||
debug_print(f"🔍 查找与阀门 {valve_node} 相连的泵...")
|
||
|
||
# 🔧 关键修复:检查节点类型,电磁阀不应该查找泵
|
||
node_data = G.nodes.get(valve_node, {})
|
||
node_class = node_data.get("class", "") or ""
|
||
|
||
debug_print(f" - 阀门类型: {node_class}")
|
||
|
||
# 如果是电磁阀,不应该查找泵(电磁阀只是开关)
|
||
if ("solenoid" in node_class.lower() or "solenoid_valve" in valve_node.lower()):
|
||
debug_print(f" ⚠️ {valve_node} 是电磁阀,不应该查找泵节点")
|
||
raise ValueError(f"电磁阀 {valve_node} 不应该参与泵查找逻辑")
|
||
|
||
# 只有多通阀等复杂阀门才需要查找连接的泵
|
||
if ("multiway" in node_class.lower() or "valve" in node_class.lower()):
|
||
debug_print(f" - {valve_node} 是多通阀,查找连接的泵...")
|
||
|
||
# 方法1:直接相邻的泵
|
||
for neighbor in G.neighbors(valve_node):
|
||
neighbor_class = G.nodes[neighbor].get("class", "") or ""
|
||
debug_print(f" - 检查邻居 {neighbor}, class: {neighbor_class}")
|
||
if "pump" in neighbor_class.lower():
|
||
debug_print(f" ✅ 找到直接相连的泵: {neighbor}")
|
||
return neighbor
|
||
|
||
# 方法2:通过路径查找泵(最多2跳)
|
||
debug_print(f" - 未找到直接相连的泵,尝试路径查找...")
|
||
|
||
# 获取所有泵节点
|
||
pump_nodes = []
|
||
for node_id in G.nodes():
|
||
node_class = G.nodes[node_id].get("class", "") or ""
|
||
if "pump" in node_class.lower():
|
||
pump_nodes.append(node_id)
|
||
|
||
debug_print(f" - 系统中的泵节点: {pump_nodes}")
|
||
|
||
# 查找到泵的最短路径
|
||
for pump_node in pump_nodes:
|
||
try:
|
||
if nx.has_path(G, valve_node, pump_node):
|
||
path = nx.shortest_path(G, valve_node, pump_node)
|
||
path_length = len(path) - 1
|
||
debug_print(f" - 到泵 {pump_node} 的路径: {path}, 距离: {path_length}")
|
||
|
||
if path_length <= 2: # 最多允许2跳
|
||
debug_print(f" ✅ 通过路径找到泵: {pump_node}")
|
||
return pump_node
|
||
except nx.NetworkXNoPath:
|
||
continue
|
||
|
||
# 方法3:降级方案 - 返回第一个可用的泵
|
||
if pump_nodes:
|
||
debug_print(f" ⚠️ 未找到连接的泵,使用第一个可用的泵: {pump_nodes[0]}")
|
||
return pump_nodes[0]
|
||
|
||
# 最终失败
|
||
debug_print(f" ❌ 完全找不到泵节点")
|
||
raise ValueError(f"未找到与阀 {valve_node} 相连的泵节点")
|
||
|
||
|
||
def build_pump_valve_maps(G, pump_backbone):
|
||
"""
|
||
构建泵-阀门映射 - 修复版本
|
||
🔧 修复:过滤掉电磁阀,只处理需要泵的多通阀
|
||
"""
|
||
pumps_from_node = {}
|
||
valve_from_node = {}
|
||
|
||
debug_print(f"🔧 构建泵-阀门映射,原始骨架: {pump_backbone}")
|
||
|
||
# 🔧 关键修复:过滤掉电磁阀
|
||
filtered_backbone = []
|
||
for node in pump_backbone:
|
||
node_data = G.nodes.get(node, {})
|
||
node_class = node_data.get("class", "") or ""
|
||
|
||
# 跳过电磁阀
|
||
if ("solenoid" in node_class.lower() or "solenoid_valve" in node.lower()):
|
||
debug_print(f" - 跳过电磁阀: {node}")
|
||
continue
|
||
|
||
filtered_backbone.append(node)
|
||
|
||
debug_print(f"🔧 过滤后的骨架: {filtered_backbone}")
|
||
|
||
for node in filtered_backbone:
|
||
if is_integrated_pump(node):
|
||
pumps_from_node[node] = node
|
||
valve_from_node[node] = node
|
||
debug_print(f" - 集成泵-阀: {node}")
|
||
else:
|
||
try:
|
||
pump_node = find_connected_pump(G, node)
|
||
pumps_from_node[node] = pump_node
|
||
valve_from_node[node] = node
|
||
debug_print(f" - 阀门 {node} -> 泵 {pump_node}")
|
||
except ValueError as e:
|
||
debug_print(f" - 跳过节点 {node}: {str(e)}")
|
||
continue
|
||
|
||
debug_print(f"🔧 最终映射: pumps={pumps_from_node}, valves={valve_from_node}")
|
||
return pumps_from_node, valve_from_node
|
||
|
||
|
||
def generate_pump_protocol(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
生成泵操作的动作序列 - 修复版本
|
||
🔧 修复:正确处理包含电磁阀的路径
|
||
"""
|
||
pump_action_sequence = []
|
||
nodes = G.nodes(data=True)
|
||
|
||
# 验证输入参数
|
||
if volume <= 0:
|
||
logger.error(f"无效的体积参数: {volume}mL")
|
||
return pump_action_sequence
|
||
|
||
if flowrate <= 0:
|
||
flowrate = 2.5
|
||
logger.warning(f"flowrate <= 0,使用默认值 {flowrate}mL/s")
|
||
|
||
if transfer_flowrate <= 0:
|
||
transfer_flowrate = 0.5
|
||
logger.warning(f"transfer_flowrate <= 0,使用默认值 {transfer_flowrate}mL/s")
|
||
|
||
# 验证容器存在
|
||
if from_vessel not in G.nodes():
|
||
logger.error(f"源容器 '{from_vessel}' 不存在")
|
||
return pump_action_sequence
|
||
|
||
if to_vessel not in G.nodes():
|
||
logger.error(f"目标容器 '{to_vessel}' 不存在")
|
||
return pump_action_sequence
|
||
|
||
try:
|
||
shortest_path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
|
||
debug_print(f"PUMP_TRANSFER: 路径 {from_vessel} -> {to_vessel}: {shortest_path}")
|
||
except nx.NetworkXNoPath:
|
||
logger.error(f"无法找到从 '{from_vessel}' 到 '{to_vessel}' 的路径")
|
||
return pump_action_sequence
|
||
|
||
# 🔧 关键修复:正确构建泵骨架,排除容器和电磁阀
|
||
pump_backbone = []
|
||
for node in shortest_path:
|
||
# 跳过起始和结束容器
|
||
if node == from_vessel or node == to_vessel:
|
||
continue
|
||
|
||
# 跳过电磁阀(电磁阀不参与泵操作)
|
||
node_data = G.nodes.get(node, {})
|
||
node_class = node_data.get("class", "") or ""
|
||
if ("solenoid" in node_class.lower() or "solenoid_valve" in node.lower()):
|
||
debug_print(f"PUMP_TRANSFER: 跳过电磁阀 {node}")
|
||
continue
|
||
|
||
# 只包含多通阀和泵
|
||
if ("multiway" in node_class.lower() or "valve" in node_class.lower() or "pump" in node_class.lower()):
|
||
pump_backbone.append(node)
|
||
|
||
debug_print(f"PUMP_TRANSFER: 过滤后的泵骨架: {pump_backbone}")
|
||
|
||
if not pump_backbone:
|
||
debug_print("PUMP_TRANSFER: 没有泵骨架节点,可能是直接容器连接或只有电磁阀")
|
||
return pump_action_sequence
|
||
|
||
if transfer_flowrate == 0:
|
||
transfer_flowrate = flowrate
|
||
|
||
try:
|
||
pumps_from_node, valve_from_node = build_pump_valve_maps(G, pump_backbone)
|
||
except Exception as e:
|
||
debug_print(f"PUMP_TRANSFER: 构建泵-阀门映射失败: {str(e)}")
|
||
return pump_action_sequence
|
||
|
||
if not pumps_from_node:
|
||
debug_print("PUMP_TRANSFER: 没有可用的泵映射")
|
||
return pump_action_sequence
|
||
|
||
# 🔧 修复:安全地获取最小转移体积
|
||
try:
|
||
min_transfer_volumes = []
|
||
for node in pump_backbone:
|
||
if node in pumps_from_node:
|
||
pump_node = pumps_from_node[node]
|
||
if pump_node in nodes:
|
||
pump_config = nodes[pump_node].get("config", {})
|
||
max_volume = pump_config.get("max_volume")
|
||
if max_volume is not None:
|
||
min_transfer_volumes.append(max_volume)
|
||
|
||
if min_transfer_volumes:
|
||
min_transfer_volume = min(min_transfer_volumes)
|
||
else:
|
||
min_transfer_volume = 25.0 # 默认值
|
||
debug_print(f"PUMP_TRANSFER: 无法获取泵的最大体积,使用默认值: {min_transfer_volume}mL")
|
||
except Exception as e:
|
||
debug_print(f"PUMP_TRANSFER: 获取最小转移体积失败: {str(e)}")
|
||
min_transfer_volume = 25.0 # 默认值
|
||
|
||
repeats = int(np.ceil(volume / min_transfer_volume))
|
||
|
||
if repeats > 1 and (from_vessel.startswith("pump") or to_vessel.startswith("pump")):
|
||
logger.error("Cannot transfer volume larger than min_transfer_volume between two pumps.")
|
||
return pump_action_sequence
|
||
|
||
volume_left = volume
|
||
debug_print(f"PUMP_TRANSFER: 需要 {repeats} 次转移,单次最大体积 {min_transfer_volume} mL")
|
||
|
||
# 🆕 只在开头打印总体概览
|
||
if repeats > 1:
|
||
debug_print(f"🔄 分批转移概览: 总体积 {volume:.2f}mL,需要 {repeats} 次转移")
|
||
logger.info(f"🔄 分批转移概览: 总体积 {volume:.2f}mL,需要 {repeats} 次转移")
|
||
|
||
# 🔧 创建一个自定义的wait动作,用于在执行时打印日志
|
||
def create_progress_log_action(message: str) -> Dict[str, Any]:
|
||
"""创建一个特殊的等待动作,在执行时打印进度日志"""
|
||
return {
|
||
"action_name": "wait",
|
||
"action_kwargs": {
|
||
"time": 0.1, # 很短的等待时间
|
||
"progress_message": message # 自定义字段,用于进度日志
|
||
}
|
||
}
|
||
|
||
# 生成泵操作序列
|
||
for i in range(repeats):
|
||
current_volume = min(volume_left, min_transfer_volume)
|
||
|
||
# 🆕 在每次循环开始时添加进度日志
|
||
if repeats > 1:
|
||
start_message = f"🚀 准备开始第 {i+1}/{repeats} 次转移: {current_volume:.2f}mL ({from_vessel} → {to_vessel}) 🚰"
|
||
pump_action_sequence.append(create_progress_log_action(start_message))
|
||
|
||
# 🔧 修复:安全地获取边数据
|
||
def get_safe_edge_data(node_a, node_b, key):
|
||
try:
|
||
edge_data = G.get_edge_data(node_a, node_b)
|
||
if edge_data and "port" in edge_data:
|
||
port_data = edge_data["port"]
|
||
if isinstance(port_data, dict) and key in port_data:
|
||
return port_data[key]
|
||
return "default"
|
||
except Exception as e:
|
||
debug_print(f"PUMP_TRANSFER: 获取边数据失败 {node_a}->{node_b}: {str(e)}")
|
||
return "default"
|
||
|
||
# 从源容器吸液
|
||
if not from_vessel.startswith("pump") and pump_backbone:
|
||
first_pump_node = pump_backbone[0]
|
||
if first_pump_node in valve_from_node and first_pump_node in pumps_from_node:
|
||
port_command = get_safe_edge_data(first_pump_node, from_vessel, first_pump_node)
|
||
pump_action_sequence.extend([
|
||
{
|
||
"device_id": valve_from_node[first_pump_node],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_command
|
||
}
|
||
},
|
||
{
|
||
"device_id": pumps_from_node[first_pump_node],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": float(current_volume),
|
||
"max_velocity": transfer_flowrate
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
|
||
|
||
# 泵间转移
|
||
for nodeA, nodeB in zip(pump_backbone[:-1], pump_backbone[1:]):
|
||
if nodeA in valve_from_node and nodeB in valve_from_node and nodeA in pumps_from_node and nodeB in pumps_from_node:
|
||
port_a = get_safe_edge_data(nodeA, nodeB, nodeA)
|
||
port_b = get_safe_edge_data(nodeB, nodeA, nodeB)
|
||
|
||
pump_action_sequence.append([
|
||
{
|
||
"device_id": valve_from_node[nodeA],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_a
|
||
}
|
||
},
|
||
{
|
||
"device_id": valve_from_node[nodeB],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_b
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append([
|
||
{
|
||
"device_id": pumps_from_node[nodeA],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": 0.0,
|
||
"max_velocity": transfer_flowrate
|
||
}
|
||
},
|
||
{
|
||
"device_id": pumps_from_node[nodeB],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": float(current_volume),
|
||
"max_velocity": transfer_flowrate
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
|
||
|
||
# 排液到目标容器
|
||
if not to_vessel.startswith("pump") and pump_backbone:
|
||
last_pump_node = pump_backbone[-1]
|
||
if last_pump_node in valve_from_node and last_pump_node in pumps_from_node:
|
||
port_command = get_safe_edge_data(last_pump_node, to_vessel, last_pump_node)
|
||
pump_action_sequence.extend([
|
||
{
|
||
"device_id": valve_from_node[last_pump_node],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_command
|
||
}
|
||
},
|
||
{
|
||
"device_id": pumps_from_node[last_pump_node],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": 0.0,
|
||
"max_velocity": flowrate
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
|
||
|
||
# 🆕 在每次循环结束时添加完成日志
|
||
if repeats > 1:
|
||
remaining_volume = volume_left - current_volume
|
||
if remaining_volume > 0:
|
||
end_message = f"✅ 第 {i+1}/{repeats} 次转移完成! 剩余 {remaining_volume:.2f}mL 待转移 ⏳"
|
||
else:
|
||
end_message = f"🎉 第 {i+1}/{repeats} 次转移完成! 全部 {volume:.2f}mL 转移完毕 ✨"
|
||
|
||
pump_action_sequence.append(create_progress_log_action(end_message))
|
||
|
||
volume_left -= current_volume
|
||
|
||
return pump_action_sequence
|
||
|
||
|
||
def generate_pump_protocol_with_rinsing(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float = 0.0,
|
||
amount: str = "",
|
||
time: float = 0.0, # 🔧 修复:统一使用 time
|
||
viscous: bool = False,
|
||
rinsing_solvent: str = "",
|
||
rinsing_volume: float = 0.0,
|
||
rinsing_repeats: int = 0,
|
||
solid: bool = False,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
rate_spec: str = "",
|
||
event: str = "",
|
||
through: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
原有的同步版本,添加防冲突机制
|
||
"""
|
||
|
||
# 添加执行锁,防止并发调用
|
||
import threading
|
||
if not hasattr(generate_pump_protocol_with_rinsing, '_lock'):
|
||
generate_pump_protocol_with_rinsing._lock = threading.Lock()
|
||
|
||
with generate_pump_protocol_with_rinsing._lock:
|
||
debug_print("=" * 60)
|
||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (同步版本)")
|
||
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
|
||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
||
debug_print(f" 🔒 获得执行锁")
|
||
debug_print("=" * 60)
|
||
|
||
# 短暂延迟,避免快速重复调用
|
||
time_module.sleep(0.01)
|
||
|
||
debug_print("🔍 步骤1: 开始体积处理...")
|
||
|
||
# 1. 处理体积参数
|
||
final_volume = volume
|
||
debug_print(f"📋 初始设置: final_volume = {final_volume}")
|
||
|
||
# 🔧 修复:如果volume为0(ROS2传入的空值),从容器读取实际体积
|
||
if volume == 0.0:
|
||
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
|
||
|
||
# 直接从源容器读取实际体积
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
debug_print(f"📖 从容器 '{from_vessel}' 读取到体积: {actual_volume}mL")
|
||
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
|
||
else:
|
||
final_volume = 10.0 # 如果读取失败,使用默认值
|
||
logger.warning(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
|
||
else:
|
||
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
|
||
|
||
# 处理 amount 参数
|
||
if amount and amount.strip():
|
||
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
|
||
parsed_volume = _parse_amount_to_volume(amount)
|
||
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
|
||
|
||
if parsed_volume > 0:
|
||
final_volume = parsed_volume
|
||
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
|
||
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
|
||
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
|
||
|
||
# 最终体积验证
|
||
debug_print(f"🔍 步骤2: 最终体积验证...")
|
||
if final_volume <= 0:
|
||
logger.error(f"❌ 体积无效: {final_volume}mL")
|
||
final_volume = 10.0
|
||
logger.warning(f"⚠️ 强制设置为默认值: {final_volume}mL")
|
||
|
||
debug_print(f"✅ 最终确定体积: {final_volume}mL")
|
||
|
||
# 2. 处理流速参数
|
||
debug_print(f"🔍 步骤3: 处理流速参数...")
|
||
debug_print(f" - 原始 flowrate: {flowrate}")
|
||
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
|
||
|
||
final_flowrate = flowrate if flowrate > 0 else 2.5
|
||
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
|
||
|
||
if flowrate <= 0:
|
||
logger.warning(f"⚠️ flowrate <= 0,修正为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0:
|
||
logger.warning(f"⚠️ transfer_flowrate <= 0,修正为: {final_transfer_flowrate}mL/s")
|
||
|
||
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
|
||
|
||
# 3. 根据时间计算流速
|
||
if time > 0 and final_volume > 0:
|
||
debug_print(f"🔍 步骤4: 根据时间计算流速...")
|
||
calculated_flowrate = final_volume / time
|
||
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
|
||
|
||
if flowrate <= 0 or flowrate == 2.5:
|
||
final_flowrate = min(calculated_flowrate, 10.0)
|
||
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
|
||
final_transfer_flowrate = min(calculated_flowrate, 5.0)
|
||
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
|
||
|
||
# 4. 根据速度规格调整
|
||
if rate_spec:
|
||
debug_print(f"🔍 步骤5: 根据速度规格调整...")
|
||
debug_print(f" - 速度规格: '{rate_spec}'")
|
||
|
||
if rate_spec == "dropwise":
|
||
final_flowrate = min(final_flowrate, 0.1)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
|
||
debug_print(f" - dropwise模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "slowly":
|
||
final_flowrate = min(final_flowrate, 0.5)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
|
||
debug_print(f" - slowly模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "quickly":
|
||
final_flowrate = max(final_flowrate, 5.0)
|
||
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
|
||
debug_print(f" - quickly模式,流速调整为: {final_flowrate}mL/s")
|
||
|
||
try:
|
||
# 🆕 修复:在这里调用带有循环日志的generate_pump_protocol_with_loop_logging函数
|
||
pump_action_sequence = generate_pump_protocol_with_loop_logging(
|
||
G, from_vessel, to_vessel, final_volume,
|
||
final_flowrate, final_transfer_flowrate
|
||
)
|
||
|
||
debug_print(f"🔓 释放执行锁")
|
||
return pump_action_sequence
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 协议生成失败: {str(e)}")
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"❌ 协议生成失败: {str(e)}"
|
||
}
|
||
}
|
||
]
|
||
|
||
|
||
def generate_pump_protocol_with_loop_logging(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
生成泵操作的动作序列 - 带循环日志版本
|
||
🔧 修复:正确处理包含电磁阀的路径,并在合适时机打印循环日志
|
||
"""
|
||
pump_action_sequence = []
|
||
nodes = G.nodes(data=True)
|
||
|
||
# 验证输入参数
|
||
if volume <= 0:
|
||
logger.error(f"无效的体积参数: {volume}mL")
|
||
return pump_action_sequence
|
||
|
||
if flowrate <= 0:
|
||
flowrate = 2.5
|
||
logger.warning(f"flowrate <= 0,使用默认值 {flowrate}mL/s")
|
||
|
||
if transfer_flowrate <= 0:
|
||
transfer_flowrate = 0.5
|
||
logger.warning(f"transfer_flowrate <= 0,使用默认值 {transfer_flowrate}mL/s")
|
||
|
||
# 验证容器存在
|
||
if from_vessel not in G.nodes():
|
||
logger.error(f"源容器 '{from_vessel}' 不存在")
|
||
return pump_action_sequence
|
||
|
||
if to_vessel not in G.nodes():
|
||
logger.error(f"目标容器 '{to_vessel}' 不存在")
|
||
return pump_action_sequence
|
||
|
||
try:
|
||
shortest_path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
|
||
debug_print(f"PUMP_TRANSFER: 路径 {from_vessel} -> {to_vessel}: {shortest_path}")
|
||
except nx.NetworkXNoPath:
|
||
logger.error(f"无法找到从 '{from_vessel}' 到 '{to_vessel}' 的路径")
|
||
return pump_action_sequence
|
||
|
||
# 🔧 关键修复:正确构建泵骨架,排除容器和电磁阀
|
||
pump_backbone = []
|
||
for node in shortest_path:
|
||
# 跳过起始和结束容器
|
||
if node == from_vessel or node == to_vessel:
|
||
continue
|
||
|
||
# 跳过电磁阀(电磁阀不参与泵操作)
|
||
node_data = G.nodes.get(node, {})
|
||
node_class = node_data.get("class", "") or ""
|
||
if ("solenoid" in node_class.lower() or "solenoid_valve" in node.lower()):
|
||
debug_print(f"PUMP_TRANSFER: 跳过电磁阀 {node}")
|
||
continue
|
||
|
||
# 只包含多通阀和泵
|
||
if ("multiway" in node_class.lower() or "valve" in node_class.lower() or "pump" in node_class.lower()):
|
||
pump_backbone.append(node)
|
||
|
||
debug_print(f"PUMP_TRANSFER: 过滤后的泵骨架: {pump_backbone}")
|
||
|
||
if not pump_backbone:
|
||
debug_print("PUMP_TRANSFER: 没有泵骨架节点,可能是直接容器连接或只有电磁阀")
|
||
return pump_action_sequence
|
||
|
||
if transfer_flowrate == 0:
|
||
transfer_flowrate = flowrate
|
||
|
||
try:
|
||
pumps_from_node, valve_from_node = build_pump_valve_maps(G, pump_backbone)
|
||
except Exception as e:
|
||
debug_print(f"PUMP_TRANSFER: 构建泵-阀门映射失败: {str(e)}")
|
||
return pump_action_sequence
|
||
|
||
if not pumps_from_node:
|
||
debug_print("PUMP_TRANSFER: 没有可用的泵映射")
|
||
return pump_action_sequence
|
||
|
||
# 🔧 修复:安全地获取最小转移体积
|
||
try:
|
||
min_transfer_volumes = []
|
||
for node in pump_backbone:
|
||
if node in pumps_from_node:
|
||
pump_node = pumps_from_node[node]
|
||
if pump_node in nodes:
|
||
pump_config = nodes[pump_node].get("config", {})
|
||
max_volume = pump_config.get("max_volume")
|
||
if max_volume is not None:
|
||
min_transfer_volumes.append(max_volume)
|
||
|
||
if min_transfer_volumes:
|
||
min_transfer_volume = min(min_transfer_volumes)
|
||
else:
|
||
min_transfer_volume = 25.0 # 默认值
|
||
debug_print(f"PUMP_TRANSFER: 无法获取泵的最大体积,使用默认值: {min_transfer_volume}mL")
|
||
except Exception as e:
|
||
debug_print(f"PUMP_TRANSFER: 获取最小转移体积失败: {str(e)}")
|
||
min_transfer_volume = 25.0 # 默认值
|
||
|
||
repeats = int(np.ceil(volume / min_transfer_volume))
|
||
|
||
if repeats > 1 and (from_vessel.startswith("pump") or to_vessel.startswith("pump")):
|
||
logger.error("Cannot transfer volume larger than min_transfer_volume between two pumps.")
|
||
return pump_action_sequence
|
||
|
||
volume_left = volume
|
||
debug_print(f"PUMP_TRANSFER: 需要 {repeats} 次转移,单次最大体积 {min_transfer_volume} mL")
|
||
|
||
# 🆕 只在开头打印总体概览
|
||
if repeats > 1:
|
||
debug_print(f"🔄 分批转移概览: 总体积 {volume:.2f}mL,需要 {repeats} 次转移")
|
||
logger.info(f"🔄 分批转移概览: 总体积 {volume:.2f}mL,需要 {repeats} 次转移")
|
||
|
||
# 🔧 创建一个自定义的wait动作,用于在执行时打印日志
|
||
def create_progress_log_action(message: str) -> Dict[str, Any]:
|
||
"""创建一个特殊的等待动作,在执行时打印进度日志"""
|
||
return {
|
||
"action_name": "wait",
|
||
"action_kwargs": {
|
||
"time": 0.1, # 很短的等待时间
|
||
"progress_message": message # 自定义字段,用于进度日志
|
||
}
|
||
}
|
||
|
||
# 生成泵操作序列
|
||
for i in range(repeats):
|
||
current_volume = min(volume_left, min_transfer_volume)
|
||
|
||
# 🆕 在每次循环开始时添加进度日志
|
||
if repeats > 1:
|
||
start_message = f"🚀 准备开始第 {i+1}/{repeats} 次转移: {current_volume:.2f}mL ({from_vessel} → {to_vessel}) 🚰"
|
||
pump_action_sequence.append(create_progress_log_action(start_message))
|
||
|
||
# 🔧 修复:安全地获取边数据
|
||
def get_safe_edge_data(node_a, node_b, key):
|
||
try:
|
||
edge_data = G.get_edge_data(node_a, node_b)
|
||
if edge_data and "port" in edge_data:
|
||
port_data = edge_data["port"]
|
||
if isinstance(port_data, dict) and key in port_data:
|
||
return port_data[key]
|
||
return "default"
|
||
except Exception as e:
|
||
debug_print(f"PUMP_TRANSFER: 获取边数据失败 {node_a}->{node_b}: {str(e)}")
|
||
return "default"
|
||
|
||
# 从源容器吸液
|
||
if not from_vessel.startswith("pump") and pump_backbone:
|
||
first_pump_node = pump_backbone[0]
|
||
if first_pump_node in valve_from_node and first_pump_node in pumps_from_node:
|
||
port_command = get_safe_edge_data(first_pump_node, from_vessel, first_pump_node)
|
||
pump_action_sequence.extend([
|
||
{
|
||
"device_id": valve_from_node[first_pump_node],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_command
|
||
}
|
||
},
|
||
{
|
||
"device_id": pumps_from_node[first_pump_node],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": float(current_volume),
|
||
"max_velocity": transfer_flowrate
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
|
||
|
||
# 泵间转移
|
||
for nodeA, nodeB in zip(pump_backbone[:-1], pump_backbone[1:]):
|
||
if nodeA in valve_from_node and nodeB in valve_from_node and nodeA in pumps_from_node and nodeB in pumps_from_node:
|
||
port_a = get_safe_edge_data(nodeA, nodeB, nodeA)
|
||
port_b = get_safe_edge_data(nodeB, nodeA, nodeB)
|
||
|
||
pump_action_sequence.append([
|
||
{
|
||
"device_id": valve_from_node[nodeA],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_a
|
||
}
|
||
},
|
||
{
|
||
"device_id": valve_from_node[nodeB],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_b
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append([
|
||
{
|
||
"device_id": pumps_from_node[nodeA],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": 0.0,
|
||
"max_velocity": transfer_flowrate
|
||
}
|
||
},
|
||
{
|
||
"device_id": pumps_from_node[nodeB],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": float(current_volume),
|
||
"max_velocity": transfer_flowrate
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
|
||
|
||
# 排液到目标容器
|
||
if not to_vessel.startswith("pump") and pump_backbone:
|
||
last_pump_node = pump_backbone[-1]
|
||
if last_pump_node in valve_from_node and last_pump_node in pumps_from_node:
|
||
port_command = get_safe_edge_data(last_pump_node, to_vessel, last_pump_node)
|
||
pump_action_sequence.extend([
|
||
{
|
||
"device_id": valve_from_node[last_pump_node],
|
||
"action_name": "set_valve_position",
|
||
"action_kwargs": {
|
||
"command": port_command
|
||
}
|
||
},
|
||
{
|
||
"device_id": pumps_from_node[last_pump_node],
|
||
"action_name": "set_position",
|
||
"action_kwargs": {
|
||
"position": 0.0,
|
||
"max_velocity": flowrate
|
||
}
|
||
}
|
||
])
|
||
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
|
||
|
||
# 🆕 在每次循环结束时添加完成日志
|
||
if repeats > 1:
|
||
remaining_volume = volume_left - current_volume
|
||
if remaining_volume > 0:
|
||
end_message = f"✅ 第 {i+1}/{repeats} 次转移完成! 剩余 {remaining_volume:.2f}mL 待转移 ⏳"
|
||
else:
|
||
end_message = f"🎉 第 {i+1}/{repeats} 次转移完成! 全部 {volume:.2f}mL 转移完毕 ✨"
|
||
|
||
pump_action_sequence.append(create_progress_log_action(end_message))
|
||
|
||
volume_left -= current_volume
|
||
|
||
return pump_action_sequence
|
||
|
||
|
||
def generate_pump_protocol_with_rinsing(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float = 0.0,
|
||
amount: str = "",
|
||
time: float = 0.0, # 🔧 修复:统一使用 time
|
||
viscous: bool = False,
|
||
rinsing_solvent: str = "",
|
||
rinsing_volume: float = 0.0,
|
||
rinsing_repeats: int = 0,
|
||
solid: bool = False,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
rate_spec: str = "",
|
||
event: str = "",
|
||
through: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
增强兼容性的泵转移协议生成器,支持自动体积检测
|
||
"""
|
||
debug_print("=" * 60)
|
||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议")
|
||
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
|
||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
||
debug_print(f" 📊 原始参数:")
|
||
debug_print(f" - volume: {volume} (类型: {type(volume)})")
|
||
debug_print(f" - amount: '{amount}'")
|
||
debug_print(f" - time: {time}") # 🔧 修复:统一使用 time
|
||
debug_print(f" - flowrate: {flowrate}")
|
||
debug_print(f" - transfer_flowrate: {transfer_flowrate}")
|
||
debug_print(f" - rate_spec: '{rate_spec}'")
|
||
debug_print("=" * 60)
|
||
|
||
# ========== 🔧 核心修复:智能体积处理 ==========
|
||
|
||
debug_print("🔍 步骤1: 开始体积处理...")
|
||
|
||
# 1. 处理体积参数
|
||
final_volume = volume
|
||
debug_print(f"📋 初始设置: final_volume = {final_volume}")
|
||
|
||
# 🔧 修复:如果volume为0(ROS2传入的空值),从容器读取实际体积
|
||
if volume == 0.0:
|
||
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
|
||
|
||
# 直接从源容器读取实际体积
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
debug_print(f"📖 从容器 '{from_vessel}' 读取到体积: {actual_volume}mL")
|
||
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
|
||
else:
|
||
final_volume = 10.0 # 如果读取失败,使用默认值
|
||
debug_print(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
|
||
else:
|
||
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
|
||
|
||
# 处理 amount 参数
|
||
if amount and amount.strip():
|
||
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
|
||
parsed_volume = _parse_amount_to_volume(amount)
|
||
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
|
||
|
||
if parsed_volume > 0:
|
||
final_volume = parsed_volume
|
||
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
|
||
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
|
||
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
|
||
|
||
# 最终体积验证
|
||
debug_print(f"🔍 步骤2: 最终体积验证...")
|
||
if final_volume <= 0:
|
||
debug_print(f"❌ 体积无效: {final_volume}mL")
|
||
final_volume = 10.0
|
||
debug_print(f"⚠️ 强制设置为默认值: {final_volume}mL")
|
||
|
||
debug_print(f"✅ 最终确定体积: {final_volume}mL")
|
||
|
||
# 2. 处理流速参数
|
||
debug_print(f"🔍 步骤3: 处理流速参数...")
|
||
debug_print(f" - 原始 flowrate: {flowrate}")
|
||
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
|
||
|
||
final_flowrate = flowrate if flowrate > 0 else 2.5
|
||
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
|
||
|
||
if flowrate <= 0:
|
||
debug_print(f"⚠️ flowrate <= 0,修正为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0:
|
||
debug_print(f"⚠️ transfer_flowrate <= 0,修正为: {final_transfer_flowrate}mL/s")
|
||
|
||
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
|
||
|
||
# 3. 根据时间计算流速
|
||
if time > 0 and final_volume > 0: # 🔧 修复:统一使用 time
|
||
debug_print(f"🔍 步骤4: 根据时间计算流速...")
|
||
calculated_flowrate = final_volume / time
|
||
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
|
||
|
||
if flowrate <= 0 or flowrate == 2.5:
|
||
final_flowrate = min(calculated_flowrate, 10.0)
|
||
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
|
||
final_transfer_flowrate = min(calculated_flowrate, 5.0)
|
||
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
|
||
|
||
# 4. 根据速度规格调整
|
||
if rate_spec:
|
||
debug_print(f"🔍 步骤5: 根据速度规格调整...")
|
||
debug_print(f" - 速度规格: '{rate_spec}'")
|
||
|
||
if rate_spec == "dropwise":
|
||
final_flowrate = min(final_flowrate, 0.1)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
|
||
debug_print(f" - dropwise模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "slowly":
|
||
final_flowrate = min(final_flowrate, 0.5)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
|
||
debug_print(f" - slowly模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "quickly":
|
||
final_flowrate = max(final_flowrate, 5.0)
|
||
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
|
||
debug_print(f" - quickly模式,流速调整为: {final_flowrate}mL/s")
|
||
|
||
# # 5. 处理冲洗参数
|
||
# debug_print(f"🔍 步骤6: 处理冲洗参数...")
|
||
# final_rinsing_solvent = rinsing_solvent
|
||
# final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
|
||
# final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
|
||
|
||
# if rinsing_volume <= 0:
|
||
# debug_print(f"⚠️ rinsing_volume <= 0,修正为: {final_rinsing_volume}mL")
|
||
# if rinsing_repeats <= 0:
|
||
# debug_print(f"⚠️ rinsing_repeats <= 0,修正为: {final_rinsing_repeats}次")
|
||
|
||
# # 根据物理属性调整冲洗参数
|
||
# if viscous or solid:
|
||
# final_rinsing_repeats = max(final_rinsing_repeats, 3)
|
||
# final_rinsing_volume = max(final_rinsing_volume, 10.0)
|
||
# debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
|
||
|
||
# 参数总结
|
||
debug_print("📊 最终参数总结:")
|
||
debug_print(f" - 体积: {final_volume}mL")
|
||
debug_print(f" - 流速: {final_flowrate}mL/s")
|
||
debug_print(f" - 转移流速: {final_transfer_flowrate}mL/s")
|
||
# debug_print(f" - 冲洗溶剂: '{final_rinsing_solvent}'")
|
||
# debug_print(f" - 冲洗体积: {final_rinsing_volume}mL")
|
||
# debug_print(f" - 冲洗次数: {final_rinsing_repeats}次")
|
||
|
||
# ========== 执行基础转移 ==========
|
||
|
||
debug_print("🔧 步骤7: 开始执行基础转移...")
|
||
|
||
try:
|
||
debug_print(f" - 调用 generate_pump_protocol...")
|
||
debug_print(f" - 参数: G, '{from_vessel}', '{to_vessel}', {final_volume}, {final_flowrate}, {final_transfer_flowrate}")
|
||
|
||
pump_action_sequence = generate_pump_protocol(
|
||
G, from_vessel, to_vessel, final_volume,
|
||
final_flowrate, final_transfer_flowrate
|
||
)
|
||
|
||
debug_print(f" - generate_pump_protocol 返回结果:")
|
||
debug_print(f" - 动作序列长度: {len(pump_action_sequence)}")
|
||
debug_print(f" - 动作序列是否为空: {len(pump_action_sequence) == 0}")
|
||
|
||
if not pump_action_sequence:
|
||
debug_print("❌ 基础转移协议生成为空,可能是路径问题")
|
||
debug_print(f" - 源容器存在: {from_vessel in G.nodes()}")
|
||
debug_print(f" - 目标容器存在: {to_vessel in G.nodes()}")
|
||
|
||
if from_vessel in G.nodes() and to_vessel in G.nodes():
|
||
try:
|
||
path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
|
||
debug_print(f" - 路径存在: {path}")
|
||
except Exception as path_error:
|
||
debug_print(f" - 无法找到路径: {str(path_error)}")
|
||
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"⚠️ 路径问题,无法转移: {final_volume}mL 从 {from_vessel} 到 {to_vessel}"
|
||
}
|
||
}
|
||
]
|
||
|
||
debug_print(f"✅ 基础转移生成了 {len(pump_action_sequence)} 个动作")
|
||
|
||
# 打印前几个动作用于调试
|
||
if len(pump_action_sequence) > 0:
|
||
debug_print("🔍 前几个动作预览:")
|
||
for i, action in enumerate(pump_action_sequence[:3]):
|
||
debug_print(f" 动作 {i+1}: {action}")
|
||
if len(pump_action_sequence) > 3:
|
||
debug_print(f" ... 还有 {len(pump_action_sequence) - 3} 个动作")
|
||
|
||
except Exception as e:
|
||
debug_print(f"❌ 基础转移失败: {str(e)}")
|
||
import traceback
|
||
debug_print(f"详细错误: {traceback.format_exc()}")
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"❌ 转移失败: {final_volume}mL 从 {from_vessel} 到 {to_vessel}, 错误: {str(e)}"
|
||
}
|
||
}
|
||
]
|
||
|
||
# ========== 执行冲洗操作 ==========
|
||
|
||
# debug_print("🔧 步骤8: 检查冲洗操作...")
|
||
|
||
# if final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0:
|
||
# debug_print(f"🧽 开始冲洗操作,溶剂: '{final_rinsing_solvent}'")
|
||
|
||
# try:
|
||
# if final_rinsing_solvent.strip() != "air":
|
||
# debug_print(" - 执行液体冲洗...")
|
||
# rinsing_actions = _generate_rinsing_sequence(
|
||
# G, from_vessel, to_vessel, final_rinsing_solvent,
|
||
# final_rinsing_volume, final_rinsing_repeats,
|
||
# final_flowrate, final_transfer_flowrate
|
||
# )
|
||
# pump_action_sequence.extend(rinsing_actions)
|
||
# debug_print(f" - 添加了 {len(rinsing_actions)} 个冲洗动作")
|
||
# else:
|
||
# debug_print(" - 执行空气冲洗...")
|
||
# air_rinsing_actions = _generate_air_rinsing_sequence(
|
||
# G, from_vessel, to_vessel, final_rinsing_volume, final_rinsing_repeats,
|
||
# final_flowrate, final_transfer_flowrate
|
||
# )
|
||
# pump_action_sequence.extend(air_rinsing_actions)
|
||
# debug_print(f" - 添加了 {len(air_rinsing_actions)} 个空气冲洗动作")
|
||
# except Exception as e:
|
||
# debug_print(f"⚠️ 冲洗操作失败: {str(e)},跳过冲洗")
|
||
# else:
|
||
# debug_print(f"⏭️ 跳过冲洗操作")
|
||
# debug_print(f" - 溶剂: '{final_rinsing_solvent}'")
|
||
# debug_print(f" - 次数: {final_rinsing_repeats}")
|
||
# debug_print(f" - 条件满足: {bool(final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0)}")
|
||
|
||
# ========== 最终结果 ==========
|
||
|
||
debug_print("=" * 60)
|
||
debug_print(f"🎉 PUMP_TRANSFER: 协议生成完成")
|
||
debug_print(f" 📊 总动作数: {len(pump_action_sequence)}")
|
||
debug_print(f" 📋 最终体积: {final_volume}mL")
|
||
debug_print(f" 🚀 执行路径: {from_vessel} -> {to_vessel}")
|
||
|
||
# 最终验证
|
||
if len(pump_action_sequence) == 0:
|
||
debug_print("🚨 协议生成结果为空!这是异常情况")
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"🚨 协议生成失败: 无法生成任何动作序列"
|
||
}
|
||
}
|
||
]
|
||
|
||
debug_print("=" * 60)
|
||
return pump_action_sequence
|
||
|
||
|
||
async def generate_pump_protocol_with_rinsing_async(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float = 0.0,
|
||
amount: str = "",
|
||
time: float = 0.0,
|
||
viscous: bool = False,
|
||
rinsing_solvent: str = "",
|
||
rinsing_volume: float = 0.0,
|
||
rinsing_repeats: int = 0,
|
||
solid: bool = False,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
rate_spec: str = "",
|
||
event: str = "",
|
||
through: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
异步版本的泵转移协议生成器,避免并发问题
|
||
"""
|
||
debug_print("=" * 60)
|
||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (异步版本)")
|
||
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
|
||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
||
debug_print("=" * 60)
|
||
|
||
# 添加唯一标识符
|
||
protocol_id = f"pump_transfer_{int(time_module.time() * 1000000)}"
|
||
debug_print(f"📋 协议ID: {protocol_id}")
|
||
|
||
# 调用原有的同步版本
|
||
result = generate_pump_protocol_with_rinsing(
|
||
G, from_vessel, to_vessel, volume, amount, time, viscous,
|
||
rinsing_solvent, rinsing_volume, rinsing_repeats, solid,
|
||
flowrate, transfer_flowrate, rate_spec, event, through, **kwargs
|
||
)
|
||
|
||
# 为每个动作添加唯一标识
|
||
for i, action in enumerate(result):
|
||
if isinstance(action, dict):
|
||
action['_protocol_id'] = protocol_id
|
||
action['_action_sequence'] = i
|
||
action['_timestamp'] = time_module.time()
|
||
|
||
debug_print(f"📊 协议 {protocol_id} 生成完成,共 {len(result)} 个动作")
|
||
return result
|
||
|
||
# 保持原有的同步版本兼容性
|
||
def generate_pump_protocol_with_rinsing(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float = 0.0,
|
||
amount: str = "",
|
||
time: float = 0.0,
|
||
viscous: bool = False,
|
||
rinsing_solvent: str = "",
|
||
rinsing_volume: float = 0.0,
|
||
rinsing_repeats: int = 0,
|
||
solid: bool = False,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
rate_spec: str = "",
|
||
event: str = "",
|
||
through: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
原有的同步版本,添加防冲突机制
|
||
"""
|
||
|
||
# 添加执行锁,防止并发调用
|
||
import threading
|
||
if not hasattr(generate_pump_protocol_with_rinsing, '_lock'):
|
||
generate_pump_protocol_with_rinsing._lock = threading.Lock()
|
||
|
||
with generate_pump_protocol_with_rinsing._lock:
|
||
debug_print("=" * 60)
|
||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (同步版本)")
|
||
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
|
||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
||
debug_print(f" 🔒 获得执行锁")
|
||
debug_print("=" * 60)
|
||
|
||
# 短暂延迟,避免快速重复调用
|
||
time_module.sleep(0.01)
|
||
|
||
debug_print("🔍 步骤1: 开始体积处理...")
|
||
|
||
# 1. 处理体积参数
|
||
final_volume = volume
|
||
debug_print(f"📋 初始设置: final_volume = {final_volume}")
|
||
|
||
# 🔧 修复:如果volume为0(ROS2传入的空值),从容器读取实际体积
|
||
if volume == 0.0:
|
||
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
|
||
|
||
# 直接从源容器读取实际体积
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
debug_print(f"📖 从容器 '{from_vessel}' 读取到体积: {actual_volume}mL")
|
||
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
|
||
else:
|
||
final_volume = 10.0 # 如果读取失败,使用默认值
|
||
logger.warning(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
|
||
else:
|
||
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
|
||
|
||
# 处理 amount 参数
|
||
if amount and amount.strip():
|
||
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
|
||
parsed_volume = _parse_amount_to_volume(amount)
|
||
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
|
||
|
||
if parsed_volume > 0:
|
||
final_volume = parsed_volume
|
||
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
|
||
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
|
||
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
|
||
|
||
# 最终体积验证
|
||
debug_print(f"🔍 步骤2: 最终体积验证...")
|
||
if final_volume <= 0:
|
||
logger.error(f"❌ 体积无效: {final_volume}mL")
|
||
final_volume = 10.0
|
||
logger.warning(f"⚠️ 强制设置为默认值: {final_volume}mL")
|
||
|
||
debug_print(f"✅ 最终确定体积: {final_volume}mL")
|
||
|
||
# 2. 处理流速参数
|
||
debug_print(f"🔍 步骤3: 处理流速参数...")
|
||
debug_print(f" - 原始 flowrate: {flowrate}")
|
||
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
|
||
|
||
final_flowrate = flowrate if flowrate > 0 else 2.5
|
||
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
|
||
|
||
if flowrate <= 0:
|
||
logger.warning(f"⚠️ flowrate <= 0,修正为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0:
|
||
logger.warning(f"⚠️ transfer_flowrate <= 0,修正为: {final_transfer_flowrate}mL/s")
|
||
|
||
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
|
||
|
||
# 3. 根据时间计算流速
|
||
if time > 0 and final_volume > 0:
|
||
debug_print(f"🔍 步骤4: 根据时间计算流速...")
|
||
calculated_flowrate = final_volume / time
|
||
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
|
||
|
||
if flowrate <= 0 or flowrate == 2.5:
|
||
final_flowrate = min(calculated_flowrate, 10.0)
|
||
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
|
||
final_transfer_flowrate = min(calculated_flowrate, 5.0)
|
||
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
|
||
|
||
# 4. 根据速度规格调整
|
||
if rate_spec:
|
||
debug_print(f"🔍 步骤5: 根据速度规格调整...")
|
||
debug_print(f" - 速度规格: '{rate_spec}'")
|
||
|
||
if rate_spec == "dropwise":
|
||
final_flowrate = min(final_flowrate, 0.1)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
|
||
debug_print(f" - dropwise模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "slowly":
|
||
final_flowrate = min(final_flowrate, 0.5)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
|
||
debug_print(f" - slowly模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "quickly":
|
||
final_flowrate = max(final_flowrate, 5.0)
|
||
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
|
||
debug_print(f" - quickly模式,流速调整为: {final_flowrate}mL/s")
|
||
|
||
# # 5. 处理冲洗参数
|
||
# debug_print(f"🔍 步骤6: 处理冲洗参数...")
|
||
# final_rinsing_solvent = rinsing_solvent
|
||
# final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
|
||
# final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
|
||
|
||
# if rinsing_volume <= 0:
|
||
# logger.warning(f"⚠️ rinsing_volume <= 0,修正为: {final_rinsing_volume}mL")
|
||
# if rinsing_repeats <= 0:
|
||
# logger.warning(f"⚠️ rinsing_repeats <= 0,修正为: {final_rinsing_repeats}次")
|
||
|
||
# # 根据物理属性调整冲洗参数
|
||
# if viscous or solid:
|
||
# final_rinsing_repeats = max(final_rinsing_repeats, 3)
|
||
# final_rinsing_volume = max(final_rinsing_volume, 10.0)
|
||
# debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
|
||
|
||
# 参数总结
|
||
debug_print("📊 最终参数总结:")
|
||
debug_print(f" - 体积: {final_volume}mL")
|
||
debug_print(f" - 流速: {final_flowrate}mL/s")
|
||
debug_print(f" - 转移流速: {final_transfer_flowrate}mL/s")
|
||
# debug_print(f" - 冲洗溶剂: '{final_rinsing_solvent}'")
|
||
# debug_print(f" - 冲洗体积: {final_rinsing_volume}mL")
|
||
# debug_print(f" - 冲洗次数: {final_rinsing_repeats}次")
|
||
|
||
# ========== 执行基础转移 ==========
|
||
|
||
debug_print("🔧 步骤7: 开始执行基础转移...")
|
||
|
||
try:
|
||
debug_print(f" - 调用 generate_pump_protocol...")
|
||
debug_print(f" - 参数: G, '{from_vessel}', '{to_vessel}', {final_volume}, {final_flowrate}, {final_transfer_flowrate}")
|
||
|
||
pump_action_sequence = generate_pump_protocol(
|
||
G, from_vessel, to_vessel, final_volume,
|
||
final_flowrate, final_transfer_flowrate
|
||
)
|
||
|
||
debug_print(f" - generate_pump_protocol 返回结果:")
|
||
debug_print(f" - 动作序列长度: {len(pump_action_sequence)}")
|
||
debug_print(f" - 动作序列是否为空: {len(pump_action_sequence) == 0}")
|
||
|
||
if not pump_action_sequence:
|
||
debug_print("❌ 基础转移协议生成为空,可能是路径问题")
|
||
debug_print(f" - 源容器存在: {from_vessel in G.nodes()}")
|
||
debug_print(f" - 目标容器存在: {to_vessel in G.nodes()}")
|
||
|
||
if from_vessel in G.nodes() and to_vessel in G.nodes():
|
||
try:
|
||
path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
|
||
debug_print(f" - 路径存在: {path}")
|
||
except Exception as path_error:
|
||
debug_print(f" - 无法找到路径: {str(path_error)}")
|
||
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"⚠️ 路径问题,无法转移: {final_volume}mL 从 {from_vessel} 到 {to_vessel}"
|
||
}
|
||
}
|
||
]
|
||
|
||
debug_print(f"✅ 基础转移生成了 {len(pump_action_sequence)} 个动作")
|
||
|
||
# 打印前几个动作用于调试
|
||
if len(pump_action_sequence) > 0:
|
||
debug_print("🔍 前几个动作预览:")
|
||
for i, action in enumerate(pump_action_sequence[:3]):
|
||
debug_print(f" 动作 {i+1}: {action}")
|
||
if len(pump_action_sequence) > 3:
|
||
debug_print(f" ... 还有 {len(pump_action_sequence) - 3} 个动作")
|
||
|
||
except Exception as e:
|
||
debug_print(f"❌ 基础转移失败: {str(e)}")
|
||
import traceback
|
||
debug_print(f"详细错误: {traceback.format_exc()}")
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"❌ 转移失败: {final_volume}mL 从 {from_vessel} 到 {to_vessel}, 错误: {str(e)}"
|
||
}
|
||
}
|
||
]
|
||
|
||
# ========== 执行冲洗操作 ==========
|
||
|
||
# debug_print("🔧 步骤8: 检查冲洗操作...")
|
||
|
||
# if final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0:
|
||
# debug_print(f"🧽 开始冲洗操作,溶剂: '{final_rinsing_solvent}'")
|
||
|
||
# try:
|
||
# if final_rinsing_solvent.strip() != "air":
|
||
# debug_print(" - 执行液体冲洗...")
|
||
# rinsing_actions = _generate_rinsing_sequence(
|
||
# G, from_vessel, to_vessel, final_rinsing_solvent,
|
||
# final_rinsing_volume, final_rinsing_repeats,
|
||
# final_flowrate, final_transfer_flowrate
|
||
# )
|
||
# pump_action_sequence.extend(rinsing_actions)
|
||
# debug_print(f" - 添加了 {len(rinsing_actions)} 个冲洗动作")
|
||
# else:
|
||
# debug_print(" - 执行空气冲洗...")
|
||
# air_rinsing_actions = _generate_air_rinsing_sequence(
|
||
# G, from_vessel, to_vessel, final_rinsing_volume, final_rinsing_repeats,
|
||
# final_flowrate, final_transfer_flowrate
|
||
# )
|
||
# pump_action_sequence.extend(air_rinsing_actions)
|
||
# debug_print(f" - 添加了 {len(air_rinsing_actions)} 个空气冲洗动作")
|
||
# except Exception as e:
|
||
# debug_print(f"⚠️ 冲洗操作失败: {str(e)},跳过冲洗")
|
||
# else:
|
||
# debug_print(f"⏭️ 跳过冲洗操作")
|
||
# debug_print(f" - 溶剂: '{final_rinsing_solvent}'")
|
||
# debug_print(f" - 次数: {final_rinsing_repeats}")
|
||
# debug_print(f" - 条件满足: {bool(final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0)}")
|
||
|
||
# ========== 最终结果 ==========
|
||
|
||
debug_print("=" * 60)
|
||
debug_print(f"🎉 PUMP_TRANSFER: 协议生成完成")
|
||
debug_print(f" 📊 总动作数: {len(pump_action_sequence)}")
|
||
debug_print(f" 📋 最终体积: {final_volume}mL")
|
||
debug_print(f" 🚀 执行路径: {from_vessel} -> {to_vessel}")
|
||
|
||
# 最终验证
|
||
if len(pump_action_sequence) == 0:
|
||
debug_print("🚨 协议生成结果为空!这是异常情况")
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"🚨 协议生成失败: 无法生成任何动作序列"
|
||
}
|
||
}
|
||
]
|
||
|
||
debug_print("=" * 60)
|
||
return pump_action_sequence
|
||
|
||
|
||
async def generate_pump_protocol_with_rinsing_async(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float = 0.0,
|
||
amount: str = "",
|
||
time: float = 0.0,
|
||
viscous: bool = False,
|
||
rinsing_solvent: str = "",
|
||
rinsing_volume: float = 0.0,
|
||
rinsing_repeats: int = 0,
|
||
solid: bool = False,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
rate_spec: str = "",
|
||
event: str = "",
|
||
through: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
异步版本的泵转移协议生成器,避免并发问题
|
||
"""
|
||
debug_print("=" * 60)
|
||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (异步版本)")
|
||
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
|
||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
||
debug_print("=" * 60)
|
||
|
||
# 添加唯一标识符
|
||
protocol_id = f"pump_transfer_{int(time_module.time() * 1000000)}"
|
||
debug_print(f"📋 协议ID: {protocol_id}")
|
||
|
||
# 调用原有的同步版本
|
||
result = generate_pump_protocol_with_rinsing(
|
||
G, from_vessel, to_vessel, volume, amount, time, viscous,
|
||
rinsing_solvent, rinsing_volume, rinsing_repeats, solid,
|
||
flowrate, transfer_flowrate, rate_spec, event, through, **kwargs
|
||
)
|
||
|
||
# 为每个动作添加唯一标识
|
||
for i, action in enumerate(result):
|
||
if isinstance(action, dict):
|
||
action['_protocol_id'] = protocol_id
|
||
action['_action_sequence'] = i
|
||
action['_timestamp'] = time_module.time()
|
||
|
||
debug_print(f"📊 协议 {protocol_id} 生成完成,共 {len(result)} 个动作")
|
||
return result
|
||
|
||
# 保持原有的同步版本兼容性
|
||
def generate_pump_protocol_with_rinsing(
|
||
G: nx.DiGraph,
|
||
from_vessel: str,
|
||
to_vessel: str,
|
||
volume: float = 0.0,
|
||
amount: str = "",
|
||
time: float = 0.0,
|
||
viscous: bool = False,
|
||
rinsing_solvent: str = "",
|
||
rinsing_volume: float = 0.0,
|
||
rinsing_repeats: int = 0,
|
||
solid: bool = False,
|
||
flowrate: float = 2.5,
|
||
transfer_flowrate: float = 0.5,
|
||
rate_spec: str = "",
|
||
event: str = "",
|
||
through: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
原有的同步版本,添加防冲突机制
|
||
"""
|
||
|
||
# 添加执行锁,防止并发调用
|
||
import threading
|
||
if not hasattr(generate_pump_protocol_with_rinsing, '_lock'):
|
||
generate_pump_protocol_with_rinsing._lock = threading.Lock()
|
||
|
||
with generate_pump_protocol_with_rinsing._lock:
|
||
debug_print("=" * 60)
|
||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (同步版本)")
|
||
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
|
||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
||
debug_print(f" 🔒 获得执行锁")
|
||
debug_print("=" * 60)
|
||
|
||
# 短暂延迟,避免快速重复调用
|
||
time_module.sleep(0.01)
|
||
|
||
debug_print("🔍 步骤1: 开始体积处理...")
|
||
|
||
# 1. 处理体积参数
|
||
final_volume = volume
|
||
debug_print(f"📋 初始设置: final_volume = {final_volume}")
|
||
|
||
# 🔧 修复:如果volume为0(ROS2传入的空值),从容器读取实际体积
|
||
if volume == 0.0:
|
||
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
|
||
|
||
# 直接从源容器读取实际体积
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
debug_print(f"📖 从容器 '{from_vessel}' 读取到体积: {actual_volume}mL")
|
||
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
|
||
else:
|
||
final_volume = 10.0 # 如果读取失败,使用默认值
|
||
logger.warning(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
|
||
else:
|
||
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
|
||
|
||
# 处理 amount 参数
|
||
if amount and amount.strip():
|
||
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
|
||
parsed_volume = _parse_amount_to_volume(amount)
|
||
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
|
||
|
||
if parsed_volume > 0:
|
||
final_volume = parsed_volume
|
||
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
|
||
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
|
||
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
|
||
actual_volume = get_vessel_liquid_volume(G, from_vessel)
|
||
if actual_volume > 0:
|
||
final_volume = actual_volume
|
||
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
|
||
|
||
# 最终体积验证
|
||
debug_print(f"🔍 步骤2: 最终体积验证...")
|
||
if final_volume <= 0:
|
||
logger.error(f"❌ 体积无效: {final_volume}mL")
|
||
final_volume = 10.0
|
||
logger.warning(f"⚠️ 强制设置为默认值: {final_volume}mL")
|
||
|
||
debug_print(f"✅ 最终确定体积: {final_volume}mL")
|
||
|
||
# 2. 处理流速参数
|
||
debug_print(f"🔍 步骤3: 处理流速参数...")
|
||
debug_print(f" - 原始 flowrate: {flowrate}")
|
||
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
|
||
|
||
final_flowrate = flowrate if flowrate > 0 else 2.5
|
||
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
|
||
|
||
if flowrate <= 0:
|
||
logger.warning(f"⚠️ flowrate <= 0,修正为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0:
|
||
logger.warning(f"⚠️ transfer_flowrate <= 0,修正为: {final_transfer_flowrate}mL/s")
|
||
|
||
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
|
||
|
||
# 3. 根据时间计算流速
|
||
if time > 0 and final_volume > 0:
|
||
debug_print(f"🔍 步骤4: 根据时间计算流速...")
|
||
calculated_flowrate = final_volume / time
|
||
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
|
||
|
||
if flowrate <= 0 or flowrate == 2.5:
|
||
final_flowrate = min(calculated_flowrate, 10.0)
|
||
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
|
||
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
|
||
final_transfer_flowrate = min(calculated_flowrate, 5.0)
|
||
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
|
||
|
||
# 4. 根据速度规格调整
|
||
if rate_spec:
|
||
debug_print(f"🔍 步骤5: 根据速度规格调整...")
|
||
debug_print(f" - 速度规格: '{rate_spec}'")
|
||
|
||
if rate_spec == "dropwise":
|
||
final_flowrate = min(final_flowrate, 0.1)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
|
||
debug_print(f" - dropwise模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "slowly":
|
||
final_flowrate = min(final_flowrate, 0.5)
|
||
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
|
||
debug_print(f" - slowly模式,流速调整为: {final_flowrate}mL/s")
|
||
elif rate_spec == "quickly":
|
||
final_flowrate = max(final_flowrate, 5.0)
|
||
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
|
||
debug_print(f" - quickly模式,流速调整为: {final_flowrate}mL/s")
|
||
|
||
# # 5. 处理冲洗参数
|
||
# debug_print(f"🔍 步骤6: 处理冲洗参数...")
|
||
# final_rinsing_solvent = rinsing_solvent
|
||
# final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
|
||
# final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
|
||
|
||
# if rinsing_volume <= 0:
|
||
# logger.warning(f"⚠️ rinsing_volume <= 0,修正为: {final_rinsing_volume}mL")
|
||
# if rinsing_repeats <= 0:
|
||
# logger.warning(f"⚠️ rinsing_repeats <= 0,修正为: {final_rinsing_repeats}次")
|
||
|
||
# # 根据物理属性调整冲洗参数
|
||
# if viscous or solid:
|
||
# final_rinsing_repeats = max(final_rinsing_repeats, 3)
|
||
# final_rinsing_volume = max(final_rinsing_volume, 10.0)
|
||
# debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
|
||
|
||
try:
|
||
pump_action_sequence = generate_pump_protocol(
|
||
G, from_vessel, to_vessel, final_volume,
|
||
flowrate, transfer_flowrate
|
||
)
|
||
|
||
# 为每个动作添加唯一标识
|
||
# for i, action in enumerate(pump_action_sequence):
|
||
# if isinstance(action, dict):
|
||
# action['_protocol_id'] = protocol_id
|
||
# action['_action_sequence'] = i
|
||
# elif isinstance(action, list):
|
||
# for j, sub_action in enumerate(action):
|
||
# if isinstance(sub_action, dict):
|
||
# sub_action['_protocol_id'] = protocol_id
|
||
# sub_action['_action_sequence'] = f"{i}_{j}"
|
||
#
|
||
# debug_print(f"📊 协议 {protocol_id} 生成完成,共 {len(pump_action_sequence)} 个动作")
|
||
debug_print(f"🔓 释放执行锁")
|
||
return pump_action_sequence
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 协议生成失败: {str(e)}")
|
||
return [
|
||
{
|
||
"device_id": "system",
|
||
"action_name": "log_message",
|
||
"action_kwargs": {
|
||
"message": f"❌ 协议生成失败: {str(e)}"
|
||
}
|
||
}
|
||
]
|
||
|
||
def _parse_amount_to_volume(amount: str) -> float:
|
||
"""解析 amount 字符串为体积"""
|
||
debug_print(f"🔍 解析 amount: '{amount}'")
|
||
|
||
if not amount:
|
||
debug_print(" - amount 为空,返回 0.0")
|
||
return 0.0
|
||
|
||
amount = amount.lower().strip()
|
||
debug_print(f" - 处理后的 amount: '{amount}'")
|
||
|
||
# 处理特殊关键词
|
||
if amount == "all":
|
||
debug_print(" - 检测到 'all',返回 0.0(需要后续处理)")
|
||
return 0.0 # 返回0.0,让调用者处理
|
||
|
||
# 提取数字
|
||
import re
|
||
numbers = re.findall(r'[\d.]+', amount)
|
||
debug_print(f" - 提取到的数字: {numbers}")
|
||
|
||
if numbers:
|
||
volume = float(numbers[0])
|
||
debug_print(f" - 基础体积: {volume}")
|
||
|
||
# 单位转换
|
||
if 'ml' in amount or 'milliliter' in amount:
|
||
debug_print(f" - 单位: mL,最终体积: {volume}")
|
||
return volume
|
||
elif 'l' in amount and 'ml' not in amount:
|
||
final_volume = volume * 1000
|
||
debug_print(f" - 单位: L,最终体积: {final_volume}mL")
|
||
return final_volume
|
||
elif 'μl' in amount or 'microliter' in amount:
|
||
final_volume = volume / 1000
|
||
debug_print(f" - 单位: μL,最终体积: {final_volume}mL")
|
||
return final_volume
|
||
else:
|
||
debug_print(f" - 无单位,假设为 mL: {volume}")
|
||
return volume
|
||
|
||
debug_print(" - 无法解析,返回 0.0")
|
||
return 0.0
|
||
|
||
|
||
def _generate_rinsing_sequence(G: nx.DiGraph, from_vessel: str, to_vessel: str,
|
||
rinsing_solvent: str, rinsing_volume: float,
|
||
rinsing_repeats: int, flowrate: float,
|
||
transfer_flowrate: float) -> List[Dict[str, Any]]:
|
||
"""生成冲洗动作序列"""
|
||
rinsing_actions = []
|
||
|
||
try:
|
||
shortest_path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
|
||
pump_backbone = shortest_path[1:-1]
|
||
|
||
if not pump_backbone:
|
||
return rinsing_actions
|
||
|
||
nodes = G.nodes(data=True)
|
||
pumps_from_node, valve_from_node = build_pump_valve_maps(G, pump_backbone)
|
||
min_transfer_volume = min([nodes[pumps_from_node[node]]["config"]["max_volume"] for node in pump_backbone])
|
||
|
||
waste_vessel = "waste_workup"
|
||
|
||
# 处理多种溶剂情况
|
||
if "," in rinsing_solvent:
|
||
rinsing_solvents = rinsing_solvent.split(",")
|
||
if len(rinsing_solvents) != rinsing_repeats:
|
||
rinsing_solvents = [rinsing_solvent] * rinsing_repeats
|
||
else:
|
||
rinsing_solvents = [rinsing_solvent] * rinsing_repeats
|
||
|
||
for solvent in rinsing_solvents:
|
||
solvent_vessel = f"flask_{solvent.strip()}"
|
||
|
||
# 检查溶剂容器是否存在
|
||
if solvent_vessel not in G.nodes():
|
||
logger.warning(f"溶剂容器 {solvent_vessel} 不存在,跳过该溶剂冲洗")
|
||
continue
|
||
|
||
# 清洗泵系统
|
||
rinsing_actions.extend(
|
||
generate_pump_protocol(G, solvent_vessel, pump_backbone[0], min_transfer_volume, flowrate, transfer_flowrate)
|
||
)
|
||
|
||
if len(pump_backbone) > 1:
|
||
rinsing_actions.extend(
|
||
generate_pump_protocol(G, pump_backbone[0], pump_backbone[-1], min_transfer_volume, flowrate, transfer_flowrate)
|
||
)
|
||
|
||
# 排到废液容器
|
||
if waste_vessel in G.nodes():
|
||
rinsing_actions.extend(
|
||
generate_pump_protocol(G, pump_backbone[-1], waste_vessel, min_transfer_volume, flowrate, transfer_flowrate)
|
||
)
|
||
|
||
# 第一种冲洗溶剂稀释源容器和目标容器
|
||
if solvent == rinsing_solvents[0]:
|
||
rinsing_actions.extend(
|
||
generate_pump_protocol(G, solvent_vessel, from_vessel, rinsing_volume, flowrate, transfer_flowrate)
|
||
)
|
||
rinsing_actions.extend(
|
||
generate_pump_protocol(G, solvent_vessel, to_vessel, rinsing_volume, flowrate, transfer_flowrate)
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"生成冲洗序列失败: {str(e)}")
|
||
|
||
return rinsing_actions
|
||
|
||
|
||
def _generate_air_rinsing_sequence(G: nx.DiGraph, from_vessel: str, to_vessel: str,
|
||
rinsing_volume: float, repeats: int,
|
||
flowrate: float, transfer_flowrate: float) -> List[Dict[str, Any]]:
|
||
"""生成空气冲洗序列"""
|
||
air_rinsing_actions = []
|
||
|
||
try:
|
||
air_vessel = "flask_air"
|
||
if air_vessel not in G.nodes():
|
||
logger.warning("空气容器 flask_air 不存在,跳过空气冲洗")
|
||
return air_rinsing_actions
|
||
|
||
for _ in range(repeats):
|
||
# 空气冲洗源容器
|
||
air_rinsing_actions.extend(
|
||
generate_pump_protocol(G, air_vessel, from_vessel, rinsing_volume, flowrate, transfer_flowrate)
|
||
)
|
||
|
||
# 空气冲洗目标容器
|
||
air_rinsing_actions.extend(
|
||
generate_pump_protocol(G, air_vessel, to_vessel, rinsing_volume, flowrate, transfer_flowrate)
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.warning(f"空气冲洗失败: {str(e)}")
|
||
|
||
return air_rinsing_actions
|