Ready for open source (#47)

* Create app/main API

* create example device

* create ROS backend and example device SDK Wrapper

* Add ROS host and host starting from app.py

* Add gripper device and mock implementation

* add "status_types" & "action_types" to ROS device decorator

* add ActionServer debug example

* [bugfix] complete mock gripper example

* ROS Backend Host for Device action calling and Resource management

* add conda/mamba ENV file

* add host_node communication with app/main.py

* add action message value mappings and converters

* Update ilabos.yaml

* Update issue templates

* example devices.json and resources.json

* Fix Device wrapper to use async property and actions (#7)

* Fix Device wrapper to use async property and actions

* Resolve #1 : support async get methods and actions. Give new examples.

* add both sync/async GRBL controller SDK

* 2 call device actions from appmainpy api to ros hostpy (#8)

* feature: add job

* fix:node start

* feature:add get job status

* fix:get device

* clean

* Resolve #5 device connection diagram and workflow compilation support (#9)

* add syringe pump device and its compilation using device connection diagram

* add RunzeSyringePump real device with ROS2 example

* Prototype machine with 1 pump and 1 CNC

* add ROS2ProtocolNode and related functions

* add ilabos_msgs (to use PumpTransfer action)

* add example device connection graph

* refactor protocol_node code into separate file

* add ROS2SerialNode

* add SerialCommand srv in ilabos_msgs

* add pump_protocol example, and fix bugs

* [fix] serial service: avoid async service deadlock by directly call serial `send_command`

* use SendCmd instead of SingleJointPosition for valve control

* initialize device connection graph when server starts

* Fix #5: async workflow execution (#16)

* add rclpyx and protocol example for async-native workflow

* use async in ROS2ProtocolNode, and host initialization

* add examples of "ros-async" protocol implementation, and `run_in_event_loop` for using native async functions

* use "ros-async" in protocols and device nodes

* fix pump_protocol: push to 0.0 μL

* Envs, docs, and conda recipes (#19)

* update ENV: use python 3.11 and deprecate ros-humble-gazebo-ros

* add ilabos-msgs conda recipe

* Update ilabos.yaml

* fix recipe and env yaml

* Add sphinx docs

* add aichemeco

* add bioyong

* add bioyong

* Support XDL devices & protocols (#20)

* [Feature] support multiple protocols in a single ProtocolNode

* add Junjie's code

* Support "Clean" protocol

* Update Grignard_flow_batchreact_single_pumpvalve.json

* test_grignard_add

* add stir device node and example

* Update device_node.py

add print_publish flag to control the node's log output

* NH4Cl_add

* add "HeaterStirrer" device and "HeatChill" action

* add wait time after each pump action for equilibration

* fix stir

* add Separate protocol

* Refactor Separator device and Stir action

* add rotavap_node

* fix stir

* add chiller node

* Move rinsings into PumpTransfer

* Fix SeparateProtocol under refactored Separator device and Stir action

* Supports automatically add new protocol action_types

* fix PumpTransfer protocol because of rinsing

* Add Rotavap and Chiller devices

* fix SeparateProtocol

* add EvaporateProtocol

* add rotavap devices config

* fix HeaterStirrer and SeparatorController IO

* Fix automatically add new protocol action_types

* Add HeaterStirrer and SeparatorController device config

* fix pump protocols

* Fix Evaporate action

* Update evaporate_protocol.py

* add temp_sensor node and add function remap

* update docs

---------

Co-authored-by: 王俊杰 <1800011822@pku.edu.cn>
Co-authored-by: q434343 <554662886@qq.com>

* fix aichemeco

* [Bugfix] fix Windows conda packaging

* add file upload api

* update dependencies: force to use 3.11 and remove conflict in WIN64 and OSX64

* update dependencies: force to use 3.11 and remove conflict in WIN64 and OSX64

* Create aichemeco_simple.py

* fix

* update

* add aichemeco file

* MQTT [1/2]: action start (#25)

* add mq

* fix

* clean

* add class

* fix excel

* update bioyong

* add api

* fix

---------

Co-authored-by: caok@dp.tech <xiaoyeqiannian@163.com>

* motor & grasp

* Add Grasp motor support and enhance EleGripper class

- Introduced a new motor configuration for Grasp in sjtu.json.
- Updated EleGripper class to inherit from UniversalDriver and added status property.
- Implemented move_and_rotate method for coordinated movement.
- Adjusted threading logic in EleGripper initialization.
- Registered Grasp motor in ROS2 device node configuration.

This commit enhances the motor control capabilities by integrating the Grasp motor and improving the existing EleGripper functionality.

* fix read data lenth

* Update Grasp.py

* MQTT (2/2): publish Device Status, Action Feedback & Results (#27)

* Add bridges in HostNode for device_status publishing

* Add "bridges" selection (fastapi & mqtt) when app start

* add MQ feedback & result publisher, and fix message converter

* fix UUID converting between ROS and MQ

* lint api model.py

* Continuous controllers: PID, MPC, custom controllers etc. (#23)

* add controller config & wrapper

* add controller setup at app.main

* control loop example

* fix com port

* add agv , ur_arm and raman

* MQTT (3/4): Unified Resources and Sync when starting the server (#28)

* update http upload api

* generate uuid when init device

* example resource json

* fix

* add new example full-content json (device, resource, graph)

* fix full-content json and related reading code

* fix

* add json_schema when initialize resources

* fix

* update schema

* refactor heaterstirrer.dalong

* fix

* fix refactor heaterstirrer.dalong

* refactor syringepump.runze: use ml instead of μL

* Update ilabos/ros/host.py

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: 王俊杰 <1800011822@pku.edu.cn>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

* Distributed initialization with self-organizing network (#29)

* add distributed launching option "--without_host"

* fix

---------

Co-authored-by: 王俊杰 <1800011822@pku.edu.cn>

* Refactor Workstation: Add resource service and tracking (#30)

* move ilabos/ros/rpc to ilabos/device_comms/rpc, and merge bioyond/aichemeco files under /devices

* add Resource srv and message_converter

* move graphio to ilabos/resources

* refactor resources type conversion

* add resource clients in device_node

* add mock resources service

* pass Gripper1 resource test

* update http resource services

* add AGV compile function

* add AGV transfer protocol

* update full mock_gripper edit_id example

* update full mock_gripper edit_id example

* get and update resource also in protocol_node

* mock resource update in AichemecoHiwo

* Create HT_hiwo.json

* add children in resources

* bugfixes

* fix rpc

* add Revvity winprep

---------

Co-authored-by: wjjxxx <43375851+wjjxxx@users.noreply.github.com>
Co-authored-by: 3218923350 <105201755+3218923350@users.noreply.github.com>

* Distributed launch (2/2): distributed resource create (#32)

* add resource_add request to host for slave mode

* add AGV

* fix protocol resources

* optimize host callbacks

* bugfixes

* add revvity registry

---------

Co-authored-by: 王俊杰 <1800011822@pku.edu.cn>
Co-authored-by: wjjxxx <43375851+wjjxxx@users.noreply.github.com>

* Refactor Driver Files Structure (#33)

* Integration with pywinauto & recorder
Added execute run and initialize procdure

* 酶标仪状态检测、使用示例,整体流程待测试

* nivo ready version

* Add HPLC driver and example script

- Introduced HPLCDriver class for managing HPLC device status and operations.
- Implemented device status monitoring and command execution via ROS2 actions.
- Added example script (hplc.py) demonstrating how to run commands on the HPLC device.
- Created PlayerUtil and UniversalDriver classes for shared functionality across devices.
- Refactored NivoDriver to utilize the new UniversalDriver structure.
- Enhanced error handling and process management in the NivoDriver.

* 修复start的错误定位

* hplc tested ok

* relative path to build msgs

* template_driver & jiageng devices

* fetch correct status type and action type

* fix mtype fetch

* gpc bus integration

* ilab build

* remove chs

* recipe rename

* modbus update 1

* json available

* hplc & modbus rewrite

* Update AgilentHPLC.py

hplc datafile reader

* move ilabos/ros/rpc to ilabos/device_comms/rpc, and merge bioyond/aichemeco files under /devices

* modbus分设备

* gpc

* gpc 2

* fix address

* default register node

* fix MainScreenGPC

* add Resource srv and message_converter

* move graphio to ilabos/resources

* refactor resources type conversion

* add resource clients in device_node

* add mock resources service

* pass Gripper1 resource test

* update http resource services

* add AGV compile function

* add AGV transfer protocol

* update recipe.yaml

* update full mock_gripper edit_id example

* update full mock_gripper edit_id example

* get and update resource also in protocol_node

* mock resource update in AichemecoHiwo

* feat: add other jiageng PLC device code

* ilabos compile

* correct format

* correct recipe format

* correct setup.py format

* remove unnecessary files

* remove unnecessary files

* Create HT_hiwo.json

* add children in resources

* hplc support sample_id

* correct hplc sample_id

* correct hplc sample_id

* hplc upload

* fix type hint

* oss upload tested ver

* recipe yaml fix for linux

* update installation yaml

* refactor: moved all driver files according to its feat

* merge main to dev

---------

Co-authored-by: 王俊杰 <2201110460@stu.pku.edu.cn>
Co-authored-by: Junhan Chang <changjh@pku.edu.cn>
Co-authored-by: jiawei <miaojiawei@dp.tech>

* add: NMR LH and RU device control (#34)

* Add Registry for device drivers and Support GraphML (#35)

* read chemputer graphml

* read graphml in app/main

* add devices in ros/devices

* add schema for devices

* read registry directory and initialize when entry from main

* Delete devices.py

* Update add_protocol.md

* delete unecessary files

* feat: 2278 devices registry yaml (#36)

* read chemputer graphml

* read graphml in app/main

* add devices in ros/devices

* add schema for devices

* read registry directory and initialize when entry from main

* Delete devices.py

* add: NMR LH and RU device control

* fix: modify jiageng devices registry

---------

Co-authored-by: Junhan Chang <changjh@pku.edu.cn>

* Device/Resource Registry and GraphML support (#37)

* add resource type conversion to PLR

* add resource registry and test

* add docs

* fix registry

* add solenoid_valve_mock, its registry and test

* fix registry for directly using examples

* add EvacuateAndRefillProtocol and testcases

* allow function sequence call in ACTION

* add read & write & extra_info for hardware_interface

* Update device_node.py

* add solenoid valve

* add doc developer guide yaml

* fixes for starting IK station

* add graphml grouping parser

* fix graphml grouping parser

* add communication edge parser

* fix io solenoid valve

* Update .gitignore

* Update plates.yaml

---------

Co-authored-by: ColumbiaCC <2100011801@stu.pku.edu.cn>

* Uni-Lab Doc v0.2 (#39)

* add Uni-Lab docs

* change doc name

* Dev (#41)

* Integration with pywinauto & recorder
Added execute run and initialize procdure

* 酶标仪状态检测、使用示例,整体流程待测试

* nivo ready version

* Add HPLC driver and example script

- Introduced HPLCDriver class for managing HPLC device status and operations.
- Implemented device status monitoring and command execution via ROS2 actions.
- Added example script (hplc.py) demonstrating how to run commands on the HPLC device.
- Created PlayerUtil and UniversalDriver classes for shared functionality across devices.
- Refactored NivoDriver to utilize the new UniversalDriver structure.
- Enhanced error handling and process management in the NivoDriver.

* 修复start的错误定位

* hplc tested ok

* relative path to build msgs

* template_driver & jiageng devices

* fetch correct status type and action type

* fix mtype fetch

* gpc bus integration

* ilab build

* remove chs

* recipe rename

* modbus update 1

* json available

* hplc & modbus rewrite

* Update AgilentHPLC.py

hplc datafile reader

* move ilabos/ros/rpc to ilabos/device_comms/rpc, and merge bioyond/aichemeco files under /devices

* modbus分设备

* gpc

* gpc 2

* fix address

* default register node

* fix MainScreenGPC

* add Resource srv and message_converter

* move graphio to ilabos/resources

* refactor resources type conversion

* add resource clients in device_node

* add mock resources service

* pass Gripper1 resource test

* update http resource services

* add AGV compile function

* add AGV transfer protocol

* update recipe.yaml

* update full mock_gripper edit_id example

* update full mock_gripper edit_id example

* get and update resource also in protocol_node

* mock resource update in AichemecoHiwo

* feat: add other jiageng PLC device code

* ilabos compile

* correct format

* correct recipe format

* correct setup.py format

* remove unnecessary files

* remove unnecessary files

* Create HT_hiwo.json

* add children in resources

* hplc support sample_id

* correct hplc sample_id

* correct hplc sample_id

* hplc upload

* fix type hint

* oss upload tested ver

* recipe yaml fix for linux

* update installation yaml

* refactor: moved all driver files according to its feat

* merge main to dev

* add HPLC registry and json

* 升级 ros2-distro-mutex 依赖版本至 0.6

* 修改 ros2-distro-mutex 依赖版本为通配符匹配

* 更新 ros-humble-ilabos-msgs 依赖为 robostack-humble 命名空间

* add resource type conversion to PLR

* add resource registry and test

* feat: 更新oss上传

* fix device id

* add docs

* fix registry

* add solenoid_valve_mock, its registry and test

* fix registry for directly using examples

* add EvacuateAndRefillProtocol and testcases

* allow function sequence call in ACTION

* add read & write & extra_info for hardware_interface

* Update device_node.py

* add solenoid valve

* add doc developer guide yaml

* use robostack-staging

* rclpy version test

* lower rclpy

* ensure 0.6* env

* fixes for starting IK station

* add graphml grouping parser

* fix graphml grouping parser

* add communication edge parser

* fix io solenoid valve

* Update .gitignore

* Update plates.yaml

* Feature/device node later init (#40)

* 修改config路径,方便后续打包
增加device_node打印

* 支持plr序列化/init创建

* 统一命名

* import mgr
logger optimize
banner print

* 日志OK

* fix unicorn frame

* banner print

* correct import format

* file path changes

* 取消后补全,在加载设备的时候直接替换

* converter update

* web page update

* 在线device更新,node继承替换

* 修复动作、状态的类型缺失 和 命令提示

* web功能实现结束

* host节点更改完成
新增status时间戳管理
新增每10s动态发现其他node

* ros2类型的节点也应该被包一次

* 修复类型提示

* websocket 动态显示状态

* add workflow & book theme for docs

* add workflow & book theme for docs

* fix workflow build

* fix workflow build

* 理清启动关系

* stm32 example

* mac . name

* device_instance device_cls

* 新增config添加方式
更新mqtt提示

* plr test

* 移动is_host_mode
新增slave_no_host

* 确保config优先修改生效

* fix graph io

* 支持带参数传入

* 支持物料解析

* 支持物料解析

* device为空的时候不进行绑定或初始化

* protocol node new

* protocol node runnable

* protocol node runnable

---------

Co-authored-by: 王俊杰 <2201110460@stu.pku.edu.cn>
Co-authored-by: Junhan Chang <changjh@pku.edu.cn>
Co-authored-by: jiawei <miaojiawei@dp.tech>
Co-authored-by: ColumbiaCC <2100011801@stu.pku.edu.cn>

* Dev (#45)

* Integration with pywinauto & recorder
Added execute run and initialize procdure

* 酶标仪状态检测、使用示例,整体流程待测试

* nivo ready version

* Add HPLC driver and example script

- Introduced HPLCDriver class for managing HPLC device status and operations.
- Implemented device status monitoring and command execution via ROS2 actions.
- Added example script (hplc.py) demonstrating how to run commands on the HPLC device.
- Created PlayerUtil and UniversalDriver classes for shared functionality across devices.
- Refactored NivoDriver to utilize the new UniversalDriver structure.
- Enhanced error handling and process management in the NivoDriver.

* 修复start的错误定位

* hplc tested ok

* relative path to build msgs

* template_driver & jiageng devices

* fetch correct status type and action type

* fix mtype fetch

* gpc bus integration

* ilab build

* remove chs

* recipe rename

* modbus update 1

* json available

* hplc & modbus rewrite

* Update AgilentHPLC.py

hplc datafile reader

* move ilabos/ros/rpc to ilabos/device_comms/rpc, and merge bioyond/aichemeco files under /devices

* modbus分设备

* gpc

* gpc 2

* fix address

* default register node

* fix MainScreenGPC

* add Resource srv and message_converter

* move graphio to ilabos/resources

* refactor resources type conversion

* add resource clients in device_node

* add mock resources service

* pass Gripper1 resource test

* update http resource services

* add AGV compile function

* add AGV transfer protocol

* update recipe.yaml

* update full mock_gripper edit_id example

* update full mock_gripper edit_id example

* get and update resource also in protocol_node

* mock resource update in AichemecoHiwo

* feat: add other jiageng PLC device code

* ilabos compile

* correct format

* correct recipe format

* correct setup.py format

* remove unnecessary files

* remove unnecessary files

* Create HT_hiwo.json

* add children in resources

* hplc support sample_id

* correct hplc sample_id

* correct hplc sample_id

* hplc upload

* fix type hint

* oss upload tested ver

* recipe yaml fix for linux

* update installation yaml

* refactor: moved all driver files according to its feat

* merge main to dev

* add HPLC registry and json

* 升级 ros2-distro-mutex 依赖版本至 0.6

* 修改 ros2-distro-mutex 依赖版本为通配符匹配

* 更新 ros-humble-ilabos-msgs 依赖为 robostack-humble 命名空间

* add resource type conversion to PLR

* add resource registry and test

* feat: 更新oss上传

* fix device id

* add docs

* fix registry

* add solenoid_valve_mock, its registry and test

* fix registry for directly using examples

* add EvacuateAndRefillProtocol and testcases

* allow function sequence call in ACTION

* add read & write & extra_info for hardware_interface

* Update device_node.py

* add solenoid valve

* add doc developer guide yaml

* use robostack-staging

* rclpy version test

* lower rclpy

* ensure 0.6* env

* fixes for starting IK station

* add graphml grouping parser

* fix graphml grouping parser

* add communication edge parser

* fix io solenoid valve

* Update .gitignore

* Update plates.yaml

* Feature/device node later init (#40)

* 修改config路径,方便后续打包
增加device_node打印

* 支持plr序列化/init创建

* 统一命名

* import mgr
logger optimize
banner print

* 日志OK

* fix unicorn frame

* banner print

* correct import format

* file path changes

* 取消后补全,在加载设备的时候直接替换

* converter update

* web page update

* 在线device更新,node继承替换

* 修复动作、状态的类型缺失 和 命令提示

* web功能实现结束

* host节点更改完成
新增status时间戳管理
新增每10s动态发现其他node

* ros2类型的节点也应该被包一次

* 修复类型提示

* websocket 动态显示状态

* add workflow & book theme for docs

* add workflow & book theme for docs

* fix workflow build

* fix workflow build

* 理清启动关系

* stm32 example

* mac . name

* device_instance device_cls

* 新增config添加方式
更新mqtt提示

* plr test

* 移动is_host_mode
新增slave_no_host

* 确保config优先修改生效

* fix graph io

* 支持带参数传入

* 支持物料解析

* 支持物料解析

* device为空的时候不进行绑定或初始化

* protocol node new

* protocol node runnable

* protocol node runnable

* Feature/device node later init (#42)

* 修改config路径,方便后续打包
增加device_node打印

* 支持plr序列化/init创建

* 统一命名

* import mgr
logger optimize
banner print

* 日志OK

* fix unicorn frame

* banner print

* correct import format

* file path changes

* 取消后补全,在加载设备的时候直接替换

* converter update

* web page update

* 在线device更新,node继承替换

* 修复动作、状态的类型缺失 和 命令提示

* web功能实现结束

* host节点更改完成
新增status时间戳管理
新增每10s动态发现其他node

* ros2类型的节点也应该被包一次

* 修复类型提示

* websocket 动态显示状态

* add workflow & book theme for docs

* add workflow & book theme for docs

* fix workflow build

* fix workflow build

* 理清启动关系

* stm32 example

* mac . name

* device_instance device_cls

* 新增config添加方式
更新mqtt提示

* plr test

* 移动is_host_mode
新增slave_no_host

* 确保config优先修改生效

* fix graph io

* 支持带参数传入

* 支持物料解析

* 支持物料解析

* device为空的时候不进行绑定或初始化

* protocol node new

* protocol node runnable

* protocol node runnable

* action

* plr suc

* plr suc!!

* plr suc!!

* plr suc!!

* plr msgs

* Feature/device node later init (#43)

* 修改config路径,方便后续打包
增加device_node打印

* 支持plr序列化/init创建

* 统一命名

* import mgr
logger optimize
banner print

* 日志OK

* fix unicorn frame

* banner print

* correct import format

* file path changes

* 取消后补全,在加载设备的时候直接替换

* converter update

* web page update

* 在线device更新,node继承替换

* 修复动作、状态的类型缺失 和 命令提示

* web功能实现结束

* host节点更改完成
新增status时间戳管理
新增每10s动态发现其他node

* ros2类型的节点也应该被包一次

* 修复类型提示

* websocket 动态显示状态

* add workflow & book theme for docs

* add workflow & book theme for docs

* fix workflow build

* fix workflow build

* 理清启动关系

* stm32 example

* mac . name

* device_instance device_cls

* 新增config添加方式
更新mqtt提示

* plr test

* 移动is_host_mode
新增slave_no_host

* 确保config优先修改生效

* fix graph io

* 支持带参数传入

* 支持物料解析

* 支持物料解析

* device为空的时候不进行绑定或初始化

* protocol node new

* protocol node runnable

* protocol node runnable

* action

* plr suc

* plr suc!!

* plr suc!!

* plr suc!!

* plr msgs

* plr

* action

* plr reg fix

* Feature/device node later init (#44)

* 修改config路径,方便后续打包
增加device_node打印

* 支持plr序列化/init创建

* 统一命名

* import mgr
logger optimize
banner print

* 日志OK

* fix unicorn frame

* banner print

* correct import format

* file path changes

* 取消后补全,在加载设备的时候直接替换

* converter update

* web page update

* 在线device更新,node继承替换

* 修复动作、状态的类型缺失 和 命令提示

* web功能实现结束

* host节点更改完成
新增status时间戳管理
新增每10s动态发现其他node

* ros2类型的节点也应该被包一次

* 修复类型提示

* websocket 动态显示状态

* add workflow & book theme for docs

* add workflow & book theme for docs

* fix workflow build

* fix workflow build

* 理清启动关系

* stm32 example

* mac . name

* device_instance device_cls

* 新增config添加方式
更新mqtt提示

* plr test

* 移动is_host_mode
新增slave_no_host

* 确保config优先修改生效

* fix graph io

* 支持带参数传入

* 支持物料解析

* 支持物料解析

* device为空的时候不进行绑定或初始化

* protocol node new

* protocol node runnable

* protocol node runnable

* action

* plr suc

* plr suc!!

* plr suc!!

* plr suc!!

* plr msgs

* plr

* fix convert error
fix async logic error
added async error print

* new device test

* test resource add

* test resource add

* test resource add

* test resource add

* local env setup

* node type fix
temp fix root_node error
fix convert res from type error

* resource tracker

* fix bug from qhh

* fix bug from qhh

* fix bug from qhh

* fix bug from qhh

* refactor MQTT client logging and connection handling; update group ID in config

* driver_params allow empty

* allow other init param

* fix driver param and enhance type hint

* refactor MQConfig to use double quotes for string literals

* fix wrong function calling

* fix wrong function calling

* fix log for mac

* fix networkx compatibility

* add mqtt loggers

* add action to jsonschema converter

* random client id

* type converter & registry

* correct conversion

* fix action publish only from discovered devices

* add "Bio" tag for action doc generation

* 改进module提示

* Fix doc

* mqtt不连接也可用
性价样例提示

* add docs

* 更新plr test案例

* Update intro.md

* 更新有机案例

* skip

---------

Co-authored-by: Harvey Que <Q-Query@outlook.com>
Co-authored-by: Junhan Chang <1700011741@pku.edu.cn>

---------

Co-authored-by: 王俊杰 <2201110460@stu.pku.edu.cn>
Co-authored-by: Junhan Chang <changjh@pku.edu.cn>
Co-authored-by: jiawei <miaojiawei@dp.tech>
Co-authored-by: ColumbiaCC <2100011801@stu.pku.edu.cn>
Co-authored-by: Harvey Que <Q-Query@outlook.com>
Co-authored-by: Junhan Chang <1700011741@pku.edu.cn>

* Canonicalize before Open Source (#46)

* big big refactor try01

* refactor 02

---------

Co-authored-by: ck <xiaoyeqiannian@163.com>
Co-authored-by: 王俊杰 <1800011822@pku.edu.cn>
Co-authored-by: q434343 <554662886@qq.com>
Co-authored-by: Xuwznln <xuwznln@gmail.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: wjjxxx <43375851+wjjxxx@users.noreply.github.com>
Co-authored-by: 3218923350 <105201755+3218923350@users.noreply.github.com>
Co-authored-by: Xuwznln <1023025701@qq.com>
Co-authored-by: 王俊杰 <2201110460@stu.pku.edu.cn>
Co-authored-by: jiawei <miaojiawei@dp.tech>
Co-authored-by: Jiawei <91898272+jiawei723@users.noreply.github.com>
Co-authored-by: ColumbiaCC <2100011801@stu.pku.edu.cn>
Co-authored-by: Harvey Que <Q-Query@outlook.com>
This commit is contained in:
Junhan Chang
2025-04-17 15:09:58 +08:00
committed by GitHub
parent 7ccb425e39
commit a62a695812
266 changed files with 40772 additions and 2 deletions

View File

View File

@@ -0,0 +1,9 @@
io_snrd:
class:
module: unilabos.device_comms.SRND_16_IO:SRND_16_IO
type: python
hardware_interface:
name: modbus_client
extra_info: address
read: read_io_coil
write: write_io_coil

View File

@@ -0,0 +1,6 @@
serial:
class:
module: unilabos.ros.nodes.presets:ROS2SerialNode
type: ros2
schema:
properties: {}

View File

@@ -0,0 +1,23 @@
# 光学表征设备:红外、紫外可见、拉曼等
raman_home_made:
class:
module: unilabos.devices.raman_uv.home_made_raman:RamanObj
type: python
status_types:
status: String
action_value_mappings:
raman_cmd:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success
schema:
properties:
status:
type: string
required:
- status
additionalProperties: false
type: object

View File

@@ -0,0 +1,189 @@
liquid_handler:
class:
module: pylabrobot.liquid_handling:LiquidHandler
type: python
status_types:
name: String
action_value_mappings:
aspirate:
type: LiquidHandlerAspirate
goal:
resources: resources
vols: vols
use_channels: use_channels
flow_rates: flow_rates
end_delay: end_delay
offsets: offsets
liquid_height: liquid_height
blow_out_air_volume: blow_out_air_volume
feedback: {}
result:
name: name
discard_tips:
type: LiquidHandlerDiscardTips
goal:
use_channels: use_channels
feedback: {}
result:
name: name
dispense:
type: LiquidHandlerDispense
goal:
resources: resources
vols: vols
use_channels: use_channels
flow_rates: flow_rates
offsets: offsets
blow_out_air_volume: blow_out_air_volume
spread: spread
feedback: {}
result:
name: name
drop_tips:
type: LiquidHandlerDropTips
goal:
tip_spots: tip_spots
use_channels: use_channels
offsets: offsets
allow_nonzero_volume: allow_nonzero_volume
feedback: {}
result:
name: name
drop_tips96:
type: LiquidHandlerDropTips96
goal:
tip_rack: tip_rack
offset: offset
allow_nonzero_volume: allow_nonzero_volume
feedback: {}
result:
name: name
move_lid:
type: LiquidHandlerMoveLid
goal:
lid: lid
to: to
intermediate_locations: intermediate_locations
resource_offset: resource_offset
destination_offset: destination_offset
pickup_direction: pickup_direction
drop_direction: drop_direction
get_direction: get_direction
put_direction: put_direction
pickup_distance_from_top: pickup_distance_from_top
feedback: {}
result:
name: name
move_plate:
type: LiquidHandlerMovePlate
goal:
plate: plate
to: to
intermediate_locations: intermediate_locations
resource_offset: resource_offset
pickup_offset: pickup_offset
destination_offset: destination_offset
pickup_direction: pickup_direction
drop_direction: drop_direction
get_direction: get_direction
put_direction: put_direction
feedback: {}
result:
name: name
move_resource:
type: LiquidHandlerMoveResource
goal:
resource: resource
to: to
intermediate_locations: intermediate_locations
resource_offset: resource_offset
destination_offset: destination_offset
pickup_distance_from_top: pickup_distance_from_top
pickup_direction: pickup_direction
drop_direction: drop_direction
get_direction: get_direction
put_direction: put_direction
feedback: {}
result:
name: name
pick_up_tips:
type: LiquidHandlerPickUpTips
goal:
tip_spots: tip_spots
use_channels: use_channels
offsets: offsets
feedback: {}
result:
name: name
pick_up_tips96:
type: LiquidHandlerPickUpTips96
goal:
tip_rack: tip_rack
offset: offset
feedback: {}
result:
name: name
return_tips:
type: LiquidHandlerReturnTips
goal:
use_channels: use_channels
allow_nonzero_volume: allow_nonzero_volume
feedback: {}
result:
name: name
return_tips96:
type: LiquidHandlerReturnTips96
goal:
allow_nonzero_volume: allow_nonzero_volume
feedback: {}
result:
name: name
stamp:
type: LiquidHandlerStamp
goal:
source: source
target: target
volume: volume
aspiration_flow_rate: aspiration_flow_rate
dispense_flow_rate: dispense_flow_rate
feedback: {}
result:
name: name
transfer:
type: LiquidHandlerTransfer
goal:
source: source
targets: targets
source_vol: source_vol
ratios: ratios
target_vols: target_vols
aspiration_flow_rate: aspiration_flow_rate
dispense_flow_rates: dispense_flow_rates
schema:
type: object
properties:
status:
type: string
description: 液体处理仪器当前状态
required:
- status
additionalProperties: false
liquid_handler.revvity:
class:
module: unilabos.devices.liquid_handling.revvity:Revvity
type: python
status_types:
status: String
action_value_mappings:
run:
type: WorkStationRun
goal:
wf_name: file_path
params: params
resource: resource
feedback:
status: status
result:
success: success

View File

@@ -0,0 +1,71 @@
separator.homemade:
class:
module: unilabos.devices.separator.homemade_grbl_conductivity:Separator_Controller
type: python
status_types:
sensordata: Float64
status: String
action_value_mappings:
stir:
type: Stir
goal:
stir_time: stir_time,
stir_speed: stir_speed
settling_time": settling_time
feedback:
status: status
result:
success: success
valve_open_cmd:
type: SendCmd
goal:
command: command
feedback:
status: status
result":
success: success
schema:
type: object
properties:
status:
type: string
description: The status of the device
sensordata:
type: number
description: 电导传感器数据
required:
- status
- sensordata
additionalProperties: false
rotavap.one:
class:
module: unilabos.devices.rotavap.rotavap_one:RotavapOne
type: python
status_types:
pump_time: Float64
rotate_time: Float64
action_value_mappings:
set_timer:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success
schema:
type: object
properties:
temperature:
type: number
description: 旋蒸水浴温度
pump_time:
type: number
description: The pump time of the device
rotate_time:
type: number
description: The rotate time of the device
required:
- pump_time
- rotate_time
additionalProperties: false

View File

@@ -0,0 +1,35 @@
syringe_pump_with_valve.runze:
class:
module: unilabos.devices.pump_and_valve.runze_backbone:RunzeSyringePump
type: python
schema:
type: object
properties:
status:
type: string
description: The status of the device
position:
type: number
description: The volume of the syringe
speed_max:
type: number
description: The speed of the syringe
valve_position:
type: string
description: The position of the valve
required:
- status
- position
- valve_position
additionalProperties: false
solenoid_valve.mock:
class:
module: unilabos.devices.pump_and_valve.solenoid_valve_mock:SolenoidValveMock
type: python
solenoid_valve:
class:
module: unilabos.devices.pump_and_valve.solenoid_valve:SolenoidValve
type: python

View File

@@ -0,0 +1,28 @@
# 仙工智能底盘(知行使用)
agv.SEER:
class:
module: unilabos.devices.agv.agv_navigator:AgvNavigator
type: python
status_types:
pose: Float64MultiArray
status: String
action_value_mappings:
send_nav_task:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success
schema:
properties:
pose:
type: array
items:
type: number
status:
type: string
required:
- status
additionalProperties: false
type: object

View File

@@ -0,0 +1,36 @@
robotic_arm.UR:
class:
module: unilabos.devices.agv.ur_arm_task:UrArmTask
type: python
status_types:
arm_pose: Float64MultiArray
gripper_pose: Float64
arm_status: String
gripper_status: String
action_value_mappings:
move_pos_task:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success
schema:
properties:
arm_pose:
type: array
items:
type: number
gripper_pose:
type: number
arm_status:
type: string
description: 机械臂设备状态
gripper_status:
type: string
description: 机械爪设备状态
required:
- arm_status
- gripper_status
additionalProperties: false
type: object

View File

@@ -0,0 +1,36 @@
gripper.mock:
class:
module: unilabos.devices.gripper.mock:MockGripper
type: python
status_types:
position: Float64
torque: Float64
status: String
action_value_mappings:
push_to:
type: GripperCommand
goal:
command.position: position
command.max_effort: torque
feedback:
position: position
effort: torque
result:
position: position
effort: torque
gripper.misumi_rz:
class:
module: unilabos.devices.motor:Grasp.EleGripper
type: python
status_types:
status: String
action_value_mappings:
execute_command_from_outer:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success

View File

@@ -0,0 +1,55 @@
linear_motion.grbl:
class:
module: unilabos.devices.cnc.grbl_sync:GrblCNC
type: python
action_value_mappings:
move_through_points: &move_through_points
type: NavigateThroughPoses
goal:
poses[].pose.position: positions[]
feedback:
current_pose.pose.position: position
navigation_time.sec: time_spent
estimated_time_remaining.sec: time_remaining
number_of_poses_remaining: pose_number_remaining
result: {}
set_spindle_speed:
type: SingleJointPosition
goal:
position: spindle_speed
feedback:
position: spindle_speed
result: {}
schema:
type: object
properties:
position:
type: array
items:
type: number
description: The position of the device
spindle_speed:
type: number
description: The spindle speed of the device
required:
- position
- spindle_speed
additionalProperties: false
motor.iCL42:
class:
module: unilabos.devices.motor.iCL42:iCL42Driver
type: python
status_types:
motor_position: Int64
is_executing_run: Bool
success: Bool
action_value_mappings:
execute_command_from_outer:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success

View File

@@ -0,0 +1,62 @@
heaterstirrer.dalong:
class:
module: unilabos.devices.heaterstirrer.dalong:HeaterStirrer_DaLong
type: python
status_types:
temp: Float64
temp_warning: Float64
stir_speed: Float64
action_value_mappings:
set_temp_warning:
type: SendCmd
goal:
command: temp
feedback: {}
result:
success: success
set_temp_target:
type: SendCmd
goal:
command: temp
feedback: {}
result:
success: success
heatchill:
type: HeatChill
goal:
vessel: vessel
temp: temp
time: time
purpose: purpose
feedback:
status: status
result:
success: success
chiller:
class:
module: unilabos.devices.temperature.chiller:Chiller
type: python
action_value_mappings:
set_temperature:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success
tempsensor:
class:
module: unilabos.devices.temperature.sensor_node:TempSensorNode
type: python
status_types:
value: Float64
warning: Float64
action_value_mappings:
set_warning:
type: SendCmd
goal:
command: command
feedback: {}
result:
success: success

View File

@@ -0,0 +1,9 @@
vacuum_pump.mock:
class:
module: unilabos.devices.pump_and_valve.vacuum_pump_mock:VacuumPumpMock
type: python
gas_source.mock:
class:
module: unilabos.devices.pump_and_valve.vacuum_pump_mock:VacuumPumpMock
type: python

View File

@@ -0,0 +1,6 @@
workstation:
class:
module: unilabos.ros.nodes.presets.protocol_node:ROS2ProtocolNode
type: ros2
schema:
properties: {}

View File

@@ -0,0 +1,176 @@
import os
import sys
from pathlib import Path
from typing import Any
import yaml
from unilabos.utils import logger
from unilabos.ros.msgs.message_converter import msg_converter_manager
from unilabos.utils.decorator import singleton
DEFAULT_PATHS = [Path(__file__).absolute().parent]
@singleton
class Registry:
def __init__(self, registry_paths=None):
self.registry_paths = DEFAULT_PATHS.copy() # 使用copy避免修改默认值
if registry_paths:
self.registry_paths.extend(registry_paths)
self.device_type_registry = {}
self.resource_type_registry = {}
self._setup_called = False # 跟踪setup是否已调用
# 其他状态变量
# self.is_host_mode = False # 移至BasicConfig中
def setup(self):
# 检查是否已调用过setup
if self._setup_called:
logger.critical("[UniLab Registry] setup方法已被调用过不允许多次调用")
return
# 标记setup已被调用
self._setup_called = True
logger.debug(f"[UniLab Registry] ----------Setup----------")
self.registry_paths = [Path(path).absolute() for path in self.registry_paths]
for i, path in enumerate(self.registry_paths):
sys_path = path.parent
logger.debug(f"[UniLab Registry] Path {i+1}/{len(self.registry_paths)}: {sys_path}")
sys.path.append(str(sys_path))
self.load_device_types(path)
self.load_resource_types(path)
logger.info("[UniLab Registry] 注册表设置完成")
def load_resource_types(self, path: os.PathLike):
abs_path = Path(path).absolute()
resource_path = abs_path / "resources"
files = list(resource_path.glob("*/*.yaml"))
logger.debug(f"[UniLab Registry] resources: {resource_path.exists()}, total: {len(files)}")
current_resource_number = len(self.resource_type_registry) + 1
for i, file in enumerate(files):
data = yaml.safe_load(open(file, encoding="utf-8"))
if data:
# 为每个资源添加文件路径信息
for resource_id, resource_info in data.items():
# 添加文件路径 - 使用规范化的完整文件路径
resource_info["file_path"] = str(file.absolute()).replace("\\", "/")
self.resource_type_registry.update(data)
logger.debug(
f"[UniLab Registry] Resource-{current_resource_number} File-{i+1}/{len(files)} "
+ f"Add {list(data.keys())}"
)
current_resource_number += 1
else:
logger.debug(f"[UniLab Registry] Res File-{i+1}/{len(files)} Not Valid YAML File: {file.absolute()}")
def _replace_type_with_class(self, type_name: str, device_id: str, field_name: str) -> Any:
"""
将类型名称替换为实际的类对象
Args:
type_name: 类型名称
device_id: 设备ID用于错误信息
field_name: 字段名称,用于错误信息
Returns:
找到的类对象或原始字符串
Raises:
SystemExit: 如果找不到类型则终止程序
"""
# 如果类型名为空,跳过替换
if not type_name or type_name == "":
logger.warning(f"[UniLab Registry] 设备 {device_id}{field_name} 类型为空,跳过替换")
return type_name
if "." in type_name:
type_class = msg_converter_manager.get_class(type_name)
else:
type_class = msg_converter_manager.search_class(type_name)
if type_class:
return type_class
else:
logger.error(f"[UniLab Registry] 无法找到类型 '{type_name}' 用于设备 {device_id}{field_name}")
sys.exit(1)
def load_device_types(self, path: os.PathLike):
abs_path = Path(path).absolute()
devices_path = abs_path / "devices"
device_comms_path = abs_path / "device_comms"
files = list(devices_path.glob("*.yaml")) + list(device_comms_path.glob("*.yaml"))
logger.debug(
f"[UniLab Registry] devices: {devices_path.exists()}, device_comms: {device_comms_path.exists()}, "
+ f"total: {len(files)}"
)
current_device_number = len(self.device_type_registry) + 1
for i, file in enumerate(files):
data = yaml.safe_load(open(file, encoding="utf-8"))
if data:
# 在添加到注册表前处理类型替换
for device_id, device_config in data.items():
# 添加文件路径信息 - 使用规范化的完整文件路径
device_config["file_path"] = str(file.absolute()).replace("\\", "/")
if "class" in device_config:
# 处理状态类型
if "status_types" in device_config["class"]:
for status_name, status_type in device_config["class"]["status_types"].items():
device_config["class"]["status_types"][status_name] = self._replace_type_with_class(
status_type, device_id, f"状态 {status_name}"
)
# 处理动作值映射
if "action_value_mappings" in device_config["class"]:
for action_name, action_config in device_config["class"]["action_value_mappings"].items():
if "type" in action_config:
action_config["type"] = self._replace_type_with_class(
action_config["type"], device_id, f"动作 {action_name}"
)
self.device_type_registry.update(data)
for device_id in data.keys():
logger.debug(
f"[UniLab Registry] Device-{current_device_number} File-{i+1}/{len(files)} Add {device_id} "
+ f"[{data[device_id].get('name', '未命名设备')}]"
)
current_device_number += 1
else:
logger.debug(
f"[UniLab Registry] Device File-{i+1}/{len(files)} Not Valid YAML File: {file.absolute()}"
)
# 全局单例实例
lab_registry = Registry()
def build_registry(registry_paths=None):
"""
构建或获取Registry单例实例
Args:
registry_paths: 额外的注册表路径列表
Returns:
Registry实例
"""
logger.info("[UniLab Registry] 构建注册表实例")
# 由于使用了单例,这里不需要重新创建实例
global lab_registry
# 如果有额外路径添加到registry_paths
if registry_paths:
current_paths = lab_registry.registry_paths.copy()
# 检查是否有新路径需要添加
for path in registry_paths:
if path not in current_paths:
lab_registry.registry_paths.append(path)
# 初始化注册表
lab_registry.setup()
return lab_registry

View File

@@ -0,0 +1,4 @@
OTDeck:
class:
module: pylabrobot.resources.opentrons.deck:OTDeck
type: pylabrobot

View File

@@ -0,0 +1,4 @@
Opentrons_96_adapter_Vb:
class:
module: pylabrobot.resources.opentrons.plate_adapters:Opentrons_96_adapter_Vb
type: pylabrobot

View File

@@ -0,0 +1,74 @@
corning_6_wellplate_16point8ml_flat:
class:
module: pylabrobot.resources.opentrons.plates:corning_6_wellplate_16point8ml_flat
type: pylabrobot
corning_12_wellplate_6point9ml_flat:
class:
module: pylabrobot.resources.opentrons.plates:corning_12_wellplate_6point9ml_flat
type: pylabrobot
corning_24_wellplate_3point4ml_flat:
class:
module: pylabrobot.resources.opentrons.plates:corning_24_wellplate_3point4ml_flat
type: pylabrobot
corning_48_wellplate_1point6ml_flat:
class:
module: pylabrobot.resources.opentrons.plates:corning_48_wellplate_1point6ml_flat
type: pylabrobot
corning_96_wellplate_360ul_flat:
class:
module: pylabrobot.resources.opentrons.plates:corning_96_wellplate_360ul_flat
type: pylabrobot
corning_384_wellplate_112ul_flat:
class:
module: pylabrobot.resources.opentrons.plates:corning_384_wellplate_112ul_flat
type: pylabrobot
nest_96_wellplate_2ml_deep:
class:
module: pylabrobot.resources.opentrons.plates:nest_96_wellplate_2ml_deep
type: pylabrobot
nest_96_wellplate_200ul_flat:
class:
module: pylabrobot.resources.opentrons.plates:nest_96_wellplate_200ul_flat
type: pylabrobot
nest_96_wellplate_100ul_pcr_full_skirt:
class:
module: pylabrobot.resources.opentrons.plates:nest_96_wellplate_100ul_pcr_full_skirt
type: pylabrobot
appliedbiosystemsmicroamp_384_wellplate_40ul:
class:
module: pylabrobot.resources.opentrons.plates:appliedbiosystemsmicroamp_384_wellplate_40ul
type: pylabrobot
thermoscientificnunc_96_wellplate_1300ul:
class:
module: pylabrobot.resources.opentrons.plates:thermoscientificnunc_96_wellplate_1300ul
type: pylabrobot
thermoscientificnunc_96_wellplate_2000ul:
class:
module: pylabrobot.resources.opentrons.plates:thermoscientificnunc_96_wellplate_2000ul
type: pylabrobot
usascientific_96_wellplate_2point4ml_deep:
class:
module: pylabrobot.resources.opentrons.plates:usascientific_96_wellplate_2point4ml_deep
type: pylabrobot
biorad_96_wellplate_200ul_pcr:
class:
module: pylabrobot.resources.opentrons.plates:biorad_96_wellplate_200ul_pcr
type: pylabrobot
biorad_384_wellplate_50ul:
class:
module: pylabrobot.resources.opentrons.plates:biorad_384_wellplate_50ul
type: pylabrobot

View File

@@ -0,0 +1,29 @@
agilent_1_reservoir_290ml:
class:
module: pylabrobot.resources.opentrons.reserviors:agilent_1_reservoir_290ml
type: pylabrobot
axygen_1_reservoir_90ml:
class:
module: pylabrobot.resources.opentrons.reserviors:axygen_1_reservoir_90ml
type: pylabrobot
nest_12_reservoir_15ml:
class:
module: pylabrobot.resources.opentrons.reserviors:nest_12_reservoir_15ml
type: pylabrobot
nest_1_reservoir_195ml:
class:
module: pylabrobot.resources.opentrons.reserviors:nest_1_reservoir_195ml
type: pylabrobot
nest_1_reservoir_290ml:
class:
module: pylabrobot.resources.opentrons.reserviors:nest_1_reservoir_290ml
type: pylabrobot
usascientific_12_reservoir_22ml:
class:
module: pylabrobot.resources.opentrons.reserviors:usascientific_12_reservoir_22ml
type: pylabrobot

View File

@@ -0,0 +1,64 @@
eppendorf_96_tiprack_1000ul_eptips:
class:
module: pylabrobot.resources.opentrons.tip_racks:eppendorf_96_tiprack_1000ul_eptips
type: pylabrobot
tipone_96_tiprack_200ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:tipone_96_tiprack_200ul
type: pylabrobot
opentrons_96_tiprack_300ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_tiprack_300ul
type: pylabrobot
opentrons_96_tiprack_10ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_tiprack_10ul
type: pylabrobot
opentrons_96_filtertiprack_10ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_filtertiprack_10ul
type: pylabrobot
geb_96_tiprack_10ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:geb_96_tiprack_10ul
type: pylabrobot
opentrons_96_filtertiprack_200ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_filtertiprack_200ul
type: pylabrobot
eppendorf_96_tiprack_10ul_eptips:
class:
module: pylabrobot.resources.opentrons.tip_racks:eppendorf_96_tiprack_10ul_eptips
type: pylabrobot
opentrons_96_tiprack_1000ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_tiprack_1000ul
type: pylabrobot
opentrons_96_tiprack_20ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_tiprack_20ul
type: pylabrobot
opentrons_96_filtertiprack_1000ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_filtertiprack_1000ul
type: pylabrobot
opentrons_96_filtertiprack_20ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:opentrons_96_filtertiprack_20ul
type: pylabrobot
geb_96_tiprack_1000ul:
class:
module: pylabrobot.resources.opentrons.tip_racks:geb_96_tiprack_1000ul
type: pylabrobot

View File

@@ -0,0 +1,99 @@
opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap
type: pylabrobot
opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap_acrylic:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_eppendorf_2ml_safelock_snapcap_acrylic
type: pylabrobot
opentrons_6_tuberack_falcon_50ml_conical:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_6_tuberack_falcon_50ml_conical
type: pylabrobot
opentrons_15_tuberack_nest_15ml_conical:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_15_tuberack_nest_15ml_conical
type: pylabrobot
opentrons_24_tuberack_nest_2ml_screwcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_nest_2ml_screwcap
type: pylabrobot
opentrons_24_tuberack_generic_0point75ml_snapcap_acrylic:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_generic_0point75ml_snapcap_acrylic
type: pylabrobot
opentrons_10_tuberack_nest_4x50ml_6x15ml_conical:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_10_tuberack_nest_4x50ml_6x15ml_conical
type: pylabrobot
opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical_acrylic:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical_acrylic
type: pylabrobot
opentrons_24_tuberack_nest_1point5ml_screwcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_nest_1point5ml_screwcap
type: pylabrobot
opentrons_24_tuberack_nest_1point5ml_snapcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_nest_1point5ml_snapcap
type: pylabrobot
opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_10_tuberack_falcon_4x50ml_6x15ml_conical
type: pylabrobot
opentrons_24_tuberack_nest_2ml_snapcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_nest_2ml_snapcap
type: pylabrobot
opentrons_24_tuberack_nest_0point5ml_screwcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_nest_0point5ml_screwcap
type: pylabrobot
opentrons_24_tuberack_eppendorf_1point5ml_safelock_snapcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_eppendorf_1point5ml_safelock_snapcap
type: pylabrobot
opentrons_6_tuberack_nest_50ml_conical:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_6_tuberack_nest_50ml_conical
type: pylabrobot
opentrons_15_tuberack_falcon_15ml_conical:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_15_tuberack_falcon_15ml_conical
type: pylabrobot
opentrons_24_tuberack_generic_2ml_screwcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_tuberack_generic_2ml_screwcap
type: pylabrobot
opentrons_96_well_aluminum_block:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_96_well_aluminum_block
type: pylabrobot
opentrons_24_aluminumblock_generic_2ml_screwcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_aluminumblock_generic_2ml_screwcap
type: pylabrobot
opentrons_24_aluminumblock_nest_1point5ml_snapcap:
class:
module: pylabrobot.resources.opentrons.tube_racks:opentrons_24_aluminumblock_nest_1point5ml_snapcap
type: pylabrobot