mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-15 13:44:39 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13a6795657 | ||
|
|
53219d8b04 |
@@ -39,7 +39,9 @@ Uni-Lab-OS recommends using `mamba` for environment management. Choose the appro
|
||||
|
||||
```bash
|
||||
# Create new environment
|
||||
mamba create -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
mamba create -n unilab python=3.11.11
|
||||
mamba activate unilab
|
||||
mamba install -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
```
|
||||
|
||||
## Install Dev Uni-Lab-OS
|
||||
|
||||
@@ -41,7 +41,9 @@ Uni-Lab-OS 建议使用 `mamba` 管理环境。根据您的操作系统选择适
|
||||
|
||||
```bash
|
||||
# 创建新环境
|
||||
mamba create -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
mamba create -n unilab python=3.11.11
|
||||
mamba activate unilab
|
||||
mamba install -n unilab uni-lab::unilabos -c robostack-staging -c conda-forge
|
||||
```
|
||||
|
||||
2. 安装开发版 Uni-Lab-OS:
|
||||
|
||||
@@ -317,45 +317,6 @@ unilab --help
|
||||
|
||||
如果所有命令都正常输出,说明开发环境配置成功!
|
||||
|
||||
### 开发工具推荐
|
||||
|
||||
#### IDE
|
||||
|
||||
- **PyCharm Professional**: 强大的 Python IDE,支持远程调试
|
||||
- **VS Code**: 轻量级,配合 Python 扩展使用
|
||||
- **Vim/Emacs**: 适合终端开发
|
||||
|
||||
#### 推荐的 VS Code 扩展
|
||||
|
||||
- Python
|
||||
- Pylance
|
||||
- ROS
|
||||
- URDF
|
||||
- YAML
|
||||
|
||||
#### 调试工具
|
||||
|
||||
```bash
|
||||
# 安装调试工具
|
||||
pip install ipdb pytest pytest-cov -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
|
||||
|
||||
# 代码质量检查
|
||||
pip install black flake8 mypy -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
|
||||
```
|
||||
|
||||
### 设置 pre-commit 钩子(可选)
|
||||
|
||||
```bash
|
||||
# 安装 pre-commit
|
||||
pip install pre-commit -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
|
||||
|
||||
# 设置钩子
|
||||
pre-commit install
|
||||
|
||||
# 手动运行检查
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验证安装
|
||||
|
||||
@@ -159,9 +159,10 @@ def parse_args():
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 解析命令行参数
|
||||
args = parse_args()
|
||||
convert_argv_dashes_to_underscores(args)
|
||||
args_dict = vars(args.parse_args())
|
||||
parser = parse_args()
|
||||
convert_argv_dashes_to_underscores(parser)
|
||||
args = parser.parse_args()
|
||||
args_dict = vars(args)
|
||||
|
||||
# 环境检查 - 检查并自动安装必需的包 (可选)
|
||||
if not args_dict.get("skip_env_check", False):
|
||||
@@ -220,17 +221,18 @@ def main():
|
||||
logger.info(f"Log level set to '{BasicConfig.log_level}' from config file.")
|
||||
configure_logger(loglevel=BasicConfig.log_level, working_dir=working_dir)
|
||||
|
||||
if args_dict["addr"] == "test":
|
||||
print_status("使用测试环境地址", "info")
|
||||
HTTPConfig.remote_addr = "https://uni-lab.test.bohrium.com/api/v1"
|
||||
elif args_dict["addr"] == "uat":
|
||||
print_status("使用uat环境地址", "info")
|
||||
HTTPConfig.remote_addr = "https://uni-lab.uat.bohrium.com/api/v1"
|
||||
elif args_dict["addr"] == "local":
|
||||
print_status("使用本地环境地址", "info")
|
||||
HTTPConfig.remote_addr = "http://127.0.0.1:48197/api/v1"
|
||||
else:
|
||||
HTTPConfig.remote_addr = args_dict.get("addr", "")
|
||||
if args.addr != parser.get_default("addr"):
|
||||
if args.addr == "test":
|
||||
print_status("使用测试环境地址", "info")
|
||||
HTTPConfig.remote_addr = "https://uni-lab.test.bohrium.com/api/v1"
|
||||
elif args.addr == "uat":
|
||||
print_status("使用uat环境地址", "info")
|
||||
HTTPConfig.remote_addr = "https://uni-lab.uat.bohrium.com/api/v1"
|
||||
elif args.addr == "local":
|
||||
print_status("使用本地环境地址", "info")
|
||||
HTTPConfig.remote_addr = "http://127.0.0.1:48197/api/v1"
|
||||
else:
|
||||
HTTPConfig.remote_addr = args.addr
|
||||
|
||||
# 设置BasicConfig参数
|
||||
if args_dict.get("ak", ""):
|
||||
@@ -327,6 +329,10 @@ def main():
|
||||
for ind, i in enumerate(resource_edge_info[::-1]):
|
||||
source_node: ResourceDict = nodes[i["source"]]
|
||||
target_node: ResourceDict = nodes[i["target"]]
|
||||
if "sourceHandle" not in source_node:
|
||||
continue
|
||||
if "targetHandle" not in target_node:
|
||||
continue
|
||||
source_handle = i["sourceHandle"]
|
||||
target_handle = i["targetHandle"]
|
||||
source_handler_keys = [
|
||||
|
||||
@@ -41,7 +41,7 @@ class WSConfig:
|
||||
|
||||
# HTTP配置
|
||||
class HTTPConfig:
|
||||
remote_addr = "http://127.0.0.1:48197/api/v1"
|
||||
remote_addr = "https://uni-lab.bohrium.com/api/v1"
|
||||
|
||||
|
||||
# ROS配置
|
||||
|
||||
0
unilabos/devices/laiyu_liquid_test/__init__.py
Normal file
0
unilabos/devices/laiyu_liquid_test/__init__.py
Normal file
0
unilabos/devices/liquid_handling/laiyu/__init__.py
Normal file
0
unilabos/devices/liquid_handling/laiyu/__init__.py
Normal file
@@ -988,6 +988,18 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
|
||||
dis_vols = [float(dis_vols)]
|
||||
else:
|
||||
dis_vols = [float(v) for v in dis_vols]
|
||||
|
||||
# 统一混合次数为标量,防止数组/列表与 int 比较时报错
|
||||
if mix_times is not None and not isinstance(mix_times, (int, float)):
|
||||
try:
|
||||
mix_times = mix_times[0] if len(mix_times) > 0 else None
|
||||
except Exception:
|
||||
try:
|
||||
mix_times = next(iter(mix_times))
|
||||
except Exception:
|
||||
pass
|
||||
if mix_times is not None:
|
||||
mix_times = int(mix_times)
|
||||
|
||||
# 识别传输模式
|
||||
num_sources = len(sources)
|
||||
|
||||
@@ -5,6 +5,7 @@ import json
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
import uuid
|
||||
from typing import Any, List, Dict, Optional, Tuple, TypedDict, Union, Sequence, Iterator, Literal
|
||||
|
||||
from pylabrobot.liquid_handling import (
|
||||
@@ -856,7 +857,30 @@ class PRCXI9300Api:
|
||||
|
||||
def _raw_request(self, payload: str) -> str:
|
||||
if self.debug:
|
||||
return " "
|
||||
# 调试/仿真模式下直接返回可解析的模拟 JSON,避免后续 json.loads 报错
|
||||
try:
|
||||
req = json.loads(payload)
|
||||
method = req.get("MethodName")
|
||||
except Exception:
|
||||
method = None
|
||||
|
||||
data: Any = True
|
||||
if method in {"AddSolution"}:
|
||||
data = str(uuid.uuid4())
|
||||
elif method in {"AddWorkTabletMatrix", "AddWorkTabletMatrix2"}:
|
||||
data = {"Success": True, "Message": "debug mock"}
|
||||
elif method in {"GetErrorCode"}:
|
||||
data = ""
|
||||
elif method in {"RemoveErrorCodet", "Reset", "Start", "LoadSolution", "Pause", "Resume", "Stop"}:
|
||||
data = True
|
||||
elif method in {"GetStepStateList", "GetStepStatus", "GetStepState"}:
|
||||
data = []
|
||||
elif method in {"GetLocation"}:
|
||||
data = {"X": 0, "Y": 0, "Z": 0}
|
||||
elif method in {"GetResetStatus"}:
|
||||
data = False
|
||||
|
||||
return json.dumps({"Success": True, "Msg": "debug mock", "Data": data})
|
||||
with contextlib.closing(socket.socket()) as sock:
|
||||
sock.settimeout(self.timeout)
|
||||
sock.connect((self.host, self.port))
|
||||
|
||||
@@ -174,35 +174,6 @@ bioyond_dispensing_station:
|
||||
title: query_resource_by_name参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-transfer_materials_to_reaction_station:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default:
|
||||
target_device_id: null
|
||||
transfer_groups: null
|
||||
handles: {}
|
||||
placeholder_keys: {}
|
||||
result: {}
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties:
|
||||
target_device_id:
|
||||
type: string
|
||||
transfer_groups:
|
||||
type: array
|
||||
required:
|
||||
- target_device_id
|
||||
- transfer_groups
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: transfer_materials_to_reaction_station参数
|
||||
type: object
|
||||
type: UniLabJsonCommand
|
||||
auto-workflow_sample_locations:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
|
||||
@@ -284,10 +284,18 @@ def modify_to_backend_format(data: list[dict[str, Any]]) -> list[dict[str, Any]]
|
||||
edge["sourceHandle"] = port[source]
|
||||
elif "source_port" in edge:
|
||||
edge["sourceHandle"] = edge.pop("source_port")
|
||||
else:
|
||||
typ = edge.get("type")
|
||||
if typ == "communication":
|
||||
continue
|
||||
if target in port:
|
||||
edge["targetHandle"] = port[target]
|
||||
elif "target_port" in edge:
|
||||
edge["targetHandle"] = edge.pop("target_port")
|
||||
else:
|
||||
typ = edge.get("type")
|
||||
if typ == "communication":
|
||||
continue
|
||||
edge["id"] = f"reactflow__edge-{source}-{edge['sourceHandle']}-{target}-{edge['targetHandle']}"
|
||||
for key in ["source_port", "target_port"]:
|
||||
if key in edge:
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "device",
|
||||
"class": "syringepump.runze",
|
||||
"class": "syringe_pump_with_valve.runze.SY03B-T08",
|
||||
"position": {
|
||||
"x": 620.6111111111111,
|
||||
"y": 171,
|
||||
@@ -93,7 +93,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 430.4087301587302,
|
||||
"y": 428,
|
||||
@@ -117,7 +117,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 295.36944444444447,
|
||||
"y": 428,
|
||||
@@ -141,7 +141,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 165.36944444444444,
|
||||
"y": 428,
|
||||
@@ -165,7 +165,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 165.36944444444444,
|
||||
"y": 428,
|
||||
@@ -189,7 +189,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 35,
|
||||
"y": 428,
|
||||
@@ -213,7 +213,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 698.1111111111111,
|
||||
"y": 428,
|
||||
@@ -255,7 +255,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "device",
|
||||
"class": "syringepump.runze",
|
||||
"class": "syringe_pump_with_valve.runze.SY03B-T08",
|
||||
"position": {
|
||||
"x": 1195.611507936508,
|
||||
"y": 686,
|
||||
@@ -279,7 +279,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1587.703373015873,
|
||||
"y": 1172.5,
|
||||
@@ -299,7 +299,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "device",
|
||||
"class": "separator_controller",
|
||||
"class": "separator.homemade",
|
||||
"position": {
|
||||
"x": 1624.4027777777778,
|
||||
"y": 665.5,
|
||||
@@ -320,7 +320,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1614.404365079365,
|
||||
"y": 948,
|
||||
@@ -340,7 +340,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1915.7035714285714,
|
||||
"y": 665.5,
|
||||
@@ -360,7 +360,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1785.7035714285714,
|
||||
"y": 665.5,
|
||||
@@ -384,7 +384,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 2054.0650793650793,
|
||||
"y": 665.5,
|
||||
@@ -408,7 +408,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "device",
|
||||
"class": "syringepump.runze",
|
||||
"class": "syringe_pump_with_valve.runze.SY03B-T08",
|
||||
"position": {
|
||||
"x": 1630.6527777777778,
|
||||
"y": 448.5,
|
||||
@@ -432,7 +432,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "device",
|
||||
"class": "rotavap",
|
||||
"class": "rotavap.one",
|
||||
"position": {
|
||||
"x": 1339.7031746031746,
|
||||
"y": 968.5,
|
||||
@@ -453,7 +453,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1339.7031746031746,
|
||||
"y": 1152,
|
||||
@@ -473,7 +473,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 909.722619047619,
|
||||
"y": 948,
|
||||
@@ -493,7 +493,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 867.972619047619,
|
||||
"y": 1152,
|
||||
@@ -513,7 +513,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 742.722619047619,
|
||||
"y": 948,
|
||||
@@ -533,7 +533,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1206.722619047619,
|
||||
"y": 948,
|
||||
@@ -553,7 +553,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"class": "container",
|
||||
"position": {
|
||||
"x": 1148.222619047619,
|
||||
"y": 1152,
|
||||
@@ -573,7 +573,7 @@
|
||||
"children": [],
|
||||
"parent": "YugongStation",
|
||||
"type": "device",
|
||||
"class": "syringepump.runze",
|
||||
"class": "syringe_pump_with_valve.runze.SY03B-T08",
|
||||
"position": {
|
||||
"x": 1469.7031746031746,
|
||||
"y": 968.5,
|
||||
|
||||
Reference in New Issue
Block a user