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>
1103 lines
47 KiB
Python
1103 lines
47 KiB
Python
from __future__ import annotations
|
||
|
||
import traceback
|
||
from typing import List, Sequence, Optional, Literal, Union, Iterator, Dict, Any, Callable, Set
|
||
|
||
import asyncio
|
||
import time
|
||
|
||
from pylabrobot.liquid_handling import LiquidHandler, LiquidHandlerBackend, LiquidHandlerChatterboxBackend, Strictness
|
||
from pylabrobot.liquid_handling.liquid_handler import TipPresenceProbingMethod
|
||
from pylabrobot.liquid_handling.standard import GripDirection
|
||
from pylabrobot.resources import (
|
||
Resource,
|
||
TipRack,
|
||
Container,
|
||
Coordinate,
|
||
Well,
|
||
Deck,
|
||
TipSpot,
|
||
Plate,
|
||
ResourceStack,
|
||
ResourceHolder,
|
||
Lid,
|
||
Trash,
|
||
Tip,
|
||
)
|
||
|
||
|
||
class LiquidHandlerMiddleware(LiquidHandler):
|
||
def __init__(self, backend: LiquidHandlerBackend, deck: Deck, simulator: bool = False, channel_num: int = 8):
|
||
self._simulator = simulator
|
||
if simulator:
|
||
self._simulate_backend = LiquidHandlerChatterboxBackend(channel_num)
|
||
self._simulate_handler = LiquidHandlerAbstract(self._simulate_backend, deck, False)
|
||
super().__init__(backend, deck)
|
||
|
||
async def setup(self, **backend_kwargs):
|
||
if self._simulator:
|
||
return await self._simulate_handler.setup(**backend_kwargs)
|
||
return await super().setup(**backend_kwargs)
|
||
|
||
def serialize_state(self) -> Dict[str, Any]:
|
||
if self._simulator:
|
||
self._simulate_handler.serialize_state()
|
||
return super().serialize_state()
|
||
|
||
def load_state(self, state: Dict[str, Any]):
|
||
if self._simulator:
|
||
self._simulate_handler.load_state(state)
|
||
super().load_state(state)
|
||
|
||
def update_head_state(self, state: Dict[int, Optional[Tip]]):
|
||
if self._simulator:
|
||
self._simulate_handler.update_head_state(state)
|
||
super().update_head_state(state)
|
||
|
||
def clear_head_state(self):
|
||
if self._simulator:
|
||
self._simulate_handler.clear_head_state()
|
||
super().clear_head_state()
|
||
|
||
def _run_async_in_thread(self, func, *args, **kwargs):
|
||
super()._run_async_in_thread(func, *args, **kwargs)
|
||
|
||
def _send_assigned_resource_to_backend(self, resource: Resource):
|
||
if self._simulator:
|
||
self._simulate_handler._send_assigned_resource_to_backend(resource)
|
||
super()._send_assigned_resource_to_backend(resource)
|
||
|
||
def _send_unassigned_resource_to_backend(self, resource: Resource):
|
||
if self._simulator:
|
||
self._simulate_handler._send_unassigned_resource_to_backend(resource)
|
||
super()._send_unassigned_resource_to_backend(resource)
|
||
|
||
def summary(self):
|
||
if self._simulator:
|
||
self._simulate_handler.summary()
|
||
super().summary()
|
||
|
||
def _assert_positions_unique(self, positions: List[str]):
|
||
super()._assert_positions_unique(positions)
|
||
|
||
def _assert_resources_exist(self, resources: Sequence[Resource]):
|
||
super()._assert_resources_exist(resources)
|
||
|
||
def _check_args(
|
||
self, method: Callable, backend_kwargs: Dict[str, Any], default: Set[str], strictness: Strictness
|
||
) -> Set[str]:
|
||
return super()._check_args(method, backend_kwargs, default, strictness)
|
||
|
||
def _make_sure_channels_exist(self, channels: List[int]):
|
||
super()._make_sure_channels_exist(channels)
|
||
|
||
def _format_param(self, value: Any) -> Any:
|
||
return super()._format_param(value)
|
||
|
||
def _log_command(self, name: str, **kwargs) -> None:
|
||
super()._log_command(name, **kwargs)
|
||
|
||
async def pick_up_tips(
|
||
self,
|
||
tip_spots: List[TipSpot],
|
||
use_channels: Optional[List[int]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
**backend_kwargs,
|
||
):
|
||
print('222'*200)
|
||
print(tip_spots)
|
||
if self._simulator:
|
||
return await self._simulate_handler.pick_up_tips(tip_spots, use_channels, offsets, **backend_kwargs)
|
||
return await super().pick_up_tips(tip_spots, use_channels, offsets, **backend_kwargs)
|
||
|
||
async def drop_tips(
|
||
self,
|
||
tip_spots: Sequence[Union[TipSpot, Trash]],
|
||
use_channels: Optional[List[int]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
allow_nonzero_volume: bool = False,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.drop_tips(
|
||
tip_spots, use_channels, offsets, allow_nonzero_volume, **backend_kwargs
|
||
)
|
||
return await super().drop_tips(tip_spots, use_channels, offsets, allow_nonzero_volume, **backend_kwargs)
|
||
|
||
async def return_tips(
|
||
self, use_channels: Optional[list[int]] = None, allow_nonzero_volume: bool = False, **backend_kwargs
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.return_tips(use_channels, allow_nonzero_volume, **backend_kwargs)
|
||
return await super().return_tips(use_channels, allow_nonzero_volume, **backend_kwargs)
|
||
|
||
async def discard_tips(
|
||
self,
|
||
use_channels: Optional[List[int]] = None,
|
||
allow_nonzero_volume: bool = True,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.discard_tips(use_channels, allow_nonzero_volume, offsets, **backend_kwargs)
|
||
return await super().discard_tips(use_channels, allow_nonzero_volume, offsets, **backend_kwargs)
|
||
|
||
def _check_containers(self, resources: Sequence[Resource]):
|
||
super()._check_containers(resources)
|
||
|
||
async def aspirate(
|
||
self,
|
||
resources: Sequence[Container],
|
||
vols: List[float],
|
||
use_channels: Optional[List[int]] = None,
|
||
flow_rates: Optional[List[Optional[float]]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
liquid_height: Optional[List[Optional[float]]] = None,
|
||
blow_out_air_volume: Optional[List[Optional[float]]] = None,
|
||
spread: Literal["wide", "tight", "custom"] = "wide",
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.aspirate(
|
||
resources,
|
||
vols,
|
||
use_channels,
|
||
flow_rates,
|
||
offsets,
|
||
liquid_height,
|
||
blow_out_air_volume,
|
||
spread,
|
||
**backend_kwargs,
|
||
)
|
||
return await super().aspirate(
|
||
resources,
|
||
vols,
|
||
use_channels,
|
||
flow_rates,
|
||
offsets,
|
||
liquid_height,
|
||
blow_out_air_volume,
|
||
spread,
|
||
**backend_kwargs,
|
||
)
|
||
|
||
async def dispense(
|
||
self,
|
||
resources: Sequence[Container],
|
||
vols: List[float],
|
||
use_channels: Optional[List[int]] = None,
|
||
flow_rates: Optional[List[Optional[float]]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
liquid_height: Optional[List[Optional[float]]] = None,
|
||
blow_out_air_volume: Optional[List[Optional[float]]] = None,
|
||
spread: Literal["wide", "tight", "custom"] = "wide",
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.dispense(
|
||
resources,
|
||
vols,
|
||
use_channels,
|
||
flow_rates,
|
||
offsets,
|
||
liquid_height,
|
||
blow_out_air_volume,
|
||
spread,
|
||
**backend_kwargs,
|
||
)
|
||
return await super().dispense(
|
||
resources,
|
||
vols,
|
||
use_channels,
|
||
flow_rates,
|
||
offsets,
|
||
liquid_height,
|
||
blow_out_air_volume,
|
||
spread,
|
||
**backend_kwargs,
|
||
)
|
||
|
||
async def transfer(
|
||
self,
|
||
source: Well,
|
||
targets: List[Well],
|
||
source_vol: Optional[float] = None,
|
||
ratios: Optional[List[float]] = None,
|
||
target_vols: Optional[List[float]] = None,
|
||
aspiration_flow_rate: Optional[float] = None,
|
||
dispense_flow_rates: Optional[List[Optional[float]]] = None,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.transfer(
|
||
source,
|
||
targets,
|
||
source_vol,
|
||
ratios,
|
||
target_vols,
|
||
aspiration_flow_rate,
|
||
dispense_flow_rates,
|
||
**backend_kwargs,
|
||
)
|
||
return await super().transfer(
|
||
source,
|
||
targets,
|
||
source_vol,
|
||
ratios,
|
||
target_vols,
|
||
aspiration_flow_rate,
|
||
dispense_flow_rates,
|
||
**backend_kwargs,
|
||
)
|
||
|
||
def use_channels(self, channels: List[int]):
|
||
if self._simulator:
|
||
self._simulate_handler.use_channels(channels)
|
||
return super().use_channels(channels)
|
||
|
||
async def pick_up_tips96(self, tip_rack: TipRack, offset: Coordinate = Coordinate.zero(), **backend_kwargs):
|
||
if self._simulator:
|
||
return await self._simulate_handler.pick_up_tips96(tip_rack, offset, **backend_kwargs)
|
||
return await super().pick_up_tips96(tip_rack, offset, **backend_kwargs)
|
||
|
||
async def drop_tips96(
|
||
self,
|
||
resource: Union[TipRack, Trash],
|
||
offset: Coordinate = Coordinate.zero(),
|
||
allow_nonzero_volume: bool = False,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.drop_tips96(resource, offset, allow_nonzero_volume, **backend_kwargs)
|
||
return await super().drop_tips96(resource, offset, allow_nonzero_volume, **backend_kwargs)
|
||
|
||
def _get_96_head_origin_tip_rack(self) -> Optional[TipRack]:
|
||
return super()._get_96_head_origin_tip_rack()
|
||
|
||
async def return_tips96(self, allow_nonzero_volume: bool = False, **backend_kwargs):
|
||
if self._simulator:
|
||
return await self._simulate_handler.return_tips96(allow_nonzero_volume, **backend_kwargs)
|
||
return await super().return_tips96(allow_nonzero_volume, **backend_kwargs)
|
||
|
||
async def discard_tips96(self, allow_nonzero_volume: bool = True, **backend_kwargs):
|
||
if self._simulator:
|
||
return await self._simulate_handler.discard_tips96(allow_nonzero_volume, **backend_kwargs)
|
||
return await super().discard_tips96(allow_nonzero_volume, **backend_kwargs)
|
||
|
||
async def aspirate96(
|
||
self,
|
||
resource: Union[Plate, Container, List[Well]],
|
||
volume: float,
|
||
offset: Coordinate = Coordinate.zero(),
|
||
flow_rate: Optional[float] = None,
|
||
blow_out_air_volume: Optional[float] = None,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.aspirate96(
|
||
resource, volume, offset, flow_rate, blow_out_air_volume, **backend_kwargs
|
||
)
|
||
return await super().aspirate96(resource, volume, offset, flow_rate, blow_out_air_volume, **backend_kwargs)
|
||
|
||
async def dispense96(
|
||
self,
|
||
resource: Union[Plate, Container, List[Well]],
|
||
volume: float,
|
||
offset: Coordinate = Coordinate.zero(),
|
||
flow_rate: Optional[float] = None,
|
||
blow_out_air_volume: Optional[float] = None,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.dispense96(
|
||
resource, volume, offset, flow_rate, blow_out_air_volume, **backend_kwargs
|
||
)
|
||
return await super().dispense96(resource, volume, offset, flow_rate, blow_out_air_volume, **backend_kwargs)
|
||
|
||
async def stamp(
|
||
self,
|
||
source: Plate,
|
||
target: Plate,
|
||
volume: float,
|
||
aspiration_flow_rate: Optional[float] = None,
|
||
dispense_flow_rate: Optional[float] = None,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.stamp(source, target, volume, aspiration_flow_rate, dispense_flow_rate)
|
||
return await super().stamp(source, target, volume, aspiration_flow_rate, dispense_flow_rate)
|
||
|
||
async def pick_up_resource(
|
||
self,
|
||
resource: Resource,
|
||
offset: Coordinate = Coordinate.zero(),
|
||
pickup_distance_from_top: float = 0,
|
||
direction: GripDirection = GripDirection.FRONT,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.pick_up_resource(
|
||
resource, offset, pickup_distance_from_top, direction, **backend_kwargs
|
||
)
|
||
return await super().pick_up_resource(resource, offset, pickup_distance_from_top, direction, **backend_kwargs)
|
||
|
||
async def move_picked_up_resource(
|
||
self,
|
||
to: Coordinate,
|
||
offset: Coordinate = Coordinate.zero(),
|
||
direction: Optional[GripDirection] = None,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_picked_up_resource(to, offset, direction, **backend_kwargs)
|
||
return await super().move_picked_up_resource(to, offset, direction, **backend_kwargs)
|
||
|
||
async def drop_resource(
|
||
self,
|
||
destination: Union[ResourceStack, ResourceHolder, Resource, Coordinate],
|
||
offset: Coordinate = Coordinate.zero(),
|
||
direction: GripDirection = GripDirection.FRONT,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.drop_resource(destination, offset, direction, **backend_kwargs)
|
||
return await super().drop_resource(destination, offset, direction, **backend_kwargs)
|
||
|
||
async def move_resource(
|
||
self,
|
||
resource: Resource,
|
||
to: Union[ResourceStack, ResourceHolder, Resource, Coordinate],
|
||
intermediate_locations: Optional[List[Coordinate]] = None,
|
||
pickup_offset: Coordinate = Coordinate.zero(),
|
||
destination_offset: Coordinate = Coordinate.zero(),
|
||
pickup_distance_from_top: float = 0,
|
||
pickup_direction: GripDirection = GripDirection.FRONT,
|
||
drop_direction: GripDirection = GripDirection.FRONT,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_resource(
|
||
resource,
|
||
to,
|
||
intermediate_locations,
|
||
pickup_offset,
|
||
destination_offset,
|
||
pickup_distance_from_top,
|
||
pickup_direction,
|
||
drop_direction,
|
||
**backend_kwargs,
|
||
)
|
||
return await super().move_resource(
|
||
resource,
|
||
to,
|
||
intermediate_locations,
|
||
pickup_offset,
|
||
destination_offset,
|
||
pickup_distance_from_top,
|
||
pickup_direction,
|
||
drop_direction,
|
||
**backend_kwargs,
|
||
)
|
||
|
||
async def move_lid(
|
||
self,
|
||
lid: Lid,
|
||
to: Union[Plate, ResourceStack, Coordinate],
|
||
intermediate_locations: Optional[List[Coordinate]] = None,
|
||
pickup_offset: Coordinate = Coordinate.zero(),
|
||
destination_offset: Coordinate = Coordinate.zero(),
|
||
pickup_direction: GripDirection = GripDirection.FRONT,
|
||
drop_direction: GripDirection = GripDirection.FRONT,
|
||
pickup_distance_from_top: float = 5.7 - 3.33,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_lid(
|
||
lid,
|
||
to,
|
||
intermediate_locations,
|
||
pickup_offset,
|
||
destination_offset,
|
||
pickup_direction,
|
||
drop_direction,
|
||
pickup_distance_from_top,
|
||
**backend_kwargs,
|
||
)
|
||
return await super().move_lid(
|
||
lid,
|
||
to,
|
||
intermediate_locations,
|
||
pickup_offset,
|
||
destination_offset,
|
||
pickup_direction,
|
||
drop_direction,
|
||
pickup_distance_from_top,
|
||
**backend_kwargs,
|
||
)
|
||
|
||
async def move_plate(
|
||
self,
|
||
plate: Plate,
|
||
to: Union[ResourceStack, ResourceHolder, Resource, Coordinate],
|
||
intermediate_locations: Optional[List[Coordinate]] = None,
|
||
pickup_offset: Coordinate = Coordinate.zero(),
|
||
destination_offset: Coordinate = Coordinate.zero(),
|
||
drop_direction: GripDirection = GripDirection.FRONT,
|
||
pickup_direction: GripDirection = GripDirection.FRONT,
|
||
pickup_distance_from_top: float = 13.2 - 3.33,
|
||
**backend_kwargs,
|
||
):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_plate(
|
||
plate,
|
||
to,
|
||
intermediate_locations,
|
||
pickup_offset,
|
||
destination_offset,
|
||
drop_direction,
|
||
pickup_direction,
|
||
pickup_distance_from_top,
|
||
**backend_kwargs,
|
||
)
|
||
return await super().move_plate(
|
||
plate,
|
||
to,
|
||
intermediate_locations,
|
||
pickup_offset,
|
||
destination_offset,
|
||
drop_direction,
|
||
pickup_direction,
|
||
pickup_distance_from_top,
|
||
**backend_kwargs,
|
||
)
|
||
|
||
def serialize(self):
|
||
if self._simulator:
|
||
self._simulate_handler.serialize()
|
||
return super().serialize()
|
||
|
||
@classmethod
|
||
def deserialize(cls, data: dict, allow_marshal: bool = False) -> LiquidHandler:
|
||
return super().deserialize(data, allow_marshal)
|
||
|
||
@classmethod
|
||
def load(cls, path: str) -> LiquidHandler:
|
||
return super().load(path)
|
||
|
||
async def prepare_for_manual_channel_operation(self, channel: int):
|
||
if self._simulator:
|
||
return await self._simulate_handler.prepare_for_manual_channel_operation(channel)
|
||
return await super().prepare_for_manual_channel_operation(channel)
|
||
|
||
async def move_channel_x(self, channel: int, x: float):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_channel_x(channel, x)
|
||
return await super().move_channel_x(channel, x)
|
||
|
||
async def move_channel_y(self, channel: int, y: float):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_channel_y(channel, y)
|
||
return await super().move_channel_y(channel, y)
|
||
|
||
async def move_channel_z(self, channel: int, z: float):
|
||
if self._simulator:
|
||
return await self._simulate_handler.move_channel_z(channel, z)
|
||
return await super().move_channel_z(channel, z)
|
||
|
||
def assign_child_resource(self, resource: Resource, location: Optional[Coordinate], reassign: bool = True):
|
||
if self._simulator:
|
||
self._simulate_handler.assign_child_resource(resource, location, reassign)
|
||
pass
|
||
|
||
async def probe_tip_presence_via_pickup(
|
||
self, tip_spots: List[TipSpot], use_channels: Optional[List[int]] = None
|
||
) -> Dict[str, bool]:
|
||
if self._simulator:
|
||
return await self._simulate_handler.probe_tip_presence_via_pickup(tip_spots, use_channels)
|
||
return await super().probe_tip_presence_via_pickup(tip_spots, use_channels)
|
||
|
||
async def probe_tip_inventory(
|
||
self,
|
||
tip_spots: List[TipSpot],
|
||
probing_fn: Optional[TipPresenceProbingMethod] = None,
|
||
use_channels: Optional[List[int]] = None,
|
||
) -> Dict[str, bool]:
|
||
if self._simulator:
|
||
return await self._simulate_handler.probe_tip_inventory(tip_spots, probing_fn, use_channels)
|
||
return await super().probe_tip_inventory(tip_spots, probing_fn, use_channels)
|
||
|
||
async def consolidate_tip_inventory(self, tip_racks: List[TipRack], use_channels: Optional[List[int]] = None):
|
||
if self._simulator:
|
||
return await self._simulate_handler.consolidate_tip_inventory(tip_racks, use_channels)
|
||
return await super().consolidate_tip_inventory(tip_racks, use_channels)
|
||
|
||
|
||
class LiquidHandlerAbstract(LiquidHandlerMiddleware):
|
||
"""Extended LiquidHandler with additional operations."""
|
||
support_touch_tip = True
|
||
|
||
def __init__(self, backend: LiquidHandlerBackend, deck: Deck, simulator: bool=False, channel_num:int = 8):
|
||
"""Initialize a LiquidHandler.
|
||
|
||
Args:
|
||
backend: Backend to use.
|
||
deck: Deck to use.
|
||
"""
|
||
self._simulator = simulator
|
||
super().__init__(backend, deck, simulator, channel_num)
|
||
|
||
@classmethod
|
||
def set_liquid(self, wells: list[Well], liquid_names: list[str], volumes: list[float]):
|
||
"""Set the liquid in a well."""
|
||
for well, liquid_name, volume in zip(wells, liquid_names, volumes):
|
||
well.set_liquids([(liquid_name, volume)]) # type: ignore
|
||
# ---------------------------------------------------------------
|
||
# REMOVE LIQUID --------------------------------------------------
|
||
# ---------------------------------------------------------------
|
||
|
||
async def create_protocol(
|
||
self,
|
||
protocol_name: str,
|
||
protocol_description: str,
|
||
protocol_version: str,
|
||
protocol_author: str,
|
||
protocol_date: str,
|
||
protocol_type: str,
|
||
none_keys: List[str] = [],
|
||
):
|
||
"""Create a new protocol with the given metadata."""
|
||
pass
|
||
|
||
async def remove_liquid(
|
||
self,
|
||
vols: List[float],
|
||
sources: Sequence[Container],
|
||
waste_liquid: Optional[Container] = None,
|
||
*,
|
||
use_channels: Optional[List[int]] = None,
|
||
flow_rates: Optional[List[Optional[float]]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
liquid_height: Optional[List[Optional[float]]] = None,
|
||
blow_out_air_volume: Optional[List[Optional[float]]] = None,
|
||
spread: Optional[Literal["wide", "tight", "custom"]] = "wide",
|
||
delays: Optional[List[int]] = None,
|
||
is_96_well: Optional[bool] = False,
|
||
top: Optional[List[float]] = None,
|
||
none_keys: List[str] = [],
|
||
):
|
||
"""A complete *remove* (aspirate → waste) operation."""
|
||
|
||
try:
|
||
if is_96_well:
|
||
pass # This mode is not verified.
|
||
else:
|
||
# 首先应该对任务分组,然后每次1个/8个进行操作处理
|
||
if len(use_channels) == 1 and self.backend.num_channels == 1:
|
||
|
||
for _ in range(len(sources)):
|
||
tip = []
|
||
for __ in range(len(use_channels)):
|
||
tip.extend(next(self.current_tip))
|
||
await self.pick_up_tips(tip)
|
||
await self.aspirate(
|
||
resources=[sources[_]],
|
||
vols=[vols[_]],
|
||
use_channels=use_channels,
|
||
flow_rates=[flow_rates[0]] if flow_rates else None,
|
||
offsets=[offsets[0]] if offsets else None,
|
||
liquid_height=[liquid_height[0]] if liquid_height else None,
|
||
blow_out_air_volume=[blow_out_air_volume[0]] if blow_out_air_volume else None,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[0])
|
||
|
||
await self.dispense(
|
||
resources=[waste_liquid],
|
||
vols=[vols[_]],
|
||
use_channels=use_channels,
|
||
flow_rates=[flow_rates[1]] if flow_rates else None,
|
||
offsets=[offsets[1]] if offsets else None,
|
||
blow_out_air_volume=[blow_out_air_volume[1]] if blow_out_air_volume else None,
|
||
liquid_height=[liquid_height[1]] if liquid_height else None,
|
||
spread=spread,
|
||
)
|
||
await self.discard_tips()
|
||
|
||
elif len(use_channels) == 8 and self.backend.num_channels == 8:
|
||
|
||
|
||
# 对于8个的情况,需要判断此时任务是不是能被8通道移液站来成功处理
|
||
if len(sources) % 8 != 0:
|
||
raise ValueError(f"Length of `sources` {len(sources)} must be a multiple of 8 for 8-channel mode.")
|
||
|
||
# 8个8个来取任务序列
|
||
|
||
for i in range(0, len(sources), 8):
|
||
tip = []
|
||
for _ in range(len(use_channels)):
|
||
tip.extend(next(self.current_tip))
|
||
await self.pick_up_tips(tip)
|
||
current_targets = waste_liquid[i:i + 8]
|
||
current_reagent_sources = sources[i:i + 8]
|
||
current_asp_vols = vols[i:i + 8]
|
||
current_dis_vols = vols[i:i + 8]
|
||
current_asp_flow_rates = flow_rates[i:i + 8] if flow_rates else [None] * 8
|
||
current_dis_flow_rates = flow_rates[-i*8-8:len(flow_rates)-i*8] if flow_rates else [None] * 8
|
||
current_asp_offset = offsets[i:i + 8] if offsets else [None] * 8
|
||
current_dis_offset = offsets[-i*8-8:len(offsets)-i*8] if offsets else [None] * 8
|
||
current_asp_liquid_height = liquid_height[i:i + 8] if liquid_height else [None] * 8
|
||
current_dis_liquid_height = liquid_height[-i*8-8:len(liquid_height)-i*8] if liquid_height else [None] * 8
|
||
current_asp_blow_out_air_volume = blow_out_air_volume[i:i + 8] if blow_out_air_volume else [None] * 8
|
||
current_dis_blow_out_air_volume = blow_out_air_volume[-i*8-8:len(blow_out_air_volume)-i*8] if blow_out_air_volume else [None] * 8
|
||
|
||
await self.aspirate(
|
||
resources=current_reagent_sources,
|
||
vols=current_asp_vols,
|
||
use_channels=use_channels,
|
||
flow_rates=current_asp_flow_rates,
|
||
offsets=current_asp_offset,
|
||
liquid_height=current_asp_liquid_height,
|
||
blow_out_air_volume=current_asp_blow_out_air_volume,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[0])
|
||
await self.dispense(
|
||
resources=current_targets,
|
||
vols=current_dis_vols,
|
||
use_channels=use_channels,
|
||
flow_rates=current_dis_flow_rates,
|
||
offsets=current_dis_offset,
|
||
liquid_height=current_dis_liquid_height,
|
||
blow_out_air_volume=current_dis_blow_out_air_volume,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.touch_tip(current_targets)
|
||
await self.discard_tips()
|
||
|
||
except Exception as e:
|
||
traceback.print_exc()
|
||
raise RuntimeError(f"Liquid addition failed: {e}") from e
|
||
|
||
# ---------------------------------------------------------------
|
||
# ADD LIQUID -----------------------------------------------------
|
||
# ---------------------------------------------------------------
|
||
|
||
async def add_liquid(
|
||
self,
|
||
asp_vols: Union[List[float], float],
|
||
dis_vols: Union[List[float], float],
|
||
reagent_sources: Sequence[Container],
|
||
targets: Sequence[Container],
|
||
*,
|
||
use_channels: Optional[List[int]] = None,
|
||
flow_rates: Optional[List[Optional[float]]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
liquid_height: Optional[List[Optional[float]]] = None,
|
||
blow_out_air_volume: Optional[List[Optional[float]]] = None,
|
||
spread: Optional[Literal["wide", "tight", "custom"]] = "wide",
|
||
is_96_well: bool = False,
|
||
delays: Optional[List[int]] = None,
|
||
mix_time: Optional[int] = None,
|
||
mix_vol: Optional[int] = None,
|
||
mix_rate: Optional[int] = None,
|
||
mix_liquid_height: Optional[float] = None,
|
||
none_keys: List[str] = [],
|
||
):
|
||
# """A complete *add* (aspirate reagent → dispense into targets) operation."""
|
||
|
||
# # try:
|
||
if is_96_well:
|
||
pass # This mode is not verified.
|
||
else:
|
||
if len(asp_vols) != len(targets):
|
||
raise ValueError(f"Length of `asp_vols` {len(asp_vols)} must match `targets` {len(targets)}.")
|
||
# 首先应该对任务分组,然后每次1个/8个进行操作处理
|
||
if len(use_channels) == 1:
|
||
for _ in range(len(targets)):
|
||
tip = []
|
||
for x in range(len(use_channels)):
|
||
tip.extend(next(self.current_tip))
|
||
await self.pick_up_tips(tip)
|
||
|
||
await self.aspirate(
|
||
resources=[reagent_sources[_]],
|
||
vols=[asp_vols[_]],
|
||
use_channels=use_channels,
|
||
flow_rates=[flow_rates[0]] if flow_rates else None,
|
||
offsets=[offsets[0]] if offsets else None,
|
||
liquid_height=[liquid_height[0]] if liquid_height else None,
|
||
blow_out_air_volume=[blow_out_air_volume[0]] if blow_out_air_volume else None,
|
||
spread=spread,
|
||
)
|
||
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[0])
|
||
await self.dispense(
|
||
resources=[targets[_]],
|
||
vols=[dis_vols[_]],
|
||
use_channels=use_channels,
|
||
flow_rates=[flow_rates[1]] if flow_rates else None,
|
||
offsets=[offsets[1]] if offsets else None,
|
||
blow_out_air_volume=[blow_out_air_volume[1]] if blow_out_air_volume else None,
|
||
liquid_height=[liquid_height[1]] if liquid_height else None,
|
||
spread=spread,
|
||
)
|
||
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.mix(
|
||
targets=[targets[_]],
|
||
mix_time=mix_time,
|
||
mix_vol=mix_vol,
|
||
offsets=offsets if offsets else None,
|
||
height_to_bottom=mix_liquid_height if mix_liquid_height else None,
|
||
mix_rate=mix_rate if mix_rate else None,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.touch_tip(targets[_])
|
||
await self.discard_tips()
|
||
|
||
elif len(use_channels) == 8:
|
||
# 对于8个的情况,需要判断此时任务是不是能被8通道移液站来成功处理
|
||
if len(targets) % 8 != 0:
|
||
raise ValueError(f"Length of `targets` {len(targets)} must be a multiple of 8 for 8-channel mode.")
|
||
|
||
for i in range(0, len(targets), 8):
|
||
tip = []
|
||
for _ in range(len(use_channels)):
|
||
tip.extend(next(self.current_tip))
|
||
await self.pick_up_tips(tip)
|
||
current_targets = targets[i:i + 8]
|
||
current_reagent_sources = reagent_sources[i:i + 8]
|
||
current_asp_vols = asp_vols[i:i + 8]
|
||
current_dis_vols = dis_vols[i:i + 8]
|
||
current_asp_flow_rates = flow_rates[i:i + 8] if flow_rates else [None] * 8
|
||
current_dis_flow_rates = flow_rates[-i*8-8:len(flow_rates)-i*8] if flow_rates else [None] * 8
|
||
current_asp_offset = offsets[i:i + 8] if offsets else [None] * 8
|
||
current_dis_offset = offsets[-i*8-8:len(offsets)-i*8] if offsets else [None] * 8
|
||
current_asp_liquid_height = liquid_height[i:i + 8] if liquid_height else [None] * 8
|
||
current_dis_liquid_height = liquid_height[-i*8-8:len(liquid_height)-i*8] if liquid_height else [None] * 8
|
||
current_asp_blow_out_air_volume = blow_out_air_volume[i:i + 8] if blow_out_air_volume else [None] * 8
|
||
current_dis_blow_out_air_volume = blow_out_air_volume[-i*8-8:len(blow_out_air_volume)-i*8] if blow_out_air_volume else [None] * 8
|
||
|
||
await self.aspirate(
|
||
resources=current_reagent_sources,
|
||
vols=current_asp_vols,
|
||
use_channels=use_channels,
|
||
flow_rates=current_asp_flow_rates,
|
||
offsets=current_asp_offset,
|
||
liquid_height=current_asp_liquid_height,
|
||
blow_out_air_volume=current_asp_blow_out_air_volume,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[0])
|
||
await self.dispense(
|
||
resources=current_targets,
|
||
vols=current_dis_vols,
|
||
use_channels=use_channels,
|
||
flow_rates=current_dis_flow_rates,
|
||
offsets=current_dis_offset,
|
||
liquid_height=current_dis_liquid_height,
|
||
blow_out_air_volume=current_dis_blow_out_air_volume,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
|
||
await self.mix(
|
||
targets=current_targets,
|
||
mix_time=mix_time,
|
||
mix_vol=mix_vol,
|
||
offsets=offsets if offsets else None,
|
||
height_to_bottom=mix_liquid_height if mix_liquid_height else None,
|
||
mix_rate=mix_rate if mix_rate else None,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.touch_tip(current_targets)
|
||
await self.discard_tips()
|
||
|
||
|
||
# except Exception as e:
|
||
# traceback.print_exc()
|
||
# raise RuntimeError(f"Liquid addition failed: {e}") from e
|
||
|
||
# ---------------------------------------------------------------
|
||
# TRANSFER LIQUID ------------------------------------------------
|
||
# ---------------------------------------------------------------
|
||
async def transfer_liquid(
|
||
self,
|
||
sources: Sequence[Container],
|
||
targets: Sequence[Container],
|
||
tip_racks: Sequence[TipRack],
|
||
*,
|
||
use_channels: Optional[List[int]] = None,
|
||
asp_vols: Union[List[float], float],
|
||
dis_vols: Union[List[float], float],
|
||
asp_flow_rates: Optional[List[Optional[float]]] = None,
|
||
dis_flow_rates: Optional[List[Optional[float]]] = None,
|
||
offsets: Optional[List[Coordinate]] = None,
|
||
touch_tip: bool = False,
|
||
liquid_height: Optional[List[Optional[float]]] = None,
|
||
blow_out_air_volume: Optional[List[Optional[float]]] = None,
|
||
spread: Literal["wide", "tight", "custom"] = "wide",
|
||
is_96_well: bool = False,
|
||
mix_stage: Optional[Literal["none", "before", "after", "both"]] = "none",
|
||
mix_times: Optional[List[int]] = None,
|
||
mix_vol: Optional[int] = None,
|
||
mix_rate: Optional[int] = None,
|
||
mix_liquid_height: Optional[float] = None,
|
||
delays: Optional[List[int]] = None,
|
||
none_keys: List[str] = [],
|
||
):
|
||
"""Transfer liquid from each *source* well/plate to the corresponding *target*.
|
||
|
||
Parameters
|
||
----------
|
||
asp_vols, dis_vols
|
||
Single volume (µL) or list matching the number of transfers.
|
||
sources, targets
|
||
Same‑length sequences of containers (wells or plates). In 96‑well mode
|
||
each must contain exactly one plate.
|
||
tip_racks
|
||
One or more TipRacks providing fresh tips.
|
||
is_96_well
|
||
Set *True* to use the 96‑channel head.
|
||
"""
|
||
|
||
|
||
if is_96_well:
|
||
pass # This mode is not verified.
|
||
else:
|
||
if len(asp_vols) != len(targets):
|
||
raise ValueError(f"Length of `asp_vols` {len(asp_vols)} must match `targets` {len(targets)}.")
|
||
|
||
# 首先应该对任务分组,然后每次1个/8个进行操作处理
|
||
if len(use_channels) == 1:
|
||
for _ in range(len(targets)):
|
||
tip = []
|
||
for ___ in range(len(use_channels)):
|
||
tip.extend(next(self.current_tip))
|
||
await self.pick_up_tips(tip)
|
||
|
||
await self.aspirate(
|
||
resources=[sources[_]],
|
||
vols=[asp_vols[_]],
|
||
use_channels=use_channels,
|
||
flow_rates=[asp_flow_rates[0]] if asp_flow_rates else None,
|
||
offsets=[offsets[0]] if offsets else None,
|
||
liquid_height=[liquid_height[0]] if liquid_height else None,
|
||
blow_out_air_volume=[blow_out_air_volume[0]] if blow_out_air_volume else None,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[0])
|
||
await self.dispense(
|
||
resources=[targets[_]],
|
||
vols=[dis_vols[_]],
|
||
use_channels=use_channels,
|
||
flow_rates=[dis_flow_rates[1]] if dis_flow_rates else None,
|
||
offsets=[offsets[1]] if offsets else None,
|
||
blow_out_air_volume=[blow_out_air_volume[1]] if blow_out_air_volume else None,
|
||
liquid_height=[liquid_height[1]] if liquid_height else None,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.mix(
|
||
targets=[targets[_]],
|
||
mix_time=mix_times,
|
||
mix_vol=mix_vol,
|
||
offsets=offsets if offsets else None,
|
||
height_to_bottom=mix_liquid_height if mix_liquid_height else None,
|
||
mix_rate=mix_rate if mix_rate else None,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.touch_tip(targets[_])
|
||
await self.discard_tips()
|
||
|
||
elif len(use_channels) == 8:
|
||
# 对于8个的情况,需要判断此时任务是不是能被8通道移液站来成功处理
|
||
if len(targets) % 8 != 0:
|
||
raise ValueError(f"Length of `targets` {len(targets)} must be a multiple of 8 for 8-channel mode.")
|
||
|
||
# 8个8个来取任务序列
|
||
|
||
for i in range(0, len(targets), 8):
|
||
# 取出8个任务
|
||
tip = []
|
||
for _ in range(len(use_channels)):
|
||
tip.extend(next(self.current_tip))
|
||
await self.pick_up_tips(tip)
|
||
current_targets = targets[i:i + 8]
|
||
current_reagent_sources = sources[i:i + 8]
|
||
current_asp_vols = asp_vols[i:i + 8]
|
||
current_dis_vols = dis_vols[i:i + 8]
|
||
current_asp_flow_rates = asp_flow_rates[i:i + 8]
|
||
current_asp_offset = offsets[i:i + 8] if offsets else [None] * 8
|
||
current_dis_offset = offsets[-i*8-8:len(offsets)-i*8] if offsets else [None] * 8
|
||
current_asp_liquid_height = liquid_height[i:i + 8] if liquid_height else [None] * 8
|
||
current_dis_liquid_height = liquid_height[-i*8-8:len(liquid_height)-i*8] if liquid_height else [None] * 8
|
||
current_asp_blow_out_air_volume = blow_out_air_volume[i:i + 8] if blow_out_air_volume else [None] * 8
|
||
current_dis_blow_out_air_volume = blow_out_air_volume[-i*8-8:len(blow_out_air_volume)-i*8] if blow_out_air_volume else [None] * 8
|
||
current_dis_flow_rates = dis_flow_rates[i:i + 8] if dis_flow_rates else [None] * 8
|
||
|
||
await self.aspirate(
|
||
resources=current_reagent_sources,
|
||
vols=current_asp_vols,
|
||
use_channels=use_channels,
|
||
flow_rates=current_asp_flow_rates,
|
||
offsets=current_asp_offset,
|
||
blow_out_air_volume=current_asp_blow_out_air_volume,
|
||
liquid_height=current_asp_liquid_height,
|
||
spread=spread,
|
||
)
|
||
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[0])
|
||
await self.dispense(
|
||
resources=current_targets,
|
||
vols=current_dis_vols,
|
||
use_channels=use_channels,
|
||
flow_rates=current_dis_flow_rates,
|
||
offsets=current_dis_offset,
|
||
blow_out_air_volume=current_dis_blow_out_air_volume,
|
||
liquid_height=current_dis_liquid_height,
|
||
spread=spread,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
|
||
await self.mix(
|
||
targets=current_targets,
|
||
mix_time=mix_times,
|
||
mix_vol=mix_vol,
|
||
offsets=offsets if offsets else None,
|
||
height_to_bottom=mix_liquid_height if mix_liquid_height else None,
|
||
mix_rate=mix_rate if mix_rate else None,
|
||
)
|
||
if delays is not None:
|
||
await self.custom_delay(seconds=delays[1])
|
||
await self.touch_tip(current_targets)
|
||
await self.discard_tips()
|
||
|
||
# except Exception as e:
|
||
# traceback.print_exc()
|
||
# raise RuntimeError(f"Liquid addition failed: {e}") from e
|
||
|
||
|
||
# ---------------------------------------------------------------
|
||
# Helper utilities
|
||
# ---------------------------------------------------------------
|
||
|
||
async def custom_delay(self, seconds=0, msg=None):
|
||
"""
|
||
seconds: seconds to wait
|
||
msg: information to be printed
|
||
"""
|
||
if seconds != None and seconds > 0:
|
||
if msg:
|
||
print(f"Waiting time: {msg}")
|
||
print(f"Current time: {time.strftime('%H:%M:%S')}")
|
||
print(f"Time to finish: {time.strftime('%H:%M:%S', time.localtime(time.time() + seconds))}")
|
||
await asyncio.sleep(seconds)
|
||
if msg:
|
||
print(f"Done: {msg}")
|
||
print(f"Current time: {time.strftime('%H:%M:%S')}")
|
||
|
||
async def touch_tip(self, targets: Sequence[Container]):
|
||
|
||
"""Touch the tip to the side of the well."""
|
||
|
||
if not self.support_touch_tip:
|
||
return
|
||
await self.aspirate(
|
||
resources=[targets],
|
||
vols=[0],
|
||
use_channels=None,
|
||
flow_rates=None,
|
||
offsets=[Coordinate(x=-targets.get_size_x() / 2, y=0, z=0)],
|
||
liquid_height=None,
|
||
blow_out_air_volume=None,
|
||
)
|
||
# await self.custom_delay(seconds=1) # In the simulation, we do not need to wait
|
||
await self.aspirate(
|
||
resources=[targets],
|
||
vols=[0],
|
||
use_channels=None,
|
||
flow_rates=None,
|
||
offsets=[Coordinate(x=targets.get_size_x() / 2, y=0, z=0)],
|
||
liquid_height=None,
|
||
blow_out_air_volume=None,
|
||
)
|
||
|
||
async def mix(
|
||
self,
|
||
targets: Sequence[Container],
|
||
mix_time: int = None,
|
||
mix_vol: Optional[int] = None,
|
||
height_to_bottom: Optional[float] = None,
|
||
offsets: Optional[Coordinate] = None,
|
||
mix_rate: Optional[float] = None,
|
||
none_keys: List[str] = [],
|
||
):
|
||
if mix_time is None: # No mixing required
|
||
return
|
||
"""Mix the liquid in the target wells."""
|
||
for _ in range(mix_time):
|
||
await self.aspirate(
|
||
resources=[targets],
|
||
vols=[mix_vol],
|
||
flow_rates=[mix_rate] if mix_rate else None,
|
||
offsets=[offsets] if offsets else None,
|
||
liquid_height=[height_to_bottom] if height_to_bottom else None,
|
||
)
|
||
await self.custom_delay(seconds=1)
|
||
await self.dispense(
|
||
resources=[targets],
|
||
vols=[mix_vol],
|
||
flow_rates=[mix_rate] if mix_rate else None,
|
||
offsets=[offsets] if offsets else None,
|
||
liquid_height=[height_to_bottom] if height_to_bottom else None,
|
||
)
|
||
|
||
def iter_tips(self, tip_racks: Sequence[TipRack]) -> Iterator[Resource]:
|
||
"""Yield tips from a list of TipRacks one-by-one until depleted."""
|
||
for rack in tip_racks:
|
||
for tip in rack:
|
||
yield tip
|
||
raise RuntimeError("Out of tips!")
|
||
|
||
def set_tiprack(self, tip_racks: Sequence[TipRack]):
|
||
"""Set the tip racks for the liquid handler."""
|
||
|
||
self.tip_racks = tip_racks
|
||
tip_iter = self.iter_tips(tip_racks)
|
||
self.current_tip = tip_iter
|
||
|
||
async def move_to(self, well: Well, dis_to_top: float = 0, channel: int = 0):
|
||
"""
|
||
Move a single channel to a specific well with a given z-height.
|
||
|
||
Parameters
|
||
----------
|
||
well : Well
|
||
The target well.
|
||
dis_to_top : float
|
||
Height in mm to move to relative to the well top.
|
||
channel : int
|
||
Pipetting channel to move (default: 0).
|
||
"""
|
||
await self.prepare_for_manual_channel_operation(channel=channel)
|
||
abs_loc = well.get_absolute_location()
|
||
well_height = well.get_absolute_size_z()
|
||
await self.move_channel_x(channel, abs_loc.x)
|
||
await self.move_channel_y(channel, abs_loc.y)
|
||
await self.move_channel_z(channel, abs_loc.z + well_height + dis_to_top)
|