diff --git a/unilabos/devices/workstation/bioyond_cell/2025092702.xlsx b/unilabos/devices/workstation/bioyond_cell/2025092702.xlsx new file mode 100644 index 00000000..5c49ab69 Binary files /dev/null and b/unilabos/devices/workstation/bioyond_cell/2025092702.xlsx differ diff --git a/unilabos/devices/workstation/bioyond_cell/2025101301.xlsx b/unilabos/devices/workstation/bioyond_cell/2025101301.xlsx new file mode 100644 index 00000000..06bffc86 Binary files /dev/null and b/unilabos/devices/workstation/bioyond_cell/2025101301.xlsx differ diff --git a/unilabos/devices/workstation/bioyond_cell/benyao_test.py b/unilabos/devices/workstation/bioyond_cell/benyao_test.py new file mode 100644 index 00000000..7c1c09c6 --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/benyao_test.py @@ -0,0 +1,49 @@ +import requests +import json +from datetime import datetime +def test_benyao_api(): + # 配置信息 + ip_addr = "192.168.1.200" + port = 44386 + #url = f"http://{ip_addr}:{port}/api/lims/scheduler/scheduler-status" + #url = f"http://{ip_addr}:{port}/api/lims/order/order-list-status" + url = f"http://{ip_addr}:{port}/api/lims/storage/stock-material" + apiKey = "8A819E5C" # 请替换为实际apiKey + + # 构造请求体 + request_data = { + "apiKey": apiKey, + "requestTime": datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ"), # 示例:2025-08-15T10:00:00.000Z + "data": { + "typeMode": 1, + "includeDetail": True + } + + } + + #request_data = { + # "apiKey": apiKey, + # "requestTime": datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ"), # 示例:2025-08-15T10:00:00.000Z + # "data": + #} + + + print(request_data) + # 发送POST请求 + try: + response = requests.post(url, json=request_data, timeout=10) + response.raise_for_status() # 检查HTTP状态码 + + # 解析响应 + result = response.json() + print("响应状态码:", response.status_code) + print("响应内容:") + print(json.dumps(result, indent=2, ensure_ascii=False)) + + except requests.exceptions.RequestException as e: + print("请求失败:", e) + except json.JSONDecodeError as e: + print("JSON解析失败:", e) + +if __name__ == "__main__": + test_benyao_api() \ No newline at end of file diff --git a/unilabos/devices/workstation/bioyond_cell/bioyond_material_management.py b/unilabos/devices/workstation/bioyond_cell/bioyond_material_management.py new file mode 100644 index 00000000..245da15c --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/bioyond_material_management.py @@ -0,0 +1,374 @@ +""" +Bioyond物料管理实现 +Bioyond Material Management Implementation + +基于Bioyond系统的物料管理,支持从Bioyond系统同步物料到UniLab工作站 +""" +from typing import Dict, Any, List, Optional, Union +import json +import asyncio +from abc import ABC, abstractmethod + +from pylabrobot.resources import ( + Resource as PLRResource, + Container, + Deck, + Coordinate as PLRCoordinate, +) + +from unilabos.ros.nodes.resource_tracker import DeviceNodeResourceTracker +from unilabos.utils.log import logger +from unilabos.resources.graphio import ( + resource_plr_to_ulab, + resource_ulab_to_plr, + resource_bioyond_to_ulab, + resource_bioyond_container_to_ulab, + resource_ulab_to_bioyond +) +from .workstation_material_management import MaterialManagementBase + + +class BioyondMaterialManagement(MaterialManagementBase): + """Bioyond物料管理类 + + 实现从Bioyond系统同步物料到UniLab工作站的功能: + 1. 从Bioyond系统获取物料数据 + 2. 转换为UniLab格式 + 3. 同步到PyLabRobot Deck + 4. 支持双向同步 + """ + + def __init__( + self, + device_id: str, + deck_config: Dict[str, Any], + resource_tracker: DeviceNodeResourceTracker, + children_config: Dict[str, Dict[str, Any]] = None, + bioyond_config: Dict[str, Any] = None + ): + self.bioyond_config = bioyond_config or {} + self.bioyond_api_client = None + self.sync_interval = self.bioyond_config.get("sync_interval", 30) # 同步间隔(秒) + + # 初始化父类 + super().__init__(device_id, deck_config, resource_tracker, children_config) + + # 初始化Bioyond API客户端 + self._initialize_bioyond_client() + + # 启动同步任务 + self._start_sync_task() + + def _initialize_bioyond_client(self): + """初始化Bioyond API客户端""" + try: + # 这里应该根据实际的Bioyond API实现 + # 暂时使用模拟客户端 + self.bioyond_api_client = BioyondAPIClient(self.bioyond_config) + logger.info(f"Bioyond API客户端初始化成功") + except Exception as e: + logger.error(f"Bioyond API客户端初始化失败: {e}") + self.bioyond_api_client = None + + def _start_sync_task(self): + """启动同步任务""" + if self.bioyond_api_client: + # 创建异步同步任务 + asyncio.create_task(self._periodic_sync()) + logger.info(f"Bioyond同步任务已启动,间隔: {self.sync_interval}秒") + + async def _periodic_sync(self): + """定期同步任务""" + while True: + try: + await self.sync_from_bioyond() + await asyncio.sleep(self.sync_interval) + except Exception as e: + logger.error(f"Bioyond同步任务出错: {e}") + await asyncio.sleep(self.sync_interval) + + async def sync_from_bioyond(self) -> bool: + """从Bioyond系统同步物料""" + try: + if not self.bioyond_api_client: + logger.warning("Bioyond API客户端未初始化") + return False + + # 1. 从Bioyond获取物料数据 + bioyond_data = await self.bioyond_api_client.get_materials() + if not bioyond_data: + logger.warning("从Bioyond获取物料数据为空") + return False + + # 2. 转换为UniLab格式 + if isinstance(bioyond_data, dict) and "data" in bioyond_data: + # 容器格式数据 + unilab_resources = resource_bioyond_container_to_ulab(bioyond_data) + else: + # 物料列表格式数据 + unilab_resources = resource_bioyond_to_ulab(bioyond_data) + + # 3. 转换为PLR格式并分配到Deck + await self._assign_resources_to_deck(unilab_resources) + + logger.info(f"从Bioyond同步了 {len(unilab_resources)} 个资源") + return True + + except Exception as e: + logger.error(f"从Bioyond同步物料失败: {e}") + return False + + async def sync_to_bioyond(self, plr_resource: PLRResource) -> bool: + """将本地物料变更同步到Bioyond系统""" + try: + if not self.bioyond_api_client: + logger.warning("Bioyond API客户端未初始化") + return False + + # 1. 转换为UniLab格式 + unilab_resource = resource_plr_to_ulab(plr_resource) + + # 2. 转换为Bioyond格式 + bioyond_materials = resource_ulab_to_bioyond([unilab_resource]) + + # 3. 发送到Bioyond系统 + success = await self.bioyond_api_client.update_materials(bioyond_materials) + + if success: + logger.info(f"成功同步物料 {plr_resource.name} 到Bioyond") + else: + logger.warning(f"同步物料 {plr_resource.name} 到Bioyond失败") + + return success + + except Exception as e: + logger.error(f"同步物料到Bioyond失败: {e}") + return False + + async def _assign_resources_to_deck(self, unilab_resources: List[Dict[str, Any]]): + """将UniLab资源分配到Deck""" + try: + # 转换为PLR格式 + from unilabos.resources.graphio import list_to_nested_dict + nested_resources = list_to_nested_dict(unilab_resources) + plr_resources = resource_ulab_to_plr(nested_resources) + + # 分配资源到Deck + if hasattr(plr_resources, 'children'): + resources_to_assign = plr_resources.children + elif isinstance(plr_resources, list): + resources_to_assign = plr_resources + else: + resources_to_assign = [plr_resources] + + for resource in resources_to_assign: + try: + # 获取资源位置 + if hasattr(resource, 'location') and resource.location: + location = PLRCoordinate(resource.location.x, resource.location.y, resource.location.z) + else: + location = PLRCoordinate(0, 0, 0) + + # 分配资源到Deck + self.plr_deck.assign_child_resource(resource, location) + + # 注册到resource tracker + self.resource_tracker.add_resource(resource) + + # 保存资源引用 + self.plr_resources[resource.name] = resource + + except Exception as e: + logger.error(f"分配资源 {resource.name} 到Deck失败: {e}") + + logger.info(f"成功分配了 {len(resources_to_assign)} 个资源到Deck") + + except Exception as e: + logger.error(f"分配资源到Deck失败: {e}") + + def _create_resource_by_type( + self, + resource_id: str, + resource_type: str, + config: Dict[str, Any], + data: Dict[str, Any], + location: PLRCoordinate + ) -> Optional[PLRResource]: + """根据类型创建Bioyond相关资源""" + try: + # 这里可以根据需要实现特定的Bioyond资源类型 + # 目前使用通用的容器类型 + if resource_type in ["container", "plate", "well"]: + return self._create_generic_container(resource_id, resource_type, config, data, location) + else: + logger.warning(f"未知的Bioyond资源类型: {resource_type}") + return None + + except Exception as e: + logger.error(f"创建Bioyond资源失败 {resource_id} ({resource_type}): {e}") + return None + + def _create_generic_container( + self, + resource_id: str, + resource_type: str, + config: Dict[str, Any], + data: Dict[str, Any], + location: PLRCoordinate + ) -> Optional[PLRResource]: + """创建通用容器资源""" + try: + from pylabrobot.resources import Plate, Well + + if resource_type == "plate": + return Plate( + name=resource_id, + size_x=config.get("size_x", 127.76), + size_y=config.get("size_y", 85.48), + size_z=config.get("size_z", 14.35), + location=location, + category="plate" + ) + elif resource_type == "well": + return Well( + name=resource_id, + size_x=config.get("size_x", 9.0), + size_y=config.get("size_y", 9.0), + size_z=config.get("size_z", 10.0), + location=location, + category="well" + ) + else: + return Container( + name=resource_id, + size_x=config.get("size_x", 50.0), + size_y=config.get("size_y", 50.0), + size_z=config.get("size_z", 10.0), + location=location, + category="container" + ) + + except Exception as e: + logger.error(f"创建通用容器失败 {resource_id}: {e}") + return None + + def get_bioyond_materials(self) -> List[Dict[str, Any]]: + """获取当前Bioyond物料列表""" + try: + # 将当前PLR资源转换为Bioyond格式 + bioyond_materials = [] + for resource in self.plr_resources.values(): + unilab_resource = resource_plr_to_ulab(resource) + bioyond_materials.extend(resource_ulab_to_bioyond([unilab_resource])) + return bioyond_materials + except Exception as e: + logger.error(f"获取Bioyond物料列表失败: {e}") + return [] + + def update_material_from_bioyond(self, material_id: str, bioyond_data: Dict[str, Any]) -> bool: + """从Bioyond数据更新指定物料""" + try: + # 查找现有物料 + material = self.find_material_by_id(material_id) + if not material: + logger.warning(f"未找到物料: {material_id}") + return False + + # 转换Bioyond数据为UniLab格式 + unilab_resources = resource_bioyond_to_ulab([bioyond_data]) + if not unilab_resources: + logger.warning(f"转换Bioyond数据失败: {material_id}") + return False + + # 更新物料属性 + unilab_resource = unilab_resources[0] + material.name = unilab_resource.get("name", material.name) + + # 更新位置 + position = unilab_resource.get("position", {}) + if position: + material.location = PLRCoordinate( + position.get("x", 0), + position.get("y", 0), + position.get("z", 0) + ) + + logger.info(f"成功更新物料: {material_id}") + return True + + except Exception as e: + logger.error(f"更新物料失败 {material_id}: {e}") + return False + + +class BioyondAPIClient: + """Bioyond API客户端(模拟实现) + + 实际使用时需要根据Bioyond系统的API接口实现 + """ + + def __init__(self, config: Dict[str, Any]): + self.config = config + self.base_url = config.get("base_url", "http://localhost:8080") + self.api_key = config.get("api_key", "") + self.timeout = config.get("timeout", 30) + + async def get_materials(self) -> Optional[Union[Dict[str, Any], List[Dict[str, Any]]]]: + """从Bioyond系统获取物料数据""" + try: + # 这里应该实现实际的API调用 + # 暂时返回模拟数据 + logger.info("从Bioyond API获取物料数据") + + # 模拟API调用延迟 + await asyncio.sleep(0.1) + + # 返回模拟数据(实际应该从API获取) + return { + "data": [], + "code": 1, + "message": "success", + "timestamp": 1234567890 + } + + except Exception as e: + logger.error(f"Bioyond API调用失败: {e}") + return None + + async def update_materials(self, materials: List[Dict[str, Any]]) -> bool: + """更新Bioyond系统中的物料数据""" + try: + # 这里应该实现实际的API调用 + logger.info(f"更新Bioyond系统中的 {len(materials)} 个物料") + + # 模拟API调用延迟 + await asyncio.sleep(0.1) + + # 模拟成功响应 + return True + + except Exception as e: + logger.error(f"更新Bioyond物料失败: {e}") + return False + + async def get_material_by_id(self, material_id: str) -> Optional[Dict[str, Any]]: + """根据ID获取单个物料""" + try: + # 这里应该实现实际的API调用 + logger.info(f"从Bioyond API获取物料: {material_id}") + + # 模拟API调用延迟 + await asyncio.sleep(0.1) + + # 返回模拟数据 + return { + "id": material_id, + "name": f"material_{material_id}", + "type": "container", + "quantity": 1.0, + "unit": "个" + } + + except Exception as e: + logger.error(f"获取Bioyond物料失败 {material_id}: {e}") + return None diff --git a/unilabos/devices/workstation/bioyond_cell/bioyond_workstation.py b/unilabos/devices/workstation/bioyond_cell/bioyond_workstation.py new file mode 100644 index 00000000..5ea406d6 --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/bioyond_workstation.py @@ -0,0 +1,796 @@ +# -*- coding: utf-8 -*- +from typing import Dict, Any, List, Optional +from datetime import datetime, timezone +import requests +from pathlib import Path +import pandas as pd +import time +from datetime import datetime, timezone, timedelta +import re +import threading + +from urllib3 import response +from unilabos.devices.workstation.workstation_base import WorkstationBase +from unilabos.devices.workstation.workstation_http_service import WorkstationHTTPService +from unilabos.utils.log import logger +from pylabrobot.resources.deck import Deck + +def _iso_utc_now_ms() -> str: + # 文档要求:到毫秒 + Z,例如 2025-08-15T05:43:22.814Z + dt = datetime.now() + return dt.strftime("%Y-%m-%dT%H:%M:%S.") + f"{int(dt.microsecond/1000):03d}Z" + + +class BioyondWorkstation(WorkstationBase): + """ + 集成 Bioyond LIMS 的工作站示例, + 覆盖:入库(2.17/2.18) → 新建实验(2.14) → 启动调度(2.7) → + 运行中推送:物料变更(2.24)、步骤完成(2.21)、订单完成(2.23) → + 查询实验(2.5/2.6) → 3-2-1 转运(2.32) → 样品/废料取出(2.28) + """ + + def __init__( + self, + bioyond_config: Optional[Dict[str, Any]] = None, + station_resource: Optional[Dict[str, Any]] = None, + debug_mode: bool = False, + *args, **kwargs, + ): + default_config = { + #"base_url": "http://192.168.1.200:44388", + "base_url": "http://61.169.57.196:44422", + "api_key": "8A819E5C", + "timeout": 30, + "report_token": "CHANGE_ME_TOKEN" + } + self.bioyond_config = {**default_config, **(bioyond_config or {})} + + self.http_service_started = False + self.debug_mode = debug_mode + super().__init__(deck=Deck, station_resource=station_resource, *args, **kwargs) + logger.info(f"Bioyond工作站初始化完成 (debug_mode={self.debug_mode})") + + # 实例化并在后台线程启动 HTTP 报送服务 + # self.order_status = {} + # try: + # t = threading.Thread(target=self._start_http_service_bg, daemon=True, name="unilab_http") + # t.start() + + # except Exception as e: + # logger.error(f"unilab-server后台启动报送服务失败: {e}") + + # @property + # def device_id(self) -> str: + # try: + # return getattr(self, "_ros_node").device_id # 兼容 ROS 场景 + # except Exception: + # return "bioyond_workstation" + + # def _start_http_service_bg(self, host: str = "192.168.1.104", port: int = 8080) -> None: + # logger.info("进入 _start_http_service_bg 函数") + # try: + # self.service = WorkstationHTTPService(self, host=host, port=port) + # logger.info("WorkstationHTTPService 实例化完成") + # self.service.start() + # self.http_service_started = True + # logger.info(f"unilab_HTTP 服务成功启动: {host}:{port}") + + # 一直挂着,直到进程退出 + # while True: + # time.sleep(1) + + # except Exception as e: + # self.http_service_started = False + # logger.error(f"启动unilab_HTTP服务失败: {e}", exc_info=True) + + # -------------------- 基础HTTP封装 -------------------- + def _url(self, path: str) -> str: + return f"{self.bioyond_config['base_url'].rstrip('/')}/{path.lstrip('/')}" + def _post_lims(self, path: str, data: Optional[Any] = None) -> Dict[str, Any]: + """LIMS API:大多数接口用 {apiKey/requestTime,data} 包装""" + payload = { + "apiKey": self.bioyond_config["api_key"], + "requestTime": _iso_utc_now_ms() + } + + if data is not None: + payload["data"] = data + + if self.debug_mode: + # 模拟返回,不发真实请求 + logger.info(f"[DEBUG] POST {path} with payload={payload}") + return {"debug": True, "url": self._url(path), "payload": payload, "status": "ok"} + try: + r = requests.post( + self._url(path), + json=payload, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"} + ) + r.raise_for_status() + #print(r.json()) + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + # --- 修正:_post_report / _post_report_raw 同样走 debug_mode --- + def _post_report(self, path: str, data: Dict[str, Any]) -> Dict[str, Any]: + payload = { + "token": self.bioyond_config.get("report_token", ""), + "request_time": _iso_utc_now_ms(), + "data": data + } + if self.debug_mode: + logger.info(f"[DEBUG] POST {path} with payload={payload}") + return {"debug": True, "url": self._url(path), "payload": payload, "status": "ok"} + try: + r = requests.post(self._url(path), json=payload, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"}) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + def _post_report_raw(self, path: str, body: Dict[str, Any]) -> Dict[str, Any]: + if self.debug_mode: + logger.info(f"[DEBUG] POST {path} with body={body}") + return {"debug": True, "url": self._url(path), "payload": body, "status": "ok"} + try: + r = requests.post(self._url(path), json=body, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"}) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + + # -------------------- 单点接口封装 -------------------- + # 2.17 入库物料(单个) + def storage_inbound(self, material_id: str, location_id: str) -> Dict[str, Any]: + return self._post_lims("/api/lims/storage/inbound", { + "materialId": material_id, + "locationId": location_id + }) + + # 2.18 批量入库(多个) + def storage_batch_inbound(self, items: List[Dict[str, str]]) -> Dict[str, Any]: + """ + items = [{"materialId": "...", "locationId": "..."}, ...] + """ + return self._post_lims("/api/lims/storage/batch-inbound", items) + + # 3.30 自动化上料(Excel -> JSON -> POST /api/lims/order/auto-feeding4to3) + def auto_feeding4to3_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: + """ + 根据固定模板解析 Excel: + - 四号手套箱加样头面 (2-13行, 3-7列) + - 四号手套箱原液瓶面 (15-23行, 3-9列) + - 三号手套箱人工堆栈 (26-40行, 3-7列) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, header=None, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + items: List[Dict[str, Any]] = [] + + # 四号手套箱 - 加样头面(2-13行, 3-7列) + for _, row in df.iloc[1:13, 2:7].iterrows(): + item = { + "sourceWHName": "四号手套箱堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", + "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, + } + if item["materialName"]: + items.append(item) + + # 四号手套箱 - 原液瓶面(15-23行, 3-9列) + for _, row in df.iloc[14:23, 2:9].iterrows(): + item = { + "sourceWHName": "四号手套箱堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", + "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, + "materialType": str(row[7]).strip() if pd.notna(row[7]) else "", + "targetWH": str(row[8]).strip() if pd.notna(row[8]) else "", + } + if item["materialName"]: + items.append(item) + + # 三号手套箱人工堆栈(26-40行, 3-7列) + for _, row in df.iloc[25:40, 2:7].iterrows(): + item = { + "sourceWHName": "三号手套箱人工堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialType": str(row[5]).strip() if pd.notna(row[5]) else "", + "materialId": str(row[6]).strip() if pd.notna(row[6]) else "", + "quantity": 1 # 默认数量1 + } + if item["materialId"] or item["materialType"]: + items.append(item) + + return self._post_lims("/api/lims/order/auto-feeding4to3", items) + + + + def auto_batch_outbound_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: + """ + 3.31 自动化下料(Excel -> JSON -> POST /api/lims/storage/auto-batch-out-bound) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + def pick(names: List[str]) -> Optional[str]: + for n in names: + if n in df.columns: + return n + return None + + c_loc = pick(["locationId", "库位ID", "库位Id", "库位id"]) + c_wh = pick(["warehouseId", "仓库ID", "仓库Id", "仓库id"]) + c_qty = pick(["数量", "quantity"]) + c_x = pick(["x", "X", "posX", "坐标X"]) + c_y = pick(["y", "Y", "posY", "坐标Y"]) + c_z = pick(["z", "Z", "posZ", "坐标Z"]) + + required = [c_loc, c_wh, c_qty, c_x, c_y, c_z] + if any(c is None for c in required): + raise KeyError("Excel 缺少必要列:locationId/warehouseId/数量/x/y/z(支持多别名,至少要能匹配到)。") + + def as_int(v, d=0): + try: + if pd.isna(v): return d + return int(v) + except Exception: + try: + return int(float(v)) + except Exception: + return d + + def as_float(v, d=0.0): + try: + if pd.isna(v): return d + return float(v) + except Exception: + return d + + def as_str(v, d=""): + if v is None or (isinstance(v, float) and pd.isna(v)): return d + s = str(v).strip() + return s if s else d + + items: List[Dict[str, Any]] = [] + for _, row in df.iterrows(): + items.append({ + "locationId": as_str(row[c_loc]), + "warehouseId": as_str(row[c_wh]), + "quantity": as_float(row[c_qty]), + "x": as_int(row[c_x]), + "y": as_int(row[c_y]), + "z": as_int(row[c_z]), + }) + + return self._post_lims("/api/lims/storage/auto-batch-out-bound", items) + + # 2.14 新建实验 + def create_orders(self, xlsx_path: str) -> Dict[str, Any]: + """ + 从 Excel 解析并创建实验(2.14) + 约定: + - batchId = Excel 文件名(不含扩展名) + - 物料列:所有以 "(g)" 结尾(不再读取“总质量(g)”列) + - totalMass 自动计算为所有物料质量之和 + - createTime 缺失或为空时自动填充为当前日期(YYYY/M/D) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + # 列名容错:返回可选列名,找不到则返回 None + def _pick(col_names: List[str]) -> Optional[str]: + for c in col_names: + if c in df.columns: + return c + return None + + col_order_name = _pick(["配方ID", "orderName", "订单编号"]) + col_create_time = _pick(["创建日期", "createTime"]) + col_bottle_type = _pick(["配液瓶类型", "bottleType"]) + col_mix_time = _pick(["混匀时间(s)", "mixTime"]) + col_load = _pick(["扣电组装分液体积", "loadSheddingInfo"]) + col_pouch = _pick(["软包组装分液体积", "pouchCellInfo"]) + col_cond = _pick(["电导测试分液体积", "conductivityInfo"]) + col_cond_cnt = _pick(["电导测试分液瓶数", "conductivityBottleCount"]) + + # 物料列:所有以 (g) 结尾 + material_cols = [c for c in df.columns if isinstance(c, str) and c.endswith("(g)")] + if not material_cols: + raise KeyError("未发现任何以“(g)”结尾的物料列,请检查表头。") + + batch_id = path.stem + + def _to_ymd_slash(v) -> str: + # 统一为 "YYYY/M/D";为空或解析失败则用当前日期 + if v is None or (isinstance(v, float) and pd.isna(v)) or str(v).strip() == "": + ts = datetime.now() + else: + try: + ts = pd.to_datetime(v) + except Exception: + ts = datetime.now() + return f"{ts.year}/{ts.month}/{ts.day}" + + def _as_int(val, default=0) -> int: + try: + if pd.isna(val): + return default + return int(val) + except Exception: + return default + + def _as_str(val, default="") -> str: + if val is None or (isinstance(val, float) and pd.isna(val)): + return default + s = str(val).strip() + return s if s else default + + orders: List[Dict[str, Any]] = [] + + for idx, row in df.iterrows(): + mats: List[Dict[str, Any]] = [] + total_mass = 0.0 + + for mcol in material_cols: + val = row.get(mcol, None) + if val is None or (isinstance(val, float) and pd.isna(val)): + continue + try: + mass = float(val) + except Exception: + continue + if mass > 0: + mats.append({"name": mcol.replace("(g)", ""), "mass": mass}) + total_mass += mass + + order_data = { + "batchId": batch_id, + "orderName": _as_str(row[col_order_name], default=f"{batch_id}_order_{idx+1}") if col_order_name else f"{batch_id}_order_{idx+1}", + "createTime": _to_ymd_slash(row[col_create_time]) if col_create_time else _to_ymd_slash(None), + "bottleType": _as_str(row[col_bottle_type], default="配液小瓶") if col_bottle_type else "配液小瓶", + "mixTime": _as_int(row[col_mix_time]) if col_mix_time else 0, + "loadSheddingInfo": _as_int(row[col_load]) if col_load else 0, + "pouchCellInfo": _as_int(row[col_pouch]) if col_pouch else 0, + "conductivityInfo": _as_int(row[col_cond]) if col_cond else 0, + "conductivityBottleCount": _as_int(row[col_cond_cnt]) if col_cond_cnt else 0, + "materialInfos": mats, + "totalMass": round(total_mass, 4) # 自动汇总 + } + orders.append(order_data) + + # print(orders) + while True: + time.sleep(5) + response = self._post_lims("/api/lims/order/orders", orders) + if response.get("data", []): + break + logger.info(f"等待配液实验创建完成") + + + + # self.order_status[response["data"]["orderCode"]] = "running" + + # while True: + # time.sleep(5) + # if self.order_status.get(response["data"]["orderCode"], None) == "finished": + # logger.info(f"配液实验已完成 ,即将执行 3-2-1 转运") + # break + # logger.info(f"等待配液实验完成") + + # self.transfer_3_to_2_to_1() + # self.wait_for_transfer_task() + # logger.info(f"3-2-1 转运完成,返回结果") + # return r321 + return response + + # 2.7 启动调度 + def scheduler_start(self) -> Dict[str, Any]: + response = self._post_lims("/api/lims/scheduler/start") + print(response) + return response + # 3.10 停止调度 + def scheduler_stop(self) -> Dict[str, Any]: + """ + 停止调度 (3.10) + 请求体只包含 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/scheduler/stop") + # 2.9 继续调度 + def scheduler_continue(self) -> Dict[str, Any]: + """ + 继续调度 (2.9) + 请求体只包含 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/scheduler/continue") + + + + # 2.24 物料变更推送 + def report_material_change(self, material_obj: Dict[str, Any]) -> Dict[str, Any]: + """ + material_obj 按 2.24 的裸对象格式(包含 id/typeName/locations/detail 等) + """ + return self._post_report_raw("/report/material_change", material_obj) + + # 2.21 步骤完成推送(BS → LIMS) + def report_step_finish(self, + order_code: str, + order_name: str, + step_name: str, + step_id: str, + sample_id: str, + start_time: str, + end_time: str, + execution_status: str = "completed") -> Dict[str, Any]: + data = { + "orderCode": order_code, + "orderName": order_name, + "stepName": step_name, + "stepId": step_id, + "sampleId": sample_id, + "startTime": start_time, + "endTime": end_time, + "executionStatus": execution_status + } + return self._post_report("/report/step_finish", data) + + # 2.23 订单完成推送(BS → LIMS) + def report_order_finish(self, + order_code: str, + order_name: str, + start_time: str, + end_time: str, + status: str = "30", # 30 完成 / -11 异常停止 / -12 人工停止 + workflow_status: str = "Finished", + completion_time: Optional[str] = None, + used_materials: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]: + data = { + "orderCode": order_code, + "orderName": order_name, + "startTime": start_time, + "endTime": end_time, + "status": status, + "workflowStatus": workflow_status, + "completionTime": completion_time or end_time, + "usedMaterials": used_materials or [] + } + return self._post_report("/report/order_finish", data) + + # 2.5 批量查询实验报告(用于轮询是否完成) + def order_list(self, + status: Optional[str] = None, + begin_time: Optional[str] = None, + end_time: Optional[str] = None, + filter_text: Optional[str] = None, + skip: int = 0, page: int = 10) -> Dict[str, Any]: + data: Dict[str, Any] = {"skipCount": skip, "pageCount": page} + if status is not None: # 80 成功 / 90 失败 / 100 执行中 + data["status"] = status + if begin_time: + data["timeType"] = "CreationTime" + data["beginTime"] = begin_time + if end_time: + data["endTime"] = end_time + if filter_text: + data["filter"] = filter_text + return self._post_lims("/api/lims/order/order-list", data) + + # 2.6 实验报告查询(根据任务ID拿详情) + def order_report(self, order_id: str) -> Dict[str, Any]: + return self._post_lims("/api/lims/order/order-report", order_id) + + # 2.32 3-2-1 物料转运 + def transfer_3_to_2_to_1(self, + # source_wh_id: Optional[str] = None, + source_wh_id: Optional[str] = '3a19debc-84b4-0359-e2d4-b3beea49348b', + source_x: int = 1, source_y: int = 1, source_z: int = 1) -> Dict[str, Any]: + payload: Dict[str, Any] = { + "sourcePosX": source_x, "sourcePosY": source_y, "sourcePosZ": source_z + } + if source_wh_id: + payload["sourceWHID"] = source_wh_id + return self._post_lims("/api/lims/order/transfer-task3To2To1", payload) + + # 2.28 样品/废料取出 + def take_out(self, + order_id: str, + preintake_ids: Optional[List[str]] = None, + material_ids: Optional[List[str]] = None) -> Dict[str, Any]: + data = { + "orderId": order_id, + "preintakeIds": preintake_ids or [], + "materialIds": material_ids or [] + } + return self._post_lims("/api/lims/order/take-out", data) + + # --------(可选)占位方法:文档未定义的“1号站内部流程 / 1-2转运”-------- + def start_station1_internal_flow(self, **kwargs) -> None: + logger.info("启动1号站内部流程(占位,按现场系统填充具体指令)") + + + # 3.x 1→2 物料转运 + def transfer_1_to_2(self) -> Dict[str, Any]: + """ + 1→2 物料转运 + URL: /api/lims/order/transfer-task1To2 + 只需要 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/order/transfer-task1To2") + + + # -------------------- 整体编排 -------------------- + def run_full_workflow(self, + inbound_items: List[Dict[str, str]], + orders: List[Dict[str, Any]], + poll_filter_code: Optional[str] = None, + poll_timeout_s: int = 600, + poll_interval_s: int = 5, + transfer_source: Optional[Dict[str, Any]] = None, + takeout_order_id: Optional[str] = None) -> None: + """ + 一键串联: + 1) 入库 3-4 个物料 → 2) 新建实验 → 3) 启动调度 + 运行中(如需):4) 物料变更推送 5) 步骤完成推送 6) 订单完成推送 + 完成后:查询实验(2.5/2.6)→ 7) 3-2-1 转运 → 8) 1号站内部流程 + → 9) 1-2 转运 → 10) 样品/废料取出 + """ + # 1. 入库(多于1个就用批量接口 2.18) + if len(inbound_items) == 1: + r = self.storage_inbound(inbound_items[0]["materialId"], inbound_items[0]["locationId"]) + logger.info(f"单个入库结果: {r}") + else: + r = self.storage_batch_inbound(inbound_items) + logger.info(f"批量入库结果: {r}") + + # 2. 新建实验(2.14) + r = self.create_orders(orders) + logger.info(f"新建实验结果: {r}") + + # 3. 启动调度(2.7) + r = self.scheduler_start() + logger.info(f"启动调度结果: {r}") + + # —— 运行中各类推送(2.24 / 2.21 / 2.23),通常由实际任务驱动,这里提供调用方式 —— # + # self.report_material_change({...}) + # self.report_step_finish(order_code="BSO...", order_name="配液分液", step_name="xxx", step_id="...", sample_id="...", + # start_time=_iso_utc_now_ms(), end_time=_iso_utc_now_ms(), execution_status="completed") + # self.report_order_finish(order_code="BSO...", order_name="配液分液", start_time="...", end_time=_iso_utc_now_ms()) + + # 完成后才能转运:用 2.5 批量查询配合 filter=任务编码 轮询到 status=80(成功) + if poll_filter_code: + import time + deadline = time.time() + poll_timeout_s + while time.time() < deadline: + res = self.order_list(status="80", filter_text=poll_filter_code, page=5) + if isinstance(res, dict) and res.get("data", {}).get("items"): + logger.info(f"实验 {poll_filter_code} 已完成:{res['data']['items'][0]}") + break + time.sleep(poll_interval_s) + else: + logger.warning(f"等待实验 {poll_filter_code} 完成超时(未到 status=80)") + + # 7. 启动 3-2-1 转运(2.32) + if transfer_source: + r = self.transfer_3_to_2_to_1( + source_wh_id=transfer_source.get("sourceWHID"), + source_x=transfer_source.get("sourcePosX", 1), + source_y=transfer_source.get("sourcePosY", 1), + source_z=transfer_source.get("sourcePosZ", 1), + ) + logger.info(f"3-2-1 转运结果: {r}") + + # 8. 1号站内部流程(占位) + self.start_station1_internal_flow() + + # 9. 1→2 转运(占位) + self.transfer_1_to_2() + + # 10. 样品/废料取出(2.28) + if takeout_order_id: + r = self.take_out(order_id=takeout_order_id) + logger.info(f"样品/废料取出结果: {r}") + + # 2.5 批量查询实验报告 + def order_list_v2(self, + timeType: str = "string", + beginTime: str = "", + endTime: str = "", + status: str = "", + filter: str = "", + skipCount: int = 0, + pageCount: int = 1, + sorting: str = "") -> Dict[str, Any]: + """ + 批量查询实验报告的详细信息 (2.5) + URL: /api/lims/order/order-list + 参数默认值和接口文档保持一致 + """ + data: Dict[str, Any] = { + "timeType": timeType, + "beginTime": beginTime, + "endTime": endTime, + "status": status, + "filter": filter, + "skipCount": skipCount, + "pageCount": pageCount, + "sorting": sorting + } + return self._post_lims("/api/lims/order/order-list", data) + + + def wait_for_transfer_task(self, timeout: int = 3000, interval: int = 5, filter_text: Optional[str] = None) -> bool: + """ + 轮询查询物料转移任务是否成功完成 (status=80) + - timeout: 最大等待秒数 (默认600秒) + - interval: 轮询间隔秒数 (默认3秒) + 返回 True 表示找到并成功完成,False 表示超时未找到 + """ + now = datetime.now() + beginTime = now.strftime("%Y-%m-%dT%H:%M:%SZ") + endTime = (now + timedelta(minutes=5)).strftime("%Y-%m-%dT%H:%M:%SZ") + print(beginTime, endTime) + + deadline = time.time() + timeout + + while time.time() < deadline: + result = self.order_list_v2( + timeType="string", + beginTime=beginTime, + endTime=endTime, + status="", + filter=filter_text, + skipCount=0, + pageCount=1, + sorting="" + ) + print(result) + + items = result.get("data", {}).get("items", []) + for item in items: + name = item.get("name", "") + status = item.get("status") + # 改成用 filter_text 判断 + if (not filter_text or filter_text in name) and status == 80: + logger.info(f"硬件转移动作完成: {name}, status={status}") + return True + + logger.info(f"等待中: {name}, status={status}") + time.sleep(interval) + + logger.warning("超时未找到成功的物料转移任务") + return False + + + def Bioystation_scheduler_start_task(self) -> bool: + logger.info("开始调度") + self.scheduler_start() + logger.info("调度已启动") + + def Bioystation_scheduler_stop_task(self) -> bool: + logger.info("停止调度") + self.scheduler_stop() + logger.info("调度已停止") + + def Bioystation_scheduler_continue_task(self) -> bool: + logger.info("继续调度") + self.scheduler_continue() + logger.info("调度已继续") + + # 3.30 上料:读取模板 Excel 自动解析并 POST + def Bioystation_feeding4to3_from_xlsx_task(self) -> bool: + logger.info("4号箱自动上料开始") + r1 = self.auto_feeding4to3_from_xlsx(r"D:\Uni-lab\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\样品导入模板.xlsx") + self.wait_for_transfer_task(filter_text="物料转移任务") + logger.info("4号箱向3号箱转运物料转移任务已完成") + return True + + # # 新建实验 + def Bioystation_start_experiment_task(self) -> bool: + logger.info("3号箱内实验开始") + response = self.create_orders(r"D:\Uni-lab\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\2025101301.xlsx") + logger.info(response) + data_list = response.get("data", []) + order_name = data_list[0].get("orderName", "") + self.wait_for_transfer_task(filter_text=order_name) + logger.info("3号站内实验完成") + return True + + def Bioystation_3_to_2_task(self) -> bool: + self.transfer_3_to_2_to_1() + self.wait_for_transfer_task(filter_text="物料转移任务") + logger.info("3号站向2号站向1号站转移任务完成") + return True + + def Bioystation_1_to_2_task(self) -> bool: + self.transfer_1_to_2() + self.wait_for_transfer_task(filter_text="物料转移任务") + logger.info("1号站向2号站转移任务完成") + logger.info("全流程结束") + return True + + def test_benyao_workstation(self, num1, num2): + num1 = int(num1) + num2 = int(num2) + for i in range(num1): + print(f"num1 = {num1}") + for j in range(num2): + print(f"num1 = {num2}") + +# -------------------------------- +if __name__ == "__main__": + ws = BioyondWorkstation() + #ws.scheduler_stop() + #ws.Bioystation_scheduler_start_task() + ws.scheduler_start() + # ws.scheduler_start() + # logger.info("调度启动完成") + + # ws.scheduler_continue() + # 3.30 上料:读取模板 Excel 自动解析并 POST + # r1 = ws.auto_feeding4to3_from_xlsx(r"C:\ML\GitHub\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\样品导入模板 (8).xlsx") + # ws.wait_for_transfer_task(filter_text="物料转移任务") + # logger.info("4号箱向3号箱转运物料转移任务已完成") + + # ws.scheduler_start() + # print(r1["payload"]["data"]) # 调试模式下可直接看到要发的 JSON items + + # # 新建实验 + # response = ws.create_orders("C:/ML/GitHub/Uni-Lab-OS/unilabos/devices/workstation/bioyond_cell/2025092701.xlsx") + # logger.info(response) + # data_list = response.get("data", []) + # order_name = data_list[0].get("orderName", "") + + # ws.wait_for_transfer_task(filter_text=order_name) + # ws.wait_for_transfer_task(filter_text='DP20250927001') + # logger.info("3号站内实验完成") + # # ws.scheduler_start() + # # print(res) + # ws.transfer_3_to_2_to_1() + # ws.wait_for_transfer_task(filter_text="物料转移任务") + # logger.info("3号站向2号站向1号站转移任务完成") + # r321 = self.wait_for_transfer_task() + #1号站启动 + # ws.transfer_1_to_2() + #s.wait_for_transfer_task(filter_text="物料转移任务") + #ogger.info("1号站向2号站转移任务完成") + #ogger.info("全流程结束") + + # 3.31 下料:同理 + # r2 = ws.auto_batch_outbound_from_xlsx(r"C:/path/样品导入模板 (8).xlsx") + # print(r2["payload"]["data"]) diff --git a/unilabos/devices/workstation/bioyond_cell/bioyond_workstation_old (2).py b/unilabos/devices/workstation/bioyond_cell/bioyond_workstation_old (2).py new file mode 100644 index 00000000..fae8da12 --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/bioyond_workstation_old (2).py @@ -0,0 +1,772 @@ +# -*- coding: utf-8 -*- +from typing import Dict, Any, List, Optional +from datetime import datetime, timezone +import requests +from pathlib import Path +import pandas as pd +import time +from datetime import datetime, timezone, timedelta +import re +import threading +from unilabos.devices.workstation.workstation_base import WorkstationBase +from unilabos.devices.workstation.workstation_http_service import WorkstationHTTPService +from unilabos.utils.log import logger +from pylabrobot.resources.deck import Deck + + +def _iso_utc_now_ms() -> str: + # 文档要求:到毫秒 + Z,例如 2025-08-15T05:43:22.814Z + dt = datetime.now(timezone.utc) + return dt.strftime("%Y-%m-%dT%H:%M:%S.") + f"{int(dt.microsecond/1000):03d}Z" + + +class BioyondWorkstation(WorkstationBase): + """ + 集成 Bioyond LIMS 的工作站示例, + 覆盖:入库(2.17/2.18) → 新建实验(2.14) → 启动调度(2.7) → + 运行中推送:物料变更(2.24)、步骤完成(2.21)、订单完成(2.23) → + 查询实验(2.5/2.6) → 3-2-1 转运(2.32) → 样品/废料取出(2.28) + """ + + def __init__( + self, + bioyond_config: Optional[Dict[str, Any]] = None, + station_resource: Optional[Dict[str, Any]] = None, + debug_mode: bool = False, # 增加调试模式开关 + *args, **kwargs, + ): + self.bioyond_config = bioyond_config or { + #"base_url": "http://192.168.1.200:44386", + #"base_url": "http://172.16.11.219:44388", + "base_url": "http://61.169.57.196:44422", + "api_key": "8A819E5C", + "timeout": 30, + "report_token": "CHANGE_ME_TOKEN" + } + + + self.debug_mode = debug_mode + super().__init__(deck=Deck, station_resource=station_resource, *args, **kwargs) + logger.info(f"Bioyond工作站初始化完成 (debug_mode={self.debug_mode})") + + # 实例化并在后台线程启动 HTTP 报送服务 + self.order_status = {} + try: + t = threading.Thread(target=self._start_http_service_bg, daemon=True, name="unilab_http") + t.start() + + except Exception as e: + logger.error(f"unilab-server后台启动报送服务失败: {e}") + + @property + def device_id(self) -> str: + try: + return getattr(self, "_ros_node").device_id # 兼容 ROS 场景 + except Exception: + return "bioyond_workstation" + + def _start_http_service_bg(self, host: str = "192.168.1.104", port: int = 7000) -> None: + try: + self.service = WorkstationHTTPService(self, host=host, port=port) + self.service.start() + + except Exception as e: + logger.error(f"启动HTTP服务失败: {e}") + + # -------------------- 基础HTTP封装 -------------------- + def _url(self, path: str) -> str: + return f"{self.bioyond_config['base_url'].rstrip('/')}/{path.lstrip('/')}" + + def _post_lims(self, path: str, data: Optional[Any] = None) -> Dict[str, Any]: + """LIMS API:大多数接口用 {apiKey/requestTime,data} 包装""" + payload = { + "apiKey": self.bioyond_config["api_key"], + "requestTime": _iso_utc_now_ms() + } + if data is not None: + payload["data"] = data + + if self.debug_mode: + # 模拟返回,不发真实请求 + logger.info(f"[DEBUG] POST {path} with payload={payload}") + return {"debug": True, "url": self._url(path), "payload": payload, "status": "ok"} + + try: + r = requests.post( + self._url(path), + json=payload, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"} + ) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + # --- 修正:_post_report / _post_report_raw 同样走 debug_mode --- + def _post_report(self, path: str, data: Dict[str, Any]) -> Dict[str, Any]: + payload = { + "token": self.bioyond_config.get("report_token", ""), + "request_time": _iso_utc_now_ms(), + "data": data + } + if self.debug_mode: + logger.info(f"[DEBUG] POST {path} with payload={payload}") + return {"debug": True, "url": self._url(path), "payload": payload, "status": "ok"} + try: + r = requests.post(self._url(path), json=payload, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"}) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + def _post_report_raw(self, path: str, body: Dict[str, Any]) -> Dict[str, Any]: + if self.debug_mode: + logger.info(f"[DEBUG] POST {path} with body={body}") + return {"debug": True, "url": self._url(path), "payload": body, "status": "ok"} + try: + r = requests.post(self._url(path), json=body, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"}) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + + # -------------------- 单点接口封装 -------------------- + # 2.17 入库物料(单个) + def storage_inbound(self, material_id: str, location_id: str) -> Dict[str, Any]: + return self._post_lims("/api/lims/storage/inbound", { + "materialId": material_id, + "locationId": location_id + }) + + # 2.18 批量入库(多个) + def storage_batch_inbound(self, items: List[Dict[str, str]]) -> Dict[str, Any]: + """ + items = [{"materialId": "...", "locationId": "..."}, ...] + """ + return self._post_lims("/api/lims/storage/batch-inbound", items) + + # 3.30 自动化上料(Excel -> JSON -> POST /api/lims/order/auto-feeding4to3) + def auto_feeding4to3_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: + """ + 根据固定模板解析 Excel: + - 四号手套箱加样头面 (2-13行, 3-7列) + - 四号手套箱原液瓶面 (15-23行, 3-9列) + - 三号手套箱人工堆栈 (26-40行, 3-7列) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, header=None, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + items: List[Dict[str, Any]] = [] + + # 四号手套箱 - 加样头面(2-13行, 3-7列) + for _, row in df.iloc[1:13, 2:7].iterrows(): + item = { + "sourceWHName": "四号手套箱堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", + "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, + } + if item["materialName"]: + items.append(item) + + # 四号手套箱 - 原液瓶面(15-23行, 3-9列) + for _, row in df.iloc[14:23, 2:9].iterrows(): + item = { + "sourceWHName": "四号手套箱堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", + "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, + "materialType": str(row[7]).strip() if pd.notna(row[7]) else "", + "targetWH": str(row[8]).strip() if pd.notna(row[8]) else "", + } + if item["materialName"]: + items.append(item) + + # 三号手套箱人工堆栈(26-40行, 3-7列) + for _, row in df.iloc[25:40, 2:7].iterrows(): + item = { + "sourceWHName": "三号手套箱人工堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialType": str(row[5]).strip() if pd.notna(row[5]) else "", + "materialId": str(row[6]).strip() if pd.notna(row[6]) else "", + "quantity": 1 # 默认数量1 + } + if item["materialId"] or item["materialType"]: + items.append(item) + print("items", items) + return self._post_lims("/api/lims/order/auto-feeding4to3", items) + + + + def auto_batch_outbound_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: + """ + 3.31 自动化下料(Excel -> JSON -> POST /api/lims/storage/auto-batch-out-bound) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + def pick(names: List[str]) -> Optional[str]: + for n in names: + if n in df.columns: + return n + return None + + c_loc = pick(["locationId", "库位ID", "库位Id", "库位id"]) + c_wh = pick(["warehouseId", "仓库ID", "仓库Id", "仓库id"]) + c_qty = pick(["数量", "quantity"]) + c_x = pick(["x", "X", "posX", "坐标X"]) + c_y = pick(["y", "Y", "posY", "坐标Y"]) + c_z = pick(["z", "Z", "posZ", "坐标Z"]) + + required = [c_loc, c_wh, c_qty, c_x, c_y, c_z] + if any(c is None for c in required): + raise KeyError("Excel 缺少必要列:locationId/warehouseId/数量/x/y/z(支持多别名,至少要能匹配到)。") + + def as_int(v, d=0): + try: + if pd.isna(v): return d + return int(v) + except Exception: + try: + return int(float(v)) + except Exception: + return d + + def as_float(v, d=0.0): + try: + if pd.isna(v): return d + return float(v) + except Exception: + return d + + def as_str(v, d=""): + if v is None or (isinstance(v, float) and pd.isna(v)): return d + s = str(v).strip() + return s if s else d + + items: List[Dict[str, Any]] = [] + for _, row in df.iterrows(): + items.append({ + "locationId": as_str(row[c_loc]), + "warehouseId": as_str(row[c_wh]), + "quantity": as_float(row[c_qty]), + "x": as_int(row[c_x]), + "y": as_int(row[c_y]), + "z": as_int(row[c_z]), + }) + + return self._post_lims("/api/lims/storage/auto-batch-out-bound", items) + + # 2.14 新建实验 + def create_orders(self, xlsx_path: str) -> Dict[str, Any]: + """ + 从 Excel 解析并创建实验(2.14) + 约定: + - batchId = Excel 文件名(不含扩展名) + - 物料列:所有以 "(g)" 结尾(不再读取“总质量(g)”列) + - totalMass 自动计算为所有物料质量之和 + - createTime 缺失或为空时自动填充为当前日期(YYYY/M/D) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + # 列名容错:返回可选列名,找不到则返回 None + def _pick(col_names: List[str]) -> Optional[str]: + for c in col_names: + if c in df.columns: + return c + return None + + col_order_name = _pick(["配方ID", "orderName", "订单编号"]) + col_create_time = _pick(["创建日期", "createTime"]) + col_bottle_type = _pick(["配液瓶类型", "bottleType"]) + col_mix_time = _pick(["混匀时间(s)", "mixTime"]) + col_load = _pick(["扣电组装分液体积", "loadSheddingInfo"]) + col_pouch = _pick(["软包组装分液体积", "pouchCellInfo"]) + col_cond = _pick(["电导测试分液体积", "conductivityInfo"]) + col_cond_cnt = _pick(["电导测试分液瓶数", "conductivityBottleCount"]) + + # 物料列:所有以 (g) 结尾 + material_cols = [c for c in df.columns if isinstance(c, str) and c.endswith("(g)")] + if not material_cols: + raise KeyError("未发现任何以“(g)”结尾的物料列,请检查表头。") + + batch_id = path.stem + + def _to_ymd_slash(v) -> str: + # 统一为 "YYYY/M/D";为空或解析失败则用当前日期 + if v is None or (isinstance(v, float) and pd.isna(v)) or str(v).strip() == "": + ts = datetime.now() + else: + try: + ts = pd.to_datetime(v) + except Exception: + ts = datetime.now() + return f"{ts.year}/{ts.month}/{ts.day}" + + def _as_int(val, default=0) -> int: + try: + if pd.isna(val): + return default + return int(val) + except Exception: + return default + + def _as_str(val, default="") -> str: + if val is None or (isinstance(val, float) and pd.isna(val)): + return default + s = str(val).strip() + return s if s else default + + orders: List[Dict[str, Any]] = [] + + for idx, row in df.iterrows(): + mats: List[Dict[str, Any]] = [] + total_mass = 0.0 + + for mcol in material_cols: + val = row.get(mcol, None) + if val is None or (isinstance(val, float) and pd.isna(val)): + continue + try: + mass = float(val) + except Exception: + continue + if mass > 0: + mats.append({"name": mcol.replace("(g)", ""), "mass": mass}) + total_mass += mass + + order_data = { + "batchId": batch_id, + "orderName": _as_str(row[col_order_name], default=f"{batch_id}_order_{idx+1}") if col_order_name else f"{batch_id}_order_{idx+1}", + "createTime": _to_ymd_slash(row[col_create_time]) if col_create_time else _to_ymd_slash(None), + "bottleType": _as_str(row[col_bottle_type], default="配液小瓶") if col_bottle_type else "配液小瓶", + "mixTime": _as_int(row[col_mix_time]) if col_mix_time else 0, + "loadSheddingInfo": _as_int(row[col_load]) if col_load else 0, + "pouchCellInfo": _as_int(row[col_pouch]) if col_pouch else 0, + "conductivityInfo": _as_int(row[col_cond]) if col_cond else 0, + "conductivityBottleCount": _as_int(row[col_cond_cnt]) if col_cond_cnt else 0, + "materialInfos": mats, + "totalMass": round(total_mass, 4) # 自动汇总 + } + orders.append(order_data) + + # print(orders) + + response = self._post_lims("/api/lims/order/orders", orders) + print(response["data"]) + + self.order_status[response["data"][0]["orderCode"]] = "running" + + while True: + time.sleep(5) + if self.order_status.get(response["data"][0]["orderCode"], None) == "finished": + break + return response + + + # 2.7 启动调度 + def scheduler_start(self) -> Dict[str, Any]: + return self._post_lims("/api/lims/scheduler/start") + # 3.10 停止调度 + def scheduler_stop(self) -> Dict[str, Any]: + """ + 停止调度 (3.10) + 请求体只包含 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/scheduler/stop") + # 2.9 继续调度 + def scheduler_continue(self) -> Dict[str, Any]: + """ + 继续调度 (2.9) + 请求体只包含 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/scheduler/continue") + + + + # 2.24 物料变更推送 + def report_material_change(self, material_obj: Dict[str, Any]) -> Dict[str, Any]: + """ + material_obj 按 2.24 的裸对象格式(包含 id/typeName/locations/detail 等) + """ + return self._post_report_raw("/report/material_change", material_obj) + + # 2.21 步骤完成推送(BS → LIMS) + def report_step_finish(self, + order_code: str, + order_name: str, + step_name: str, + step_id: str, + sample_id: str, + start_time: str, + end_time: str, + execution_status: str = "completed") -> Dict[str, Any]: + data = { + "orderCode": order_code, + "orderName": order_name, + "stepName": step_name, + "stepId": step_id, + "sampleId": sample_id, + "startTime": start_time, + "endTime": end_time, + "executionStatus": execution_status + } + return self._post_report("/report/step_finish", data) + + # 2.23 订单完成推送(BS → LIMS) + def report_order_finish(self, + order_code: str, + order_name: str, + start_time: str, + end_time: str, + status: str = "30", # 30 完成 / -11 异常停止 / -12 人工停止 + workflow_status: str = "Finished", + completion_time: Optional[str] = None, + used_materials: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]: + data = { + "orderCode": order_code, + "orderName": order_name, + "startTime": start_time, + "endTime": end_time, + "status": status, + "workflowStatus": workflow_status, + "completionTime": completion_time or end_time, + "usedMaterials": used_materials or [] + } + return self._post_report("/report/order_finish", data) + + # 2.5 批量查询实验报告(用于轮询是否完成) + def order_list(self, + status: Optional[str] = None, + begin_time: Optional[str] = None, + end_time: Optional[str] = None, + filter_text: Optional[str] = None, + skip: int = 0, page: int = 10) -> Dict[str, Any]: + data: Dict[str, Any] = {"skipCount": skip, "pageCount": page} + if status is not None: # 80 成功 / 90 失败 / 100 执行中 + data["status"] = status + if begin_time: + data["timeType"] = "CreationTime" + data["beginTime"] = begin_time + if end_time: + data["endTime"] = end_time + if filter_text: + data["filter"] = filter_text + return self._post_lims("/api/lims/order/order-list", data) + + # 2.6 实验报告查询(根据任务ID拿详情) + def order_report(self, order_id: str) -> Dict[str, Any]: + return self._post_lims("/api/lims/order/order-report", order_id) + + # 2.32 3-2-1 物料转运 + def transfer_3_to_2_to_1(self, + # source_wh_id: Optional[str] = None, + source_wh_id: Optional[str] = '3a19debc-84b4-0359-e2d4-b3beea49348b', + source_x: int = 1, source_y: int = 1, source_z: int = 1) -> Dict[str, Any]: + payload: Dict[str, Any] = { + "sourcePosX": source_x, "sourcePosY": source_y, "sourcePosZ": source_z + } + if source_wh_id: + payload["sourceWHID"] = source_wh_id + return self._post_lims("/api/lims/order/transfer-task3To2To1", payload) + + # 2.28 样品/废料取出 + def take_out(self, + order_id: str, + preintake_ids: Optional[List[str]] = None, + material_ids: Optional[List[str]] = None) -> Dict[str, Any]: + data = { + "orderId": order_id, + "preintakeIds": preintake_ids or [], + "materialIds": material_ids or [] + } + return self._post_lims("/api/lims/order/take-out", data) + + # --------(可选)占位方法:文档未定义的“1号站内部流程 / 1-2转运”-------- + def start_station1_internal_flow(self, **kwargs) -> None: + logger.info("启动1号站内部流程(占位,按现场系统填充具体指令)") + + + # 3.x 1→2 物料转运 + def transfer_1_to_2(self) -> Dict[str, Any]: + """ + 1→2 物料转运 + URL: /api/lims/order/transfer-task1To2 + 只需要 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/order/transfer-task1To2") + + + # -------------------- 整体编排 -------------------- + def run_full_workflow(self, + inbound_items: List[Dict[str, str]], + orders: List[Dict[str, Any]], + poll_filter_code: Optional[str] = None, + poll_timeout_s: int = 600, + poll_interval_s: int = 5, + transfer_source: Optional[Dict[str, Any]] = None, + takeout_order_id: Optional[str] = None) -> None: + """ + 一键串联: + 1) 入库 3-4 个物料 → 2) 新建实验 → 3) 启动调度 + 运行中(如需):4) 物料变更推送 5) 步骤完成推送 6) 订单完成推送 + 完成后:查询实验(2.5/2.6)→ 7) 3-2-1 转运 → 8) 1号站内部流程 + → 9) 1-2 转运 → 10) 样品/废料取出 + """ + # 1. 入库(多于1个就用批量接口 2.18) + if len(inbound_items) == 1: + r = self.storage_inbound(inbound_items[0]["materialId"], inbound_items[0]["locationId"]) + logger.info(f"单个入库结果: {r}") + else: + r = self.storage_batch_inbound(inbound_items) + logger.info(f"批量入库结果: {r}") + + # 2. 新建实验(2.14) + r = self.create_orders(orders) + logger.info(f"新建实验结果: {r}") + + # 3. 启动调度(2.7) + r = self.scheduler_start() + logger.info(f"启动调度结果: {r}") + + # —— 运行中各类推送(2.24 / 2.21 / 2.23),通常由实际任务驱动,这里提供调用方式 —— # + # self.report_material_change({...}) + # self.report_step_finish(order_code="BSO...", order_name="配液分液", step_name="xxx", step_id="...", sample_id="...", + # start_time=_iso_utc_now_ms(), end_time=_iso_utc_now_ms(), execution_status="completed") + # self.report_order_finish(order_code="BSO...", order_name="配液分液", start_time="...", end_time=_iso_utc_now_ms()) + + # 完成后才能转运:用 2.5 批量查询配合 filter=任务编码 轮询到 status=80(成功) + if poll_filter_code: + import time + deadline = time.time() + poll_timeout_s + while time.time() < deadline: + res = self.order_list(status="80", filter_text=poll_filter_code, page=5) + if isinstance(res, dict) and res.get("data", {}).get("items"): + logger.info(f"实验 {poll_filter_code} 已完成:{res['data']['items'][0]}") + break + time.sleep(poll_interval_s) + else: + logger.warning(f"等待实验 {poll_filter_code} 完成超时(未到 status=80)") + + # 7. 启动 3-2-1 转运(2.32) + if transfer_source: + r = self.transfer_3_to_2_to_1( + source_wh_id=transfer_source.get("sourceWHID"), + source_x=transfer_source.get("sourcePosX", 1), + source_y=transfer_source.get("sourcePosY", 1), + source_z=transfer_source.get("sourcePosZ", 1), + ) + logger.info(f"3-2-1 转运结果: {r}") + + # 8. 1号站内部流程(占位) + self.start_station1_internal_flow() + + # 9. 1→2 转运(占位) + self.transfer_1_to_2() + + # 10. 样品/废料取出(2.28) + if takeout_order_id: + r = self.take_out(order_id=takeout_order_id) + logger.info(f"样品/废料取出结果: {r}") + + # 2.5 批量查询实验报告 + def order_list_v2(self, + timeType: str = "string", + beginTime: str = "", + endTime: str = "", + status: str = "", + filter: str = "物料转移任务", + skipCount: int = 0, + pageCount: int = 1, + sorting: str = "") -> Dict[str, Any]: + """ + 批量查询实验报告的详细信息 (2.5) + URL: /api/lims/order/order-list + 参数默认值和接口文档保持一致 + """ + data: Dict[str, Any] = { + "timeType": timeType, + "beginTime": beginTime, + "endTime": endTime, + "status": status, + "filter": filter, + "skipCount": skipCount, + "pageCount": pageCount, + "sorting": sorting + } + return self._post_lims("/api/lims/order/order-list", data) + + + def wait_for_transfer_task(self, timeout: int = 600, interval: int = 3) -> bool: + """ + 轮询查询物料转移任务是否成功完成 (status=80) + - timeout: 最大等待秒数 (默认600秒) + - interval: 轮询间隔秒数 (默认3秒) + 返回 True 表示找到并成功完成,False 表示超时未找到 + """ + now = datetime.now() + beginTime = now.strftime("%Y-%m-%dT%H:%M:%SZ") + endTime = (now + timedelta(minutes=5)).strftime("%Y-%m-%dT%H:%M:%SZ") + print(beginTime, endTime) + + deadline = time.time() + timeout + + while time.time() < deadline: + result = self.order_list_v2( + timeType="string", + beginTime=beginTime, + endTime=endTime, + status="", + filter="物料转移任务", + skipCount=0, + pageCount=1, + sorting="" + ) + print(result) + + items = result.get("data", {}).get("items", []) + for item in items: + name = item.get("name", "") + status = item.get("status") + if name.startswith("物料转移任务") and status == 80: + logger.info(f"硬件转移动作完成: {name}") + return True + + time.sleep(interval) + + logger.warning("超时未找到成功的物料转移任务") + return False + + def wait_for_recent_task(self, timeout: int = 600, interval: int = 3) -> bool: + """ + 轮询查询最近的任务是否成功完成 (status=80) + - timeout: 最大等待秒数 (默认600秒) + - interval: 轮询间隔秒数 (默认3秒) + 返回 True 表示找到并成功完成,False 表示超时未找到 + """ + now = datetime.now() + beginTime = now.strftime("%Y-%m-%dT%H:%M:%SZ") + endTime = (now + timedelta(minutes=5)).strftime("%Y-%m-%dT%H:%M:%SZ") + print(beginTime, endTime) + + deadline = time.time() + timeout + + while time.time() < deadline: + result = self.order_list_v2( + timeType="string", + beginTime=beginTime, + endTime=endTime, + status="", + filter="", + skipCount=0, + pageCount=1, + sorting="" + ) + print(result) + + items = result.get("data", {}).get("items", []) + for item in items: + name = item.get("name", "") + status = item.get("status") + if name.startswith("物料转移任务") and status == 80: + logger.info(f"硬件转移动作完成: {name}") + return True + + time.sleep(interval) + + logger.warning("超时未找到成功的任务") + return False + +# -------------------------------- +if __name__ == "__main__": + ws = BioyondWorkstation() + ws.scheduler_stop() + ws.scheduler_start() + #物料入库 + r1 = ws.auto_feeding4to3_from_xlsx(r"D:\Uni-lab\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\样品导入模板.xlsx") +# print(r1) +# print("物料入库任务已提交0") +# #等待任务完成 +# ws.wait_for_transfer_task() +# +# print("物料入库任务已完成1") +# +# #新建实验 +# res = ws.create_orders(r"D:\Uni-lab\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\2025092501.xlsx") +# print(res) +# #等待任务完成 +# ws.wait_for_recent_task() +# print("配液任务已完成") +# +# #新建3-2-1转运任务 +# r321 = ws.transfer_3_to_2_to_1() +# print(r321) +# #等待任务完成 +# ws.wait_for_recent_task() +# +# ws.transfer_1_to_2() +# #等待任务完成 +# ws.wait_for_recent_task() + + + #ws._start_http_service_bg() + # ws.scheduler_stop() + #ws.scheduler_start() + # ws.scheduler_continue() + # 3.30 上料:读取模板 Excel 自动解析并 POST + # r1 = ws.auto_feeding4to3_from_xlsx(r"C:\ML\GitHub\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\样品导入模板 (8).xlsx") + #ws.wait_for_transfer_task() + #print("转运物料转移任务已完成") + + # ws.scheduler_start() + # print(r1["payload"]["data"]) # 调试模式下可直接看到要发的 JSON items + + # 新建实验 + # res = ws.create_orders("C:/ML/GitHub/Uni-Lab-OS/unilabos/devices/workstation/bioyond_cell/2025092501.xlsx") + # ws.scheduler_start() + # print(res) + # r321 = ws.transfer_3_to_2_to_1() + + + # 3.31 下料:同理 + # r2 = ws.auto_batch_outbound_from_xlsx(r"C:/path/样品导入模板 (8).xlsx") + # print(r2["payload"]["data"]) + + # r321 = ws.transfer_3_to_2_to_1() + # ws.transfer_1_to_2() + + diff --git a/unilabos/devices/workstation/bioyond_cell/bioyond_workstation_old.py b/unilabos/devices/workstation/bioyond_cell/bioyond_workstation_old.py new file mode 100644 index 00000000..c13bce3a --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/bioyond_workstation_old.py @@ -0,0 +1,644 @@ +# -*- coding: utf-8 -*- +from typing import Dict, Any, List, Optional +from datetime import datetime, timezone +import requests +from pathlib import Path +import pandas as pd +import time +import threading +from unilabos.devices.workstation.workstation_base import WorkstationBase +from unilabos.devices.workstation.workstation_http_service import WorkstationHTTPService +from unilabos.utils.log import logger +from pylabrobot.resources.deck import Deck +from .benyao_test import test_benyao_api + +def _iso_utc_now_ms() -> str: + # 文档要求:到毫秒 + Z,例如 2025-08-15T05:43:22.814Z + dt = datetime.now(timezone.utc) + return dt.strftime("%Y-%m-%dT%H:%M:%S.") + f"{int(dt.microsecond/1000):03d}Z" + + +class BioyondWorkstation(WorkstationBase): + """ + 集成 Bioyond LIMS 的工作站示例, + 覆盖:入库(2.17/2.18) → 新建实验(2.14) → 启动调度(2.7) → + 运行中推送:物料变更(2.24)、步骤完成(2.21)、订单完成(2.23) → + 查询实验(2.5/2.6) → 3-2-1 转运(2.32) → 样品/废料取出(2.28) + """ + + def __init__( + self, + bioyond_config: Optional[Dict[str, Any]] = None, + station_resource: Optional[Dict[str, Any]] = None, + debug_mode: bool = False, # 增加调试模式开关 + *args, **kwargs, + ): + self.bioyond_config = bioyond_config or { + "base_url": "http://192.168.1.200:44386", + "api_key": "8A819E5C", + "timeout": 30, + "report_token": "CHANGE_ME_TOKEN" + } + + + self.debug_mode = debug_mode + super().__init__(deck=Deck, station_resource=station_resource, *args, **kwargs) + logger.info(f"Bioyond工作站初始化完成 (debug_mode={self.debug_mode})") + + # 实例化并在后台线程启动 HTTP 报送服务 + self.order_status = {} + try: + t = threading.Thread(target=self._start_http_service_bg, daemon=True, name="unilab_http") + t.start() + except Exception as e: + logger.error(f"unilab-server后台启动报送服务失败: {e}") + + @property + def device_id(self) -> str: + try: + return getattr(self, "_ros_node").device_id # 兼容 ROS 场景 + except Exception: + return "bioyond_workstation" + + def _start_http_service_bg(self, host: str = "127.0.0.1", port: int = 8080) -> None: + try: + self.service = WorkstationHTTPService(self, host=host, port=port) + self.service.start() + except Exception as e: + logger.error(f"启动HTTP服务失败: {e}") + + # -------------------- 基础HTTP封装 -------------------- + def _url(self, path: str) -> str: + return f"{self.bioyond_config['base_url'].rstrip('/')}/{path.lstrip('/')}" + + def _post_lims(self, path: str, data: Optional[Any] = None) -> Dict[str, Any]: + """LIMS API:大多数接口用 {apiKey/requestTime,data} 包装""" + payload = { + "apiKey": self.bioyond_config["api_key"], + "requestTime": _iso_utc_now_ms() + } + if data is not None: + payload["data"] = data + + if self.debug_mode: + # 模拟返回,不发真实请求 + logger.info(f"[DEBUG] POST {path} with payload={payload}") + return {"debug": True, "url": self._url(path), "payload": payload, "status": "ok"} + + try: + r = requests.post( + self._url(path), + json=payload, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"} + ) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + # --- 修正:_post_report / _post_report_raw 同样走 debug_mode --- + def _post_report(self, path: str, data: Dict[str, Any]) -> Dict[str, Any]: + payload = { + "token": self.bioyond_config.get("report_token", ""), + "request_time": _iso_utc_now_ms(), + "data": data + } + if self.debug_mode: + logger.info(f"[DEBUG] POST {path} with payload={payload}") + return {"debug": True, "url": self._url(path), "payload": payload, "status": "ok"} + try: + r = requests.post(self._url(path), json=payload, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"}) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + def _post_report_raw(self, path: str, body: Dict[str, Any]) -> Dict[str, Any]: + if self.debug_mode: + logger.info(f"[DEBUG] POST {path} with body={body}") + return {"debug": True, "url": self._url(path), "payload": body, "status": "ok"} + try: + r = requests.post(self._url(path), json=body, + timeout=self.bioyond_config.get("timeout", 30), + headers={"Content-Type": "application/json"}) + r.raise_for_status() + return r.json() + except Exception as e: + logger.error(f"POST {path} 失败: {e}") + return {"error": str(e)} + + + # -------------------- 单点接口封装 -------------------- + # 2.17 入库物料(单个) + def storage_inbound(self, material_id: str, location_id: str) -> Dict[str, Any]: + return self._post_lims("/api/lims/storage/inbound", { + "materialId": material_id, + "locationId": location_id + }) + + # 2.18 批量入库(多个) + def storage_batch_inbound(self, items: List[Dict[str, str]]) -> Dict[str, Any]: + """ + items = [{"materialId": "...", "locationId": "..."}, ...] + """ + return self._post_lims("/api/lims/storage/batch-inbound", items) + + # 3.30 自动化上料(Excel -> JSON -> POST /api/lims/order/auto-feeding4to3) + def auto_feeding4to3_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: + """ + 根据固定模板解析 Excel: + - 四号手套箱加样头面 (2-13行, 3-7列) + - 四号手套箱原液瓶面 (15-23行, 3-9列) + - 三号手套箱人工堆栈 (26-40行, 3-7列) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, header=None, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + items: List[Dict[str, Any]] = [] + + # 四号手套箱 - 加样头面(2-13行, 3-7列) + for _, row in df.iloc[1:13, 2:7].iterrows(): + item = { + "sourceWHName": "四号手套箱堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", + "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, + } + if item["materialName"]: + items.append(item) + + # 四号手套箱 - 原液瓶面(15-23行, 3-9列) + for _, row in df.iloc[14:23, 2:9].iterrows(): + item = { + "sourceWHName": "四号手套箱堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", + "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, + "materialType": str(row[7]).strip() if pd.notna(row[7]) else "", + "targetWH": str(row[8]).strip() if pd.notna(row[8]) else "", + } + if item["materialName"]: + items.append(item) + + # 三号手套箱人工堆栈(26-40行, 3-7列) + for _, row in df.iloc[25:40, 2:7].iterrows(): + item = { + "sourceWHName": "三号手套箱人工堆栈", + "posX": int(row[2]), + "posY": int(row[3]), + "posZ": int(row[4]), + "materialType": str(row[5]).strip() if pd.notna(row[5]) else "", + "materialId": str(row[6]).strip() if pd.notna(row[6]) else "", + "quantity": 1 # 默认数量1 + } + if item["materialId"] or item["materialType"]: + items.append(item) + + return self._post_lims("/api/lims/order/auto-feeding4to3", items) + + + + def auto_batch_outbound_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: + """ + 3.31 自动化下料(Excel -> JSON -> POST /api/lims/storage/auto-batch-out-bound) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + def pick(names: List[str]) -> Optional[str]: + for n in names: + if n in df.columns: + return n + return None + + c_loc = pick(["locationId", "库位ID", "库位Id", "库位id"]) + c_wh = pick(["warehouseId", "仓库ID", "仓库Id", "仓库id"]) + c_qty = pick(["数量", "quantity"]) + c_x = pick(["x", "X", "posX", "坐标X"]) + c_y = pick(["y", "Y", "posY", "坐标Y"]) + c_z = pick(["z", "Z", "posZ", "坐标Z"]) + + required = [c_loc, c_wh, c_qty, c_x, c_y, c_z] + if any(c is None for c in required): + raise KeyError("Excel 缺少必要列:locationId/warehouseId/数量/x/y/z(支持多别名,至少要能匹配到)。") + + def as_int(v, d=0): + try: + if pd.isna(v): return d + return int(v) + except Exception: + try: + return int(float(v)) + except Exception: + return d + + def as_float(v, d=0.0): + try: + if pd.isna(v): return d + return float(v) + except Exception: + return d + + def as_str(v, d=""): + if v is None or (isinstance(v, float) and pd.isna(v)): return d + s = str(v).strip() + return s if s else d + + items: List[Dict[str, Any]] = [] + for _, row in df.iterrows(): + items.append({ + "locationId": as_str(row[c_loc]), + "warehouseId": as_str(row[c_wh]), + "quantity": as_float(row[c_qty]), + "x": as_int(row[c_x]), + "y": as_int(row[c_y]), + "z": as_int(row[c_z]), + }) + + return self._post_lims("/api/lims/storage/auto-batch-out-bound", items) + + # 2.14 新建实验 + def create_orders(self, xlsx_path: str) -> Dict[str, Any]: + """ + 从 Excel 解析并创建实验(2.14) + 约定: + - batchId = Excel 文件名(不含扩展名) + - 物料列:所有以 "(g)" 结尾(不再读取“总质量(g)”列) + - totalMass 自动计算为所有物料质量之和 + - createTime 缺失或为空时自动填充为当前日期(YYYY/M/D) + """ + path = Path(xlsx_path) + if not path.exists(): + raise FileNotFoundError(f"未找到 Excel 文件:{path}") + + try: + df = pd.read_excel(path, sheet_name=0, engine="openpyxl") + except Exception as e: + raise RuntimeError(f"读取 Excel 失败:{e}") + + # 列名容错:返回可选列名,找不到则返回 None + def _pick(col_names: List[str]) -> Optional[str]: + for c in col_names: + if c in df.columns: + return c + return None + + col_order_name = _pick(["配方ID", "orderName", "订单编号"]) + col_create_time = _pick(["创建日期", "createTime"]) + col_bottle_type = _pick(["配液瓶类型", "bottleType"]) + col_mix_time = _pick(["混匀时间(s)", "mixTime"]) + col_load = _pick(["扣电组装分液体积", "loadSheddingInfo"]) + col_pouch = _pick(["软包组装分液体积", "pouchCellInfo"]) + col_cond = _pick(["电导测试分液体积", "conductivityInfo"]) + col_cond_cnt = _pick(["电导测试分液瓶数", "conductivityBottleCount"]) + + # 物料列:所有以 (g) 结尾 + material_cols = [c for c in df.columns if isinstance(c, str) and c.endswith("(g)")] + if not material_cols: + raise KeyError("未发现任何以“(g)”结尾的物料列,请检查表头。") + + batch_id = path.stem + + def _to_ymd_slash(v) -> str: + # 统一为 "YYYY/M/D";为空或解析失败则用当前日期 + if v is None or (isinstance(v, float) and pd.isna(v)) or str(v).strip() == "": + ts = datetime.now() + else: + try: + ts = pd.to_datetime(v) + except Exception: + ts = datetime.now() + return f"{ts.year}/{ts.month}/{ts.day}" + + def _as_int(val, default=0) -> int: + try: + if pd.isna(val): + return default + return int(val) + except Exception: + return default + + def _as_str(val, default="") -> str: + if val is None or (isinstance(val, float) and pd.isna(val)): + return default + s = str(val).strip() + return s if s else default + + orders: List[Dict[str, Any]] = [] + + for idx, row in df.iterrows(): + mats: List[Dict[str, Any]] = [] + total_mass = 0.0 + + for mcol in material_cols: + val = row.get(mcol, None) + if val is None or (isinstance(val, float) and pd.isna(val)): + continue + try: + mass = float(val) + except Exception: + continue + if mass > 0: + mats.append({"name": mcol.replace("(g)", ""), "mass": mass}) + total_mass += mass + + order_data = { + "batchId": batch_id, + "orderName": _as_str(row[col_order_name], default=f"{batch_id}_order_{idx+1}") if col_order_name else f"{batch_id}_order_{idx+1}", + "createTime": _to_ymd_slash(row[col_create_time]) if col_create_time else _to_ymd_slash(None), + "bottleType": _as_str(row[col_bottle_type], default="配液小瓶") if col_bottle_type else "配液小瓶", + "mixTime": _as_int(row[col_mix_time]) if col_mix_time else 0, + "loadSheddingInfo": _as_int(row[col_load]) if col_load else 0, + "pouchCellInfo": _as_int(row[col_pouch]) if col_pouch else 0, + "conductivityInfo": _as_int(row[col_cond]) if col_cond else 0, + "conductivityBottleCount": _as_int(row[col_cond_cnt]) if col_cond_cnt else 0, + "materialInfos": mats, + "totalMass": round(total_mass, 4) # 自动汇总 + } + orders.append(order_data) + + # print(orders) + + response = self._post_lims("/api/lims/order/orders", orders) + self.order_status[response["data"]["orderCode"]] = "running" + + while True: + time.sleep(5) + if self.order_status.get(response["data"]["orderCode"], None) == "finished": + break + return response + + + # 2.7 启动调度 + def scheduler_start(self) -> Dict[str, Any]: + return self._post_lims("/api/lims/scheduler/start") + # 3.10 停止调度 + def scheduler_stop(self) -> Dict[str, Any]: + """ + 停止调度 (3.10) + 请求体只包含 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/scheduler/stop") + + + # 2.24 物料变更推送 + def report_material_change(self, material_obj: Dict[str, Any]) -> Dict[str, Any]: + """ + material_obj 按 2.24 的裸对象格式(包含 id/typeName/locations/detail 等) + """ + return self._post_report_raw("/report/material_change", material_obj) + + # 2.21 步骤完成推送(BS → LIMS) + def report_step_finish(self, + order_code: str, + order_name: str, + step_name: str, + step_id: str, + sample_id: str, + start_time: str, + end_time: str, + execution_status: str = "completed") -> Dict[str, Any]: + data = { + "orderCode": order_code, + "orderName": order_name, + "stepName": step_name, + "stepId": step_id, + "sampleId": sample_id, + "startTime": start_time, + "endTime": end_time, + "executionStatus": execution_status + } + return self._post_report("/report/step_finish", data) + + # 2.23 订单完成推送(BS → LIMS) + def report_order_finish(self, + order_code: str, + order_name: str, + start_time: str, + end_time: str, + status: str = "30", # 30 完成 / -11 异常停止 / -12 人工停止 + workflow_status: str = "Finished", + completion_time: Optional[str] = None, + used_materials: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]: + data = { + "orderCode": order_code, + "orderName": order_name, + "startTime": start_time, + "endTime": end_time, + "status": status, + "workflowStatus": workflow_status, + "completionTime": completion_time or end_time, + "usedMaterials": used_materials or [] + } + return self._post_report("/report/order_finish", data) + + # 2.5 批量查询实验报告(用于轮询是否完成) + def order_list(self, + status: Optional[str] = None, + begin_time: Optional[str] = None, + end_time: Optional[str] = None, + filter_text: Optional[str] = None, + skip: int = 0, page: int = 10) -> Dict[str, Any]: + data: Dict[str, Any] = {"skipCount": skip, "pageCount": page} + if status is not None: # 80 成功 / 90 失败 / 100 执行中 + data["status"] = status + if begin_time: + data["timeType"] = "CreationTime" + data["beginTime"] = begin_time + if end_time: + data["endTime"] = end_time + if filter_text: + data["filter"] = filter_text + return self._post_lims("/api/lims/order/order-list", data) + + # 2.6 实验报告查询(根据任务ID拿详情) + def order_report(self, order_id: str) -> Dict[str, Any]: + return self._post_lims("/api/lims/order/order-report", order_id) + + # 2.32 3-2-1 物料转运 + def transfer_3_to_2_to_1(self, + # source_wh_id: Optional[str] = None, + source_wh_id: Optional[str] = '3a19debc-84b4-0359-e2d4-b3beea49348b', + source_x: int = 1, source_y: int = 1, source_z: int = 1) -> Dict[str, Any]: + payload: Dict[str, Any] = { + "sourcePosX": source_x, "sourcePosY": source_y, "sourcePosZ": source_z + } + if source_wh_id: + payload["sourceWHID"] = source_wh_id + return self._post_lims("/api/lims/order/transfer-task3To2To1", payload) + + # 2.28 样品/废料取出 + def take_out(self, + order_id: str, + preintake_ids: Optional[List[str]] = None, + material_ids: Optional[List[str]] = None) -> Dict[str, Any]: + data = { + "orderId": order_id, + "preintakeIds": preintake_ids or [], + "materialIds": material_ids or [] + } + return self._post_lims("/api/lims/order/take-out", data) + + # --------(可选)占位方法:文档未定义的“1号站内部流程 / 1-2转运”-------- + def start_station1_internal_flow(self, **kwargs) -> None: + logger.info("启动1号站内部流程(占位,按现场系统填充具体指令)") + + + # 3.x 1→2 物料转运 + def transfer_1_to_2(self) -> Dict[str, Any]: + """ + 1→2 物料转运 + URL: /api/lims/order/transfer-task1To2 + 只需要 apiKey 和 requestTime + """ + return self._post_lims("/api/lims/order/transfer-task1To2") + + + # -------------------- 整体编排 -------------------- + def run_full_workflow(self, + inbound_items: List[Dict[str, str]], + orders: List[Dict[str, Any]], + poll_filter_code: Optional[str] = None, + poll_timeout_s: int = 600, + poll_interval_s: int = 5, + transfer_source: Optional[Dict[str, Any]] = None, + takeout_order_id: Optional[str] = None) -> None: + """ + 一键串联: + 1) 入库 3-4 个物料 → 2) 新建实验 → 3) 启动调度 + 运行中(如需):4) 物料变更推送 5) 步骤完成推送 6) 订单完成推送 + 完成后:查询实验(2.5/2.6)→ 7) 3-2-1 转运 → 8) 1号站内部流程 + → 9) 1-2 转运 → 10) 样品/废料取出 + """ + # 1. 入库(多于1个就用批量接口 2.18) + if len(inbound_items) == 1: + r = self.storage_inbound(inbound_items[0]["materialId"], inbound_items[0]["locationId"]) + logger.info(f"单个入库结果: {r}") + else: + r = self.storage_batch_inbound(inbound_items) + logger.info(f"批量入库结果: {r}") + + # 2. 新建实验(2.14) + r = self.create_orders(orders) + logger.info(f"新建实验结果: {r}") + + # 3. 启动调度(2.7) + r = self.scheduler_start() + logger.info(f"启动调度结果: {r}") + + # —— 运行中各类推送(2.24 / 2.21 / 2.23),通常由实际任务驱动,这里提供调用方式 —— # + # self.report_material_change({...}) + # self.report_step_finish(order_code="BSO...", order_name="配液分液", step_name="xxx", step_id="...", sample_id="...", + # start_time=_iso_utc_now_ms(), end_time=_iso_utc_now_ms(), execution_status="completed") + # self.report_order_finish(order_code="BSO...", order_name="配液分液", start_time="...", end_time=_iso_utc_now_ms()) + + # 完成后才能转运:用 2.5 批量查询配合 filter=任务编码 轮询到 status=80(成功) + if poll_filter_code: + import time + deadline = time.time() + poll_timeout_s + while time.time() < deadline: + res = self.order_list(status="80", filter_text=poll_filter_code, page=5) + if isinstance(res, dict) and res.get("data", {}).get("items"): + logger.info(f"实验 {poll_filter_code} 已完成:{res['data']['items'][0]}") + break + time.sleep(poll_interval_s) + else: + logger.warning(f"等待实验 {poll_filter_code} 完成超时(未到 status=80)") + + # 7. 启动 3-2-1 转运(2.32) + if transfer_source: + r = self.transfer_3_to_2_to_1( + source_wh_id=transfer_source.get("sourceWHID"), + source_x=transfer_source.get("sourcePosX", 1), + source_y=transfer_source.get("sourcePosY", 1), + source_z=transfer_source.get("sourcePosZ", 1), + ) + logger.info(f"3-2-1 转运结果: {r}") + + # 8. 1号站内部流程(占位) + self.start_station1_internal_flow() + + # 9. 1→2 转运(占位) + self.transfer_1_to_2() + + # 10. 样品/废料取出(2.28) + if takeout_order_id: + r = self.take_out(order_id=takeout_order_id) + logger.info(f"样品/废料取出结果: {r}") + + + # 套接字服务端收到“步骤完成”时调用 + def process_step_finish_report(self, report_request): + order_code = report_request.data.get("orderCode") + if order_code: + self.order_status[order_code] = "step_finished" + logger.info(f"[REPORT] 订单 {order_code} 步骤完成") + return {"ack": True} + + def process_order_finish_report(self, report_request, used_materials=None): + order_code = report_request.data.get("orderCode") + if order_code: + self.order_status[order_code] = "finished" + logger.info(f"[REPORT] 订单 {order_code} 已完成,状态改为 finished") + return {"ack": True, "usedMaterials": used_materials or []} + + # 收到“通量完成”时调用 + def process_sample_finish_report(self, report_request): + order_code = report_request.data.get("orderCode") + if order_code: + self.order_status[order_code] = "sample_finished" + logger.info(f"[REPORT] 订单 {order_code} 通量完成") + return {"ack": True} + + def test_benyao_workstation(self, num1, num2): + num1 = int(num1) + num2 = int(num2) + for i in range(num1): + print(f"num1 = {num1}") + for j in range(num2): + print(f"num1 = {num2}") + test_benyao_api() + + +# -------------------------------- +if __name__ == "__main__": + ws = BioyondWorkstation() + # ws.scheduler_stop() + # 3.30 上料:读取模板 Excel 自动解析并 POST + # r1 = ws.auto_feeding4to3_from_xlsx(r"C:\ML\GitHub\Uni-Lab-OS\unilabos\devices\workstation\bioyond_cell\样品导入模板 (8).xlsx") + # ws.scheduler_start() + # print(r1["payload"]["data"]) # 调试模式下可直接看到要发的 JSON items + + # 新建实验 + # res = ws.create_orders("C:/ML/GitHub/Uni-Lab-OS/unilabos/devices/workstation/bioyond_cell/2025092501.xlsx") + # print(res) + + # 3.31 下料:同理 + # r2 = ws.auto_batch_outbound_from_xlsx(r"C:/path/样品导入模板 (8).xlsx") + # print(r2["payload"]["data"]) + + # r321 = ws.transfer_3_to_2_to_1() + # ws.transfer_1_to_2() + + diff --git a/unilabos/devices/workstation/bioyond_cell/cellconfig3c.json b/unilabos/devices/workstation/bioyond_cell/cellconfig3c.json new file mode 100644 index 00000000..9dfdc8ac --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/cellconfig3c.json @@ -0,0 +1,706 @@ +{ + "nodes": [ + { + "id": "bioyond_workstation", + "name": "配液分液工站", + "children": [ + ], + "parent": null, + "type": "device", + "class": "bioyondworkstation_device", + "config": { + "protocol_type": [], + "station_resource": {} + + }, + "data": {} + }, + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "coin_cell_deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "\u7535\u6c60\u6599\u76d8" + ], + "parent": null, + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1000, + "size_y": 1000, + "size_z": 900, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "\u7535\u6c60\u6599\u76d8", + "name": "\u7535\u6c60\u6599\u76d8", + "sample_id": null, + "children": [ + "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "\u7535\u6c60\u6599\u76d8_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 100, + "y": 100, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 160.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "B1": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "C1": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "D1": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "A2": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "B2": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "C2": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "D2": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "A3": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "B3": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "C3": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "D3": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "A4": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "B4": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "C4": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "D4": "\u7535\u6c60\u6599\u76d8_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/bioyond_cell/cellconfig3ca.json b/unilabos/devices/workstation/bioyond_cell/cellconfig3ca.json new file mode 100644 index 00000000..73286986 --- /dev/null +++ b/unilabos/devices/workstation/bioyond_cell/cellconfig3ca.json @@ -0,0 +1,20 @@ +{ + "nodes": [ + { + "id": "bioyond_workstation", + "name": "配液分液工站", + "children": [ + ], + "parent": null, + "type": "device", + "class": "bioyondworkstation_device", + "config": { + "protocol_type": [], + "station_resource": {} + + }, + "data": {} + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/bioyond_cell/样品导入模板.xlsx b/unilabos/devices/workstation/bioyond_cell/样品导入模板.xlsx new file mode 100644 index 00000000..7faa29ac Binary files /dev/null and b/unilabos/devices/workstation/bioyond_cell/样品导入模板.xlsx differ diff --git a/unilabos/devices/workstation/bioyond_material_management.py b/unilabos/devices/workstation/bioyond_material_management.py new file mode 100644 index 00000000..245da15c --- /dev/null +++ b/unilabos/devices/workstation/bioyond_material_management.py @@ -0,0 +1,374 @@ +""" +Bioyond物料管理实现 +Bioyond Material Management Implementation + +基于Bioyond系统的物料管理,支持从Bioyond系统同步物料到UniLab工作站 +""" +from typing import Dict, Any, List, Optional, Union +import json +import asyncio +from abc import ABC, abstractmethod + +from pylabrobot.resources import ( + Resource as PLRResource, + Container, + Deck, + Coordinate as PLRCoordinate, +) + +from unilabos.ros.nodes.resource_tracker import DeviceNodeResourceTracker +from unilabos.utils.log import logger +from unilabos.resources.graphio import ( + resource_plr_to_ulab, + resource_ulab_to_plr, + resource_bioyond_to_ulab, + resource_bioyond_container_to_ulab, + resource_ulab_to_bioyond +) +from .workstation_material_management import MaterialManagementBase + + +class BioyondMaterialManagement(MaterialManagementBase): + """Bioyond物料管理类 + + 实现从Bioyond系统同步物料到UniLab工作站的功能: + 1. 从Bioyond系统获取物料数据 + 2. 转换为UniLab格式 + 3. 同步到PyLabRobot Deck + 4. 支持双向同步 + """ + + def __init__( + self, + device_id: str, + deck_config: Dict[str, Any], + resource_tracker: DeviceNodeResourceTracker, + children_config: Dict[str, Dict[str, Any]] = None, + bioyond_config: Dict[str, Any] = None + ): + self.bioyond_config = bioyond_config or {} + self.bioyond_api_client = None + self.sync_interval = self.bioyond_config.get("sync_interval", 30) # 同步间隔(秒) + + # 初始化父类 + super().__init__(device_id, deck_config, resource_tracker, children_config) + + # 初始化Bioyond API客户端 + self._initialize_bioyond_client() + + # 启动同步任务 + self._start_sync_task() + + def _initialize_bioyond_client(self): + """初始化Bioyond API客户端""" + try: + # 这里应该根据实际的Bioyond API实现 + # 暂时使用模拟客户端 + self.bioyond_api_client = BioyondAPIClient(self.bioyond_config) + logger.info(f"Bioyond API客户端初始化成功") + except Exception as e: + logger.error(f"Bioyond API客户端初始化失败: {e}") + self.bioyond_api_client = None + + def _start_sync_task(self): + """启动同步任务""" + if self.bioyond_api_client: + # 创建异步同步任务 + asyncio.create_task(self._periodic_sync()) + logger.info(f"Bioyond同步任务已启动,间隔: {self.sync_interval}秒") + + async def _periodic_sync(self): + """定期同步任务""" + while True: + try: + await self.sync_from_bioyond() + await asyncio.sleep(self.sync_interval) + except Exception as e: + logger.error(f"Bioyond同步任务出错: {e}") + await asyncio.sleep(self.sync_interval) + + async def sync_from_bioyond(self) -> bool: + """从Bioyond系统同步物料""" + try: + if not self.bioyond_api_client: + logger.warning("Bioyond API客户端未初始化") + return False + + # 1. 从Bioyond获取物料数据 + bioyond_data = await self.bioyond_api_client.get_materials() + if not bioyond_data: + logger.warning("从Bioyond获取物料数据为空") + return False + + # 2. 转换为UniLab格式 + if isinstance(bioyond_data, dict) and "data" in bioyond_data: + # 容器格式数据 + unilab_resources = resource_bioyond_container_to_ulab(bioyond_data) + else: + # 物料列表格式数据 + unilab_resources = resource_bioyond_to_ulab(bioyond_data) + + # 3. 转换为PLR格式并分配到Deck + await self._assign_resources_to_deck(unilab_resources) + + logger.info(f"从Bioyond同步了 {len(unilab_resources)} 个资源") + return True + + except Exception as e: + logger.error(f"从Bioyond同步物料失败: {e}") + return False + + async def sync_to_bioyond(self, plr_resource: PLRResource) -> bool: + """将本地物料变更同步到Bioyond系统""" + try: + if not self.bioyond_api_client: + logger.warning("Bioyond API客户端未初始化") + return False + + # 1. 转换为UniLab格式 + unilab_resource = resource_plr_to_ulab(plr_resource) + + # 2. 转换为Bioyond格式 + bioyond_materials = resource_ulab_to_bioyond([unilab_resource]) + + # 3. 发送到Bioyond系统 + success = await self.bioyond_api_client.update_materials(bioyond_materials) + + if success: + logger.info(f"成功同步物料 {plr_resource.name} 到Bioyond") + else: + logger.warning(f"同步物料 {plr_resource.name} 到Bioyond失败") + + return success + + except Exception as e: + logger.error(f"同步物料到Bioyond失败: {e}") + return False + + async def _assign_resources_to_deck(self, unilab_resources: List[Dict[str, Any]]): + """将UniLab资源分配到Deck""" + try: + # 转换为PLR格式 + from unilabos.resources.graphio import list_to_nested_dict + nested_resources = list_to_nested_dict(unilab_resources) + plr_resources = resource_ulab_to_plr(nested_resources) + + # 分配资源到Deck + if hasattr(plr_resources, 'children'): + resources_to_assign = plr_resources.children + elif isinstance(plr_resources, list): + resources_to_assign = plr_resources + else: + resources_to_assign = [plr_resources] + + for resource in resources_to_assign: + try: + # 获取资源位置 + if hasattr(resource, 'location') and resource.location: + location = PLRCoordinate(resource.location.x, resource.location.y, resource.location.z) + else: + location = PLRCoordinate(0, 0, 0) + + # 分配资源到Deck + self.plr_deck.assign_child_resource(resource, location) + + # 注册到resource tracker + self.resource_tracker.add_resource(resource) + + # 保存资源引用 + self.plr_resources[resource.name] = resource + + except Exception as e: + logger.error(f"分配资源 {resource.name} 到Deck失败: {e}") + + logger.info(f"成功分配了 {len(resources_to_assign)} 个资源到Deck") + + except Exception as e: + logger.error(f"分配资源到Deck失败: {e}") + + def _create_resource_by_type( + self, + resource_id: str, + resource_type: str, + config: Dict[str, Any], + data: Dict[str, Any], + location: PLRCoordinate + ) -> Optional[PLRResource]: + """根据类型创建Bioyond相关资源""" + try: + # 这里可以根据需要实现特定的Bioyond资源类型 + # 目前使用通用的容器类型 + if resource_type in ["container", "plate", "well"]: + return self._create_generic_container(resource_id, resource_type, config, data, location) + else: + logger.warning(f"未知的Bioyond资源类型: {resource_type}") + return None + + except Exception as e: + logger.error(f"创建Bioyond资源失败 {resource_id} ({resource_type}): {e}") + return None + + def _create_generic_container( + self, + resource_id: str, + resource_type: str, + config: Dict[str, Any], + data: Dict[str, Any], + location: PLRCoordinate + ) -> Optional[PLRResource]: + """创建通用容器资源""" + try: + from pylabrobot.resources import Plate, Well + + if resource_type == "plate": + return Plate( + name=resource_id, + size_x=config.get("size_x", 127.76), + size_y=config.get("size_y", 85.48), + size_z=config.get("size_z", 14.35), + location=location, + category="plate" + ) + elif resource_type == "well": + return Well( + name=resource_id, + size_x=config.get("size_x", 9.0), + size_y=config.get("size_y", 9.0), + size_z=config.get("size_z", 10.0), + location=location, + category="well" + ) + else: + return Container( + name=resource_id, + size_x=config.get("size_x", 50.0), + size_y=config.get("size_y", 50.0), + size_z=config.get("size_z", 10.0), + location=location, + category="container" + ) + + except Exception as e: + logger.error(f"创建通用容器失败 {resource_id}: {e}") + return None + + def get_bioyond_materials(self) -> List[Dict[str, Any]]: + """获取当前Bioyond物料列表""" + try: + # 将当前PLR资源转换为Bioyond格式 + bioyond_materials = [] + for resource in self.plr_resources.values(): + unilab_resource = resource_plr_to_ulab(resource) + bioyond_materials.extend(resource_ulab_to_bioyond([unilab_resource])) + return bioyond_materials + except Exception as e: + logger.error(f"获取Bioyond物料列表失败: {e}") + return [] + + def update_material_from_bioyond(self, material_id: str, bioyond_data: Dict[str, Any]) -> bool: + """从Bioyond数据更新指定物料""" + try: + # 查找现有物料 + material = self.find_material_by_id(material_id) + if not material: + logger.warning(f"未找到物料: {material_id}") + return False + + # 转换Bioyond数据为UniLab格式 + unilab_resources = resource_bioyond_to_ulab([bioyond_data]) + if not unilab_resources: + logger.warning(f"转换Bioyond数据失败: {material_id}") + return False + + # 更新物料属性 + unilab_resource = unilab_resources[0] + material.name = unilab_resource.get("name", material.name) + + # 更新位置 + position = unilab_resource.get("position", {}) + if position: + material.location = PLRCoordinate( + position.get("x", 0), + position.get("y", 0), + position.get("z", 0) + ) + + logger.info(f"成功更新物料: {material_id}") + return True + + except Exception as e: + logger.error(f"更新物料失败 {material_id}: {e}") + return False + + +class BioyondAPIClient: + """Bioyond API客户端(模拟实现) + + 实际使用时需要根据Bioyond系统的API接口实现 + """ + + def __init__(self, config: Dict[str, Any]): + self.config = config + self.base_url = config.get("base_url", "http://localhost:8080") + self.api_key = config.get("api_key", "") + self.timeout = config.get("timeout", 30) + + async def get_materials(self) -> Optional[Union[Dict[str, Any], List[Dict[str, Any]]]]: + """从Bioyond系统获取物料数据""" + try: + # 这里应该实现实际的API调用 + # 暂时返回模拟数据 + logger.info("从Bioyond API获取物料数据") + + # 模拟API调用延迟 + await asyncio.sleep(0.1) + + # 返回模拟数据(实际应该从API获取) + return { + "data": [], + "code": 1, + "message": "success", + "timestamp": 1234567890 + } + + except Exception as e: + logger.error(f"Bioyond API调用失败: {e}") + return None + + async def update_materials(self, materials: List[Dict[str, Any]]) -> bool: + """更新Bioyond系统中的物料数据""" + try: + # 这里应该实现实际的API调用 + logger.info(f"更新Bioyond系统中的 {len(materials)} 个物料") + + # 模拟API调用延迟 + await asyncio.sleep(0.1) + + # 模拟成功响应 + return True + + except Exception as e: + logger.error(f"更新Bioyond物料失败: {e}") + return False + + async def get_material_by_id(self, material_id: str) -> Optional[Dict[str, Any]]: + """根据ID获取单个物料""" + try: + # 这里应该实现实际的API调用 + logger.info(f"从Bioyond API获取物料: {material_id}") + + # 模拟API调用延迟 + await asyncio.sleep(0.1) + + # 返回模拟数据 + return { + "id": material_id, + "name": f"material_{material_id}", + "type": "container", + "quantity": 1.0, + "unit": "个" + } + + except Exception as e: + logger.error(f"获取Bioyond物料失败 {material_id}: {e}") + return None diff --git a/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py index ffce04be..eae09b84 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py +++ b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py @@ -24,6 +24,13 @@ class ElectrodeSheetState(TypedDict): thickness: float # 厚度 (mm) mass: float # 质量 (g) material_type: str # 材料类型(正极、负极、隔膜、弹片、垫片、铝箔等) + height: float + electrolyte_name: str + data_electrolyte_code: str + open_circuit_voltage: float + assembly_pressure: float + electrolyte_volume: float + info: Optional[str] # 附加信息 class ElectrodeSheet(Resource): @@ -147,10 +154,10 @@ class MaterialHole(Resource): ): """放置极片""" # TODO: 这里要改,diameter找不到,加入._unilabos_state后应该没问题 - if resource._unilabos_state["diameter"] > self._unilabos_state["diameter"]: - raise ValueError(f"极片直径 {resource._unilabos_state['diameter']} 超过洞位直径 {self._unilabos_state['diameter']}") - if len(self.children) >= self._unilabos_state["max_sheets"]: - raise ValueError(f"洞位已满,无法放置更多极片") + #if resource._unilabos_state["diameter"] > self._unilabos_state["diameter"]: + # raise ValueError(f"极片直径 {resource._unilabos_state['diameter']} 超过洞位直径 {self._unilabos_state['diameter']}") + #if len(self.children) >= self._unilabos_state["max_sheets"]: + # raise ValueError(f"洞位已满,无法放置更多极片") super().assign_child_resource(resource, location, reassign) # 根据children的编号取物料对象。 @@ -165,8 +172,6 @@ class MaterialPlateState(TypedDict): hole_diameter: float info: Optional[str] # 附加信息 - - class MaterialPlate(ItemizedResource[MaterialHole]): """料板类 - 4x4个洞位,每个洞位放1个极片""" @@ -324,12 +329,13 @@ class PlateSlot(ResourceStack): class ClipMagazineHole(Container): """子弹夹洞位类""" - children: List[ElectrodeSheet] = [] + def __init__( self, name: str, diameter: float, depth: float, + max_sheets: int = 100, category: str = "clip_magazine_hole", ): """初始化子弹夹洞位 @@ -338,6 +344,7 @@ class ClipMagazineHole(Container): name: 洞位名称 diameter: 洞直径 (mm) depth: 洞深度 (mm) + max_sheets: 最大极片数量 category: 类别 """ super().__init__( @@ -349,143 +356,46 @@ class ClipMagazineHole(Container): ) self.diameter = diameter self.depth = depth + self.max_sheets = max_sheets + self._sheets: List[ElectrodeSheet] = [] def can_add_sheet(self, sheet: ElectrodeSheet) -> bool: - """检查是否可以添加极片 - - 根据洞的深度和极片的厚度来判断是否可以添加极片 - """ - # 检查极片直径是否适合洞的直径 - if sheet._unilabos_state["diameter"] > self.diameter: - return False - - # 计算当前已添加极片的总厚度 - current_thickness = sum(s._unilabos_state["thickness"] for s in self.children) - - # 检查添加新极片后总厚度是否超过洞的深度 - if current_thickness + sheet._unilabos_state["thickness"] > self.depth: - return False - - return True + """检查是否可以添加极片""" + return (len(self._sheets) < self.max_sheets and + sheet.diameter <= self.diameter) + def add_sheet(self, sheet: ElectrodeSheet) -> None: + """添加极片""" + if not self.can_add_sheet(sheet): + raise ValueError(f"无法向洞位 {self.name} 添加极片") + self._sheets.append(sheet) - def assign_child_resource( - self, - resource: ElectrodeSheet, - location: Optional[Coordinate] = None, - reassign: bool = True, - ): - """放置极片到洞位中 - - Args: - resource: 要放置的极片 - location: 极片在洞位中的位置(对于洞位,通常为None) - reassign: 是否允许重新分配 - """ - # 检查是否可以添加极片 - if not self.can_add_sheet(resource): - raise ValueError(f"无法向洞位 {self.name} 添加极片:直径或厚度不匹配") - - # 调用父类方法实际执行分配 - super().assign_child_resource(resource, location, reassign) - - def unassign_child_resource(self, resource: ElectrodeSheet): - """从洞位中移除极片 - - Args: - resource: 要移除的极片 - """ - if resource not in self.children: - raise ValueError(f"极片 {resource.name} 不在洞位 {self.name} 中") - - # 调用父类方法实际执行移除 - super().unassign_child_resource(resource) - + def take_sheet(self) -> ElectrodeSheet: + """取出极片""" + if len(self._sheets) == 0: + raise ValueError(f"洞位 {self.name} 没有极片") + return self._sheets.pop() + def get_sheet_count(self) -> int: + """获取极片数量""" + return len(self._sheets) def serialize_state(self) -> Dict[str, Any]: return { - "sheet_count": len(self.children), - "sheets": [sheet.serialize() for sheet in self.children], + "sheet_count": len(self._sheets), + "sheets": [sheet.serialize() for sheet in self._sheets], } -class ClipMagazine_four(ItemizedResource[ClipMagazineHole]): - """子弹夹类 - 有4个洞位,每个洞位放多个极片""" - children: List[ClipMagazineHole] - def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - hole_diameter: float = 14.0, - hole_depth: float = 10.0, - hole_spacing: float = 25.0, - max_sheets_per_hole: int = 100, - category: str = "clip_magazine_four", - model: Optional[str] = None, - ): - """初始化子弹夹 - Args: - name: 子弹夹名称 - size_x: 长度 (mm) - size_y: 宽度 (mm) - size_z: 高度 (mm) - hole_diameter: 洞直径 (mm) - hole_depth: 洞深度 (mm) - hole_spacing: 洞位间距 (mm) - max_sheets_per_hole: 每个洞位最大极片数量 - category: 类别 - model: 型号 - """ - # 创建4个洞位,排成2x2布局 - holes = create_ordered_items_2d( - klass=ClipMagazineHole, - num_items_x=2, - num_items_y=2, - dx=(size_x - 2 * hole_spacing) / 2, # 居中 - dy=(size_y - hole_spacing) / 2, # 居中 - dz=size_z - 0, - item_dx=hole_spacing, - item_dy=hole_spacing, - diameter=hole_diameter, - depth=hole_depth, - ) - - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - ordered_items=holes, - category=category, - model=model, - ) - - # 保存洞位的直径和深度 - self.hole_diameter = hole_diameter - self.hole_depth = hole_depth - self.max_sheets_per_hole = max_sheets_per_hole - - def serialize(self) -> dict: - return { - **super().serialize(), - "hole_diameter": self.hole_diameter, - "hole_depth": self.hole_depth, - "max_sheets_per_hole": self.max_sheets_per_hole, - } # TODO: 这个要改 -class ClipMagazine(ItemizedResource[ClipMagazineHole]): +class ClipMagazine(Resource): """子弹夹类 - 有6个洞位,每个洞位放多个极片""" - children: List[ClipMagazineHole] + def __init__( self, name: str, size_x: float, size_y: float, size_z: float, - hole_diameter: float = 14.0, - hole_depth: float = 10.0, hole_spacing: float = 25.0, max_sheets_per_hole: int = 100, category: str = "clip_magazine", @@ -515,8 +425,8 @@ class ClipMagazine(ItemizedResource[ClipMagazineHole]): dz=size_z - 0, item_dx=hole_spacing, item_dy=hole_spacing, - diameter=hole_diameter, - depth=hole_depth, + diameter=0, + depth=0, ) super().__init__( @@ -529,7 +439,6 @@ class ClipMagazine(ItemizedResource[ClipMagazineHole]): model=model, ) - # 保存洞位的直径和深度 self.hole_diameter = hole_diameter self.hole_depth = hole_depth self.max_sheets_per_hole = max_sheets_per_hole @@ -546,9 +455,9 @@ class BatteryState(TypedDict): """电池状态字典""" diameter: float height: float - - electrolyte_name: str + assembly_pressure: float electrolyte_volume: float + electrolyte_name: str class Battery(Resource): """电池类 - 可容纳极片""" @@ -557,6 +466,9 @@ class Battery(Resource): def __init__( self, name: str, + size_x=1, + size_y=1, + size_z=1, category: str = "battery", ): """初始化电池 @@ -577,7 +489,13 @@ class Battery(Resource): size_z=1, category=category, ) - self._unilabos_state: BatteryState = BatteryState() + self._unilabos_state: BatteryState = BatteryState( + diameter = 1.0, + height = 1.0, + assembly_pressure = 1.0, + electrolyte_volume = 1.0, + electrolyte_name = "DP001" + ) def add_electrolyte_with_bottle(self, bottle: Bottle) -> bool: to_add_name = bottle._unilabos_state["electrolyte_name"] @@ -733,15 +651,6 @@ class TipBox64(TipRack): make_tip=make_tip, ) self._unilabos_state: WasteTipBoxstate = WasteTipBoxstate() - # 记录网格参数用于前端渲染 - self._grid_params = { - "num_items_x": 8, - "num_items_y": 8, - "dx": 8.0, - "dy": 8.0, - "item_dx": 9.0, - "item_dy": 9.0, - } super().__init__( name=name, size_x=size_x, @@ -753,12 +662,6 @@ class TipBox64(TipRack): with_tips=True, ) - def serialize(self) -> dict: - return { - **super().serialize(), - **self._grid_params, - } - class WasteTipBoxstate(TypedDict): @@ -836,34 +739,19 @@ class BottleRackState(TypedDict): name_to_index: dict -class BottleRackState(TypedDict): - """ bottle_diameter: 瓶子直径 (mm) - bottle_height: 瓶子高度 (mm) - position_spacing: 位置间距 (mm)""" - bottle_diameter: float - bottle_height: float - position_spacing: float - name_to_index: dict - class BottleRack(Resource): """瓶架类 - 12个待配位置+12个已配位置""" - children: List[Resource] = [] + children: List[Bottle] = [] def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - category: str = "bottle_rack", - model: Optional[str] = None, - num_items_x: int = 3, - num_items_y: int = 4, - position_spacing: float = 35.0, - orientation: str = "horizontal", - padding_x: float = 20.0, - padding_y: float = 20.0, + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + category: str = "bottle_rack", + model: Optional[str] = None, ): """初始化瓶架 @@ -883,42 +771,13 @@ class BottleRack(Resource): category=category, model=model, ) - # 初始化状态 - self._unilabos_state: BottleRackState = BottleRackState( - bottle_diameter=30.0, - bottle_height=100.0, - position_spacing=position_spacing, - name_to_index={}, - ) - # 基于网格生成瓶位坐标映射(居中摆放) - # 使用内边距,避免点跑到容器外(前端渲染不按mm等比缩放时更稳妥) - origin_x = padding_x - origin_y = padding_y - self.index_to_pos = {} - for j in range(num_items_y): - for i in range(num_items_x): - idx = j * num_items_x + i - if orientation == "vertical": - # 纵向:沿 y 方向优先排列 - self.index_to_pos[idx] = Coordinate( - x=origin_x + j * position_spacing, - y=origin_y + i * position_spacing, - z=0, - ) - else: - # 横向(默认):沿 x 方向优先排列 - self.index_to_pos[idx] = Coordinate( - x=origin_x + i * position_spacing, - y=origin_y + j * position_spacing, - z=0, - ) + # TODO: 添加瓶位坐标映射 + self.index_to_pos = { + 0: Coordinate.zero(), + 1: Coordinate(x=1, y=2, z=3) # 添加 + } self.name_to_index = {} self.name_to_pos = {} - self.num_items_x = num_items_x - self.num_items_y = num_items_y - self.orientation = orientation - self.padding_x = padding_x - self.padding_y = padding_y def load_state(self, state: Dict[str, Any]) -> None: """格式不变""" @@ -928,23 +787,20 @@ class BottleRack(Resource): def serialize_state(self) -> Dict[str, Dict[str, Any]]: """格式不变""" data = super().serialize_state() - data.update( - self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) return data # TODO: 这里有些问题要重新写一下 - def assign_child_resource_old(self, resource: Resource, location=Coordinate.zero(), reassign=True): - capacity = self.num_items_x * self.num_items_y - assert len(self.children) < capacity, "瓶架已满,无法添加更多瓶子" + def assign_child_resource(self, resource: Bottle, location=Coordinate.zero(), reassign = True): + assert len(self.children) <= 12, "瓶架已满,无法添加更多瓶子" index = len(self.children) - location = self.index_to_pos.get(index, Coordinate.zero()) + location = Coordinate(x=20 + (index % 4) * 15, y=20 + (index // 4) * 15, z=0) self.name_to_pos[resource.name] = location self.name_to_index[resource.name] = index return super().assign_child_resource(resource, location, reassign) - - def assign_child_resource(self, resource: Resource, index: int): - capacity = self.num_items_x * self.num_items_y - assert 0 <= index < capacity, "无效的瓶子索引" + + def assign_child_resource_by_index(self, resource: Bottle, index: int): + assert 0 <= index < 12, "无效的瓶子索引" self.name_to_index[resource.name] = index location = self.index_to_pos[index] return super().assign_child_resource(resource, location) @@ -953,16 +809,9 @@ class BottleRack(Resource): super().unassign_child_resource(resource) self.index_to_pos.pop(self.name_to_index.pop(resource.name, None), None) - def serialize(self) -> dict: - return { - **super().serialize(), - "num_items_x": self.num_items_x, - "num_items_y": self.num_items_y, - "position_spacing": self._unilabos_state.get("position_spacing", 35.0), - "orientation": self.orientation, - "padding_x": self.padding_x, - "padding_y": self.padding_y, - } + # def serialize(self): + # self.children.sort(key=lambda x: self.name_to_index.get(x.name, 0)) + # return super().serialize() class BottleState(TypedDict): @@ -1098,16 +947,20 @@ class CoincellDeck(Deck): # plate.assign_child_resource(hole) # return plate -def create_a_liaopan(): - liaopan = MaterialPlate(name="liaopan", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) - for i in range(16): - jipian = ElectrodeSheet(name=f"jipian_{i}", size_x= 12, size_y=12, size_z=0.1) - liaopan1.children[i].assign_child_resource(jipian, location=None) - return liaopan +import json -def create_a_coin_cell_deck(): - deck = Deck(size_x=1200, - size_y=800, +if __name__ == "__main__": + #electrode1 = BatteryPressSlot() + #print(electrode1.get_size_x()) + #print(electrode1.get_size_y()) + #print(electrode1.get_size_z()) + #jipian = ElectrodeSheet() + #jipian._unilabos_state["diameter"] = 18 + #print(jipian.serialize()) + #print(jipian.serialize_state()) + + deck = CoincellDeck(size_x=1000, + size_y=1000, size_z=900) #liaopan = TipBox64(name="liaopan") @@ -1118,172 +971,33 @@ def create_a_coin_cell_deck(): deck.assign_child_resource(liaopan1, Coordinate(x=0, y=0, z=0)) #创建一个极片 for i in range(16): - jipian = ElectrodeSheet(name=f"jipian_{i}", size_x= 12, size_y=12, size_z=0.1) + jipian = ElectrodeSheet(name=f"jipian1_{i}", size_x= 12, size_y=12, size_z=0.1) liaopan1.children[i].assign_child_resource(jipian, location=None) +# #创建一个4*4的物料板 liaopan2 = MaterialPlate(name="liaopan2", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) #把物料板放到桌子上 deck.assign_child_resource(liaopan2, Coordinate(x=500, y=0, z=0)) #创建一个4*4的物料板 - liaopan3 = MaterialPlate(name="liaopan3", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) + liaopan3 = MaterialPlate(name="电池料盘", size_x=120.8, size_y=160.5, size_z=10.0, fill=True) #把物料板放到桌子上 - deck.assign_child_resource(liaopan3, Coordinate(x=1000, y=0, z=0)) - - print(deck) - - return deck + deck.assign_child_resource(liaopan3, Coordinate(x=100, y=100, z=0)) -import json -if __name__ == "__main__": - electrode1 = BatteryPressSlot() - #print(electrode1.get_size_x()) - #print(electrode1.get_size_y()) - #print(electrode1.get_size_z()) - #jipian = ElectrodeSheet() - #jipian._unilabos_state["diameter"] = 18 - #print(jipian.serialize()) - #print(jipian.serialize_state()) - - deck = CoincellDeck() - """======================================子弹夹============================================""" - zip_dan_jia = ClipMagazine_four("zi_dan_jia", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia, Coordinate(x=1400, y=50, z=0)) - zip_dan_jia2 = ClipMagazine_four("zi_dan_jia2", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia2, Coordinate(x=1600, y=200, z=0)) - zip_dan_jia3 = ClipMagazine("zi_dan_jia3", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia3, Coordinate(x=1500, y=200, z=0)) - zip_dan_jia4 = ClipMagazine("zi_dan_jia4", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia4, Coordinate(x=1500, y=300, z=0)) - zip_dan_jia5 = ClipMagazine("zi_dan_jia5", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia5, Coordinate(x=1600, y=300, z=0)) - zip_dan_jia6 = ClipMagazine("zi_dan_jia6", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia6, Coordinate(x=1530, y=500, z=0)) - zip_dan_jia7 = ClipMagazine("zi_dan_jia7", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia7, Coordinate(x=1180, y=400, z=0)) - zip_dan_jia8 = ClipMagazine("zi_dan_jia8", 80, 80, 10) - deck.assign_child_resource(zip_dan_jia8, Coordinate(x=1280, y=400, z=0)) - for i in range(4): - jipian = ElectrodeSheet(name=f"zi_dan_jia_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia2.children[i].assign_child_resource(jipian, location=None) - for i in range(4): - jipian2 = ElectrodeSheet(name=f"zi_dan_jia2_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia.children[i].assign_child_resource(jipian2, location=None) - for i in range(6): - jipian3 = ElectrodeSheet(name=f"zi_dan_jia3_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia3.children[i].assign_child_resource(jipian3, location=None) - for i in range(6): - jipian4 = ElectrodeSheet(name=f"zi_dan_jia4_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia4.children[i].assign_child_resource(jipian4, location=None) - for i in range(6): - jipian5 = ElectrodeSheet(name=f"zi_dan_jia5_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia5.children[i].assign_child_resource(jipian5, location=None) - for i in range(6): - jipian6 = ElectrodeSheet(name=f"zi_dan_jia6_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia6.children[i].assign_child_resource(jipian6, location=None) - for i in range(6): - jipian7 = ElectrodeSheet(name=f"zi_dan_jia7_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia7.children[i].assign_child_resource(jipian7, location=None) - for i in range(6): - jipian8 = ElectrodeSheet(name=f"zi_dan_jia8_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - zip_dan_jia8.children[i].assign_child_resource(jipian8, location=None) - """======================================子弹夹============================================""" - #liaopan = TipBox64(name="liaopan") - """======================================物料板============================================""" - #创建一个4*4的物料板 - liaopan1 = MaterialPlate(name="liaopan1", size_x=120, size_y=100, size_z=10.0, fill=True) - deck.assign_child_resource(liaopan1, Coordinate(x=1010, y=50, z=0)) - for i in range(16): - jipian_1 = ElectrodeSheet(name=f"{liaopan1.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - liaopan1.children[i].assign_child_resource(jipian_1, location=None) - - liaopan2 = MaterialPlate(name="liaopan2", size_x=120, size_y=100, size_z=10.0, fill=True) - deck.assign_child_resource(liaopan2, Coordinate(x=1130, y=50, z=0)) - - liaopan3 = MaterialPlate(name="liaopan3", size_x=120, size_y=100, size_z=10.0, fill=True) - deck.assign_child_resource(liaopan3, Coordinate(x=1250, y=50, z=0)) - - liaopan4 = MaterialPlate(name="liaopan4", size_x=120, size_y=100, size_z=10.0, fill=True) - deck.assign_child_resource(liaopan4, Coordinate(x=1010, y=150, z=0)) - for i in range(16): - jipian_4 = ElectrodeSheet(name=f"{liaopan4.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) - liaopan4.children[i].assign_child_resource(jipian_4, location=None) - liaopan5 = MaterialPlate(name="liaopan5", size_x=120, size_y=100, size_z=10.0, fill=True) - deck.assign_child_resource(liaopan5, Coordinate(x=1130, y=150, z=0)) - liaopan6 = MaterialPlate(name="liaopan6", size_x=120, size_y=100, size_z=10.0, fill=True) - deck.assign_child_resource(liaopan6, Coordinate(x=1250, y=150, z=0)) #liaopan.children[3].assign_child_resource(jipian, location=None) - """======================================物料板============================================""" - """======================================瓶架,移液枪============================================""" - # 在台面上放置 3x4 瓶架、6x2 瓶架 与 64孔移液枪头盒 - bottle_rack_3x4 = BottleRack( - name="bottle_rack_3x4", - size_x=210.0, - size_y=140.0, - size_z=100.0, - num_items_x=3, - num_items_y=4, - position_spacing=35.0, - orientation="vertical", - ) - deck.assign_child_resource(bottle_rack_3x4, Coordinate(x=100, y=200, z=0)) - - bottle_rack_6x2 = BottleRack( - name="bottle_rack_6x2", - size_x=120.0, - size_y=250.0, - size_z=100.0, - num_items_x=6, - num_items_y=2, - position_spacing=35.0, - orientation="vertical", - ) - deck.assign_child_resource(bottle_rack_6x2, Coordinate(x=300, y=300, z=0)) - - bottle_rack_6x2_2 = BottleRack( - name="bottle_rack_6x2_2", - size_x=120.0, - size_y=250.0, - size_z=100.0, - num_items_x=6, - num_items_y=2, - position_spacing=35.0, - orientation="vertical", - ) - deck.assign_child_resource(bottle_rack_6x2_2, Coordinate(x=430, y=300, z=0)) - - - # 将 ElectrodeSheet 放满 3x4 与 6x2 的所有孔位 - for idx in range(bottle_rack_3x4.num_items_x * bottle_rack_3x4.num_items_y): - sheet = ElectrodeSheet(name=f"sheet_3x4_{idx}", size_x=12, size_y=12, size_z=0.1) - bottle_rack_3x4.assign_child_resource(sheet, index=idx) - - for idx in range(bottle_rack_6x2.num_items_x * bottle_rack_6x2.num_items_y): - sheet = ElectrodeSheet(name=f"sheet_6x2_{idx}", size_x=12, size_y=12, size_z=0.1) - bottle_rack_6x2.assign_child_resource(sheet, index=idx) - - tip_box = TipBox64(name="tip_box_64") - deck.assign_child_resource(tip_box, Coordinate(x=300, y=100, z=0)) - - waste_tip_box = WasteTipBox(name="waste_tip_box") - deck.assign_child_resource(waste_tip_box, Coordinate(x=300, y=200, z=0)) - """======================================瓶架,移液枪============================================""" print(deck) from unilabos.resources.graphio import convert_resources_from_type from unilabos.config.config import BasicConfig - BasicConfig.ak = "56bbed5b-6e30-438c-b06d-f69eaa63bb45" - BasicConfig.sk = "238222fe-0bf7-4350-a426-e5ced8011dcf" + BasicConfig.ak = "4d5ce6ae-7234-4639-834e-93899b9caf94" + BasicConfig.sk = "505d3b0a-620e-459a-9905-1efcffce382a" from unilabos.app.web.client import http_client resources = convert_resources_from_type([deck], [Resource]) - - # 检查序列化后的资源 - - json.dump({"nodes": resources, "links": []}, open("button_battery_decks_unilab.json", "w"), indent=2) + json.dump({"nodes": resources, "links": []}, open("button_battery_station_resources_unilab.json", "w"), indent=2) #print(resources) diff --git a/unilabos/devices/workstation/coin_cell_assembly/button_battery_station_big.py b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station_big.py new file mode 100644 index 00000000..765328f2 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station_big.py @@ -0,0 +1,1412 @@ +""" +纽扣电池组装工作站物料类定义 +Button Battery Assembly Station Resource Classes +""" + +from __future__ import annotations + +from collections import OrderedDict +from typing import Any, Dict, List, Optional, TypedDict, Union, cast + +from pylabrobot.resources.coordinate import Coordinate +from pylabrobot.resources.container import Container +from pylabrobot.resources.deck import Deck +from pylabrobot.resources.itemized_resource import ItemizedResource +from pylabrobot.resources.resource import Resource +from pylabrobot.resources.resource_stack import ResourceStack +from pylabrobot.resources.tip_rack import TipRack, TipSpot +from pylabrobot.resources.trash import Trash +from pylabrobot.resources.utils import create_ordered_items_2d + + +class ElectrodeSheetState(TypedDict): + diameter: float # 直径 (mm) + thickness: float # 厚度 (mm) + mass: float # 质量 (g) + material_type: str # 材料类型(正极、负极、隔膜、弹片、垫片、铝箔等) + info: Optional[str] # 附加信息 + +class ElectrodeSheet(Resource): + """极片类 - 包含正负极片、隔膜、弹片、垫片、铝箔等所有片状材料""" + + def __init__( + self, + name: str = "极片", + size_x=10, + size_y=10, + size_z=10, + category: str = "electrode_sheet", + model: Optional[str] = None, + ): + """初始化极片 + + Args: + name: 极片名称 + category: 类别 + model: 型号 + """ + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + category=category, + model=model, + ) + self._unilabos_state: ElectrodeSheetState = ElectrodeSheetState( + diameter=14, + thickness=0.1, + mass=0.5, + material_type="copper", + info=None + ) + + # TODO: 这个还要不要?给self._unilabos_state赋值的? + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + #序列化 + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + +# TODO: 这个应该只能放一个极片 +class MaterialHoleState(TypedDict): + diameter: int + depth: int + max_sheets: int + info: Optional[str] # 附加信息 + +class MaterialHole(Resource): + """料板洞位类""" + children: List[ElectrodeSheet] = [] + + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + category: str = "material_hole", + **kwargs + ): + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + category=category, + ) + self._unilabos_state: MaterialHoleState = MaterialHoleState( + diameter=20, + depth=10, + max_sheets=1, + info=None + ) + + def get_all_sheet_info(self): + info_list = [] + for sheet in self.children: + info_list.append(sheet._unilabos_state["info"]) + return info_list + + #这个函数函数好像没用,一般不会集中赋值质量 + def set_all_sheet_mass(self): + for sheet in self.children: + sheet._unilabos_state["mass"] = 0.5 # 示例:设置质量为0.5g + + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + #移动极片前先取出对象 + def get_sheet_with_name(self, name: str) -> Optional[ElectrodeSheet]: + for sheet in self.children: + if sheet.name == name: + return sheet + return None + + def has_electrode_sheet(self) -> bool: + """检查洞位是否有极片""" + return len(self.children) > 0 + + def assign_child_resource( + self, + resource: ElectrodeSheet, + location: Optional[Coordinate], + reassign: bool = True, + ): + """放置极片""" + # TODO: 这里要改,diameter找不到,加入._unilabos_state后应该没问题 + if resource._unilabos_state["diameter"] > self._unilabos_state["diameter"]: + raise ValueError(f"极片直径 {resource._unilabos_state['diameter']} 超过洞位直径 {self._unilabos_state['diameter']}") + if len(self.children) >= self._unilabos_state["max_sheets"]: + raise ValueError(f"洞位已满,无法放置更多极片") + super().assign_child_resource(resource, location, reassign) + + # 根据children的编号取物料对象。 + def get_electrode_sheet_info(self, index: int) -> ElectrodeSheet: + return self.children[index] + + + +class MaterialPlateState(TypedDict): + hole_spacing_x: float + hole_spacing_y: float + hole_diameter: float + info: Optional[str] # 附加信息 + + + +class MaterialPlate(ItemizedResource[MaterialHole]): + """料板类 - 4x4个洞位,每个洞位放1个极片""" + + children: List[MaterialHole] + + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + ordered_items: Optional[Dict[str, MaterialHole]] = None, + ordering: Optional[OrderedDict[str, str]] = None, + category: str = "material_plate", + model: Optional[str] = None, + fill: bool = False + ): + """初始化料板 + + Args: + name: 料板名称 + size_x: 长度 (mm) + size_y: 宽度 (mm) + size_z: 高度 (mm) + hole_diameter: 洞直径 (mm) + hole_depth: 洞深度 (mm) + hole_spacing_x: X方向洞位间距 (mm) + hole_spacing_y: Y方向洞位间距 (mm) + number: 编号 + category: 类别 + model: 型号 + """ + self._unilabos_state: MaterialPlateState = MaterialPlateState( + hole_spacing_x=24.0, + hole_spacing_y=24.0, + hole_diameter=20.0, + info="", + ) + # 创建4x4的洞位 + # TODO: 这里要改,对应不同形状 + holes = create_ordered_items_2d( + klass=MaterialHole, + num_items_x=4, + num_items_y=4, + dx=(size_x - 4 * self._unilabos_state["hole_spacing_x"]) / 2, # 居中 + dy=(size_y - 4 * self._unilabos_state["hole_spacing_y"]) / 2, # 居中 + dz=size_z, + item_dx=self._unilabos_state["hole_spacing_x"], + item_dy=self._unilabos_state["hole_spacing_y"], + size_x = 16, + size_y = 16, + size_z = 16, + ) + if fill: + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + ordered_items=holes, + category=category, + model=model, + ) + else: + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + ordered_items=ordered_items, + ordering=ordering, + category=category, + model=model, + ) + + def update_locations(self): + # TODO:调多次相加 + holes = create_ordered_items_2d( + klass=MaterialHole, + num_items_x=4, + num_items_y=4, + dx=(self._size_x - 3 * self._unilabos_state["hole_spacing_x"]) / 2, # 居中 + dy=(self._size_y - 3 * self._unilabos_state["hole_spacing_y"]) / 2, # 居中 + dz=self._size_z, + item_dx=self._unilabos_state["hole_spacing_x"], + item_dy=self._unilabos_state["hole_spacing_y"], + size_x = 1, + size_y = 1, + size_z = 1, + ) + for item, original_item in zip(holes.items(), self.children): + original_item.location = item[1].location + + +class PlateSlot(ResourceStack): + """板槽位类 - 1个槽上能堆放8个板,移板只能操作最上方的板""" + + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + max_plates: int = 8, + category: str = "plate_slot", + model: Optional[str] = None + ): + """初始化板槽位 + + Args: + name: 槽位名称 + max_plates: 最大板数量 + category: 类别 + """ + super().__init__( + name=name, + direction="z", # Z方向堆叠 + resources=[], + ) + self.max_plates = max_plates + self.category = category + + def can_add_plate(self) -> bool: + """检查是否可以添加板""" + return len(self.children) < self.max_plates + + def add_plate(self, plate: MaterialPlate) -> None: + """添加料板""" + if not self.can_add_plate(): + raise ValueError(f"槽位 {self.name} 已满,无法添加更多板") + self.assign_child_resource(plate) + + def get_top_plate(self) -> MaterialPlate: + """获取最上方的板""" + if len(self.children) == 0: + raise ValueError(f"槽位 {self.name} 为空") + return cast(MaterialPlate, self.get_top_item()) + + def take_top_plate(self) -> MaterialPlate: + """取出最上方的板""" + top_plate = self.get_top_plate() + self.unassign_child_resource(top_plate) + return top_plate + + def can_access_for_picking(self) -> bool: + """检查是否可以进行取料操作(只有最上方的板能进行取料操作)""" + return len(self.children) > 0 + + def serialize(self) -> dict: + return { + **super().serialize(), + "max_plates": self.max_plates, + } + + +class ClipMagazineHole(Container): + """子弹夹洞位类""" + children: List[ElectrodeSheet] = [] + def __init__( + self, + name: str, + diameter: float, + depth: float, + category: str = "clip_magazine_hole", + ): + """初始化子弹夹洞位 + + Args: + name: 洞位名称 + diameter: 洞直径 (mm) + depth: 洞深度 (mm) + category: 类别 + """ + super().__init__( + name=name, + size_x=diameter, + size_y=diameter, + size_z=depth, + category=category, + ) + self.diameter = diameter + self.depth = depth + + def can_add_sheet(self, sheet: ElectrodeSheet) -> bool: + """检查是否可以添加极片 + + 根据洞的深度和极片的厚度来判断是否可以添加极片 + """ + # 检查极片直径是否适合洞的直径 + if sheet._unilabos_state["diameter"] > self.diameter: + return False + + # 计算当前已添加极片的总厚度 + current_thickness = sum(s._unilabos_state["thickness"] for s in self.children) + + # 检查添加新极片后总厚度是否超过洞的深度 + if current_thickness + sheet._unilabos_state["thickness"] > self.depth: + return False + + return True + + + def assign_child_resource( + self, + resource: ElectrodeSheet, + location: Optional[Coordinate] = None, + reassign: bool = True, + ): + """放置极片到洞位中 + + Args: + resource: 要放置的极片 + location: 极片在洞位中的位置(对于洞位,通常为None) + reassign: 是否允许重新分配 + """ + # 检查是否可以添加极片 + if not self.can_add_sheet(resource): + raise ValueError(f"无法向洞位 {self.name} 添加极片:直径或厚度不匹配") + + # 调用父类方法实际执行分配 + super().assign_child_resource(resource, location, reassign) + + def unassign_child_resource(self, resource: ElectrodeSheet): + """从洞位中移除极片 + + Args: + resource: 要移除的极片 + """ + if resource not in self.children: + raise ValueError(f"极片 {resource.name} 不在洞位 {self.name} 中") + + # 调用父类方法实际执行移除 + super().unassign_child_resource(resource) + + + + def serialize_state(self) -> Dict[str, Any]: + return { + "sheet_count": len(self.children), + "sheets": [sheet.serialize() for sheet in self.children], + } +class ClipMagazine_four(ItemizedResource[ClipMagazineHole]): + """子弹夹类 - 有4个洞位,每个洞位放多个极片""" + children: List[ClipMagazineHole] + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + hole_diameter: float = 14.0, + hole_depth: float = 10.0, + hole_spacing: float = 25.0, + max_sheets_per_hole: int = 100, + category: str = "clip_magazine_four", + model: Optional[str] = None, + ): + """初始化子弹夹 + + Args: + name: 子弹夹名称 + size_x: 长度 (mm) + size_y: 宽度 (mm) + size_z: 高度 (mm) + hole_diameter: 洞直径 (mm) + hole_depth: 洞深度 (mm) + hole_spacing: 洞位间距 (mm) + max_sheets_per_hole: 每个洞位最大极片数量 + category: 类别 + model: 型号 + """ + # 创建4个洞位,排成2x2布局 + holes = create_ordered_items_2d( + klass=ClipMagazineHole, + num_items_x=2, + num_items_y=2, + dx=(size_x - 2 * hole_spacing) / 2, # 居中 + dy=(size_y - hole_spacing) / 2, # 居中 + dz=size_z - 0, + item_dx=hole_spacing, + item_dy=hole_spacing, + diameter=hole_diameter, + depth=hole_depth, + ) + + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + ordered_items=holes, + category=category, + model=model, + ) + + # 保存洞位的直径和深度 + self.hole_diameter = hole_diameter + self.hole_depth = hole_depth + self.max_sheets_per_hole = max_sheets_per_hole + + def serialize(self) -> dict: + return { + **super().serialize(), + "hole_diameter": self.hole_diameter, + "hole_depth": self.hole_depth, + "max_sheets_per_hole": self.max_sheets_per_hole, + } +# TODO: 这个要改 +class ClipMagazine(ItemizedResource[ClipMagazineHole]): + """子弹夹类 - 有6个洞位,每个洞位放多个极片""" + children: List[ClipMagazineHole] + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + hole_diameter: float = 14.0, + hole_depth: float = 10.0, + hole_spacing: float = 25.0, + max_sheets_per_hole: int = 100, + category: str = "clip_magazine", + model: Optional[str] = None, + ): + """初始化子弹夹 + + Args: + name: 子弹夹名称 + size_x: 长度 (mm) + size_y: 宽度 (mm) + size_z: 高度 (mm) + hole_diameter: 洞直径 (mm) + hole_depth: 洞深度 (mm) + hole_spacing: 洞位间距 (mm) + max_sheets_per_hole: 每个洞位最大极片数量 + category: 类别 + model: 型号 + """ + # 创建6个洞位,排成2x3布局 + holes = create_ordered_items_2d( + klass=ClipMagazineHole, + num_items_x=3, + num_items_y=2, + dx=(size_x - 2 * hole_spacing) / 2, # 居中 + dy=(size_y - hole_spacing) / 2, # 居中 + dz=size_z - 0, + item_dx=hole_spacing, + item_dy=hole_spacing, + diameter=hole_diameter, + depth=hole_depth, + ) + + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + ordered_items=holes, + category=category, + model=model, + ) + + # 保存洞位的直径和深度 + self.hole_diameter = hole_diameter + self.hole_depth = hole_depth + self.max_sheets_per_hole = max_sheets_per_hole + + def serialize(self) -> dict: + return { + **super().serialize(), + "hole_diameter": self.hole_diameter, + "hole_depth": self.hole_depth, + "max_sheets_per_hole": self.max_sheets_per_hole, + } +#是一种类型注解,不用self +class BatteryState(TypedDict): + """电池状态字典""" + diameter: float + height: float + + electrolyte_name: str + electrolyte_volume: float + +class Battery(Resource): + """电池类 - 可容纳极片""" + children: List[ElectrodeSheet] = [] + + def __init__( + self, + name: str, + category: str = "battery", + ): + """初始化电池 + + Args: + name: 电池名称 + diameter: 直径 (mm) + height: 高度 (mm) + max_volume: 最大容量 (μL) + barcode: 二维码编号 + category: 类别 + model: 型号 + """ + super().__init__( + name=name, + size_x=1, + size_y=1, + size_z=1, + category=category, + ) + self._unilabos_state: BatteryState = BatteryState() + + def add_electrolyte_with_bottle(self, bottle: Bottle) -> bool: + to_add_name = bottle._unilabos_state["electrolyte_name"] + if bottle.aspirate_electrolyte(10): + if self.add_electrolyte(to_add_name, 10): + pass + else: + bottle._unilabos_state["electrolyte_volume"] += 10 + + def set_electrolyte(self, name: str, volume: float) -> None: + """设置电解液信息""" + self._unilabos_state["electrolyte_name"] = name + self._unilabos_state["electrolyte_volume"] = volume + #这个应该没用,不会有加了后再加的事情 + def add_electrolyte(self, name: str, volume: float) -> bool: + """添加电解液信息""" + if name != self._unilabos_state["electrolyte_name"]: + return False + self._unilabos_state["electrolyte_volume"] += volume + + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + +# 电解液作为属性放进去 + +class BatteryPressSlotState(TypedDict): + """电池状态字典""" + diameter: float =20.0 + depth: float = 4.0 + +class BatteryPressSlot(Resource): + """电池压制槽类 - 设备,可容纳一个电池""" + children: List[Battery] = [] + + def __init__( + self, + name: str = "BatteryPressSlot", + category: str = "battery_press_slot", + ): + """初始化电池压制槽 + + Args: + name: 压制槽名称 + diameter: 直径 (mm) + depth: 深度 (mm) + category: 类别 + model: 型号 + """ + super().__init__( + name=name, + size_x=10, + size_y=12, + size_z=13, + category=category, + ) + self._unilabos_state: BatteryPressSlotState = BatteryPressSlotState() + + def has_battery(self) -> bool: + """检查是否有电池""" + return len(self.children) > 0 + + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + + def assign_child_resource( + self, + resource: Battery, + location: Optional[Coordinate], + reassign: bool = True, + ): + """放置极片""" + # TODO: 让高京看下槽位只有一个电池时是否这么写。 + if self.has_battery(): + raise ValueError(f"槽位已含有一个电池,无法再放置其他电池") + super().assign_child_resource(resource, location, reassign) + + # 根据children的编号取物料对象。 + def get_battery_info(self, index: int) -> Battery: + return self.children[0] + +# TODO:这个移液枪架子看一下从哪继承 +class TipBox64State(TypedDict): + """电池状态字典""" + tip_diameter: float = 5.0 + tip_length: float = 50.0 + with_tips: bool = True + +class TipBox64(TipRack): + """64孔枪头盒类""" + + children: List[TipSpot] = [] + def __init__( + self, + name: str, + size_x: float = 127.8, + size_y: float = 85.5, + size_z: float = 60.0, + category: str = "tip_box_64", + model: Optional[str] = None, + ): + """初始化64孔枪头盒 + + Args: + name: 枪头盒名称 + size_x: 长度 (mm) + size_y: 宽度 (mm) + size_z: 高度 (mm) + tip_diameter: 枪头直径 (mm) + tip_length: 枪头长度 (mm) + category: 类别 + model: 型号 + with_tips: 是否带枪头 + """ + from pylabrobot.resources.tip import Tip + + # 创建8x8=64个枪头位 + def make_tip(): + return Tip( + has_filter=False, + total_tip_length=20.0, + maximal_volume=1000, # 1mL + fitting_depth=8.0, + ) + + tip_spots = create_ordered_items_2d( + klass=TipSpot, + num_items_x=8, + num_items_y=8, + dx=8.0, + dy=8.0, + dz=0.0, + item_dx=9.0, + item_dy=9.0, + size_x=10, + size_y=10, + size_z=0.0, + make_tip=make_tip, + ) + self._unilabos_state: WasteTipBoxstate = WasteTipBoxstate() + # 记录网格参数用于前端渲染 + self._grid_params = { + "num_items_x": 8, + "num_items_y": 8, + "dx": 8.0, + "dy": 8.0, + "item_dx": 9.0, + "item_dy": 9.0, + } + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + ordered_items=tip_spots, + category=category, + model=model, + with_tips=True, + ) + + def serialize(self) -> dict: + return { + **super().serialize(), + **self._grid_params, + } + + + +class WasteTipBoxstate(TypedDict): + """"废枪头盒状态字典""" + max_tips: int = 100 + tip_count: int = 0 + +#枪头不是一次性的(同一溶液则反复使用),根据寄存器判断 +class WasteTipBox(Trash): + """废枪头盒类 - 100个枪头容量""" + + def __init__( + self, + name: str, + size_x: float = 127.8, + size_y: float = 85.5, + size_z: float = 60.0, + category: str = "waste_tip_box", + model: Optional[str] = None, + ): + """初始化废枪头盒 + + Args: + name: 废枪头盒名称 + size_x: 长度 (mm) + size_y: 宽度 (mm) + size_z: 高度 (mm) + max_tips: 最大枪头容量 + category: 类别 + model: 型号 + """ + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + category=category, + model=model, + ) + self._unilabos_state: WasteTipBoxstate = WasteTipBoxstate() + + def add_tip(self) -> None: + """添加废枪头""" + if self._unilabos_state["tip_count"] >= self._unilabos_state["max_tips"]: + raise ValueError(f"废枪头盒 {self.name} 已满") + self._unilabos_state["tip_count"] += 1 + + def get_tip_count(self) -> int: + """获取枪头数量""" + return self._unilabos_state["tip_count"] + + def empty(self) -> None: + """清空废枪头盒""" + self._unilabos_state["tip_count"] = 0 + + + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + +class BottleRackState(TypedDict): + """ bottle_diameter: 瓶子直径 (mm) + bottle_height: 瓶子高度 (mm) + position_spacing: 位置间距 (mm)""" + bottle_diameter: float + bottle_height: float + position_spacing: float + name_to_index: dict + + +class BottleRack(Resource): + """瓶架类 - 12个待配位置+12个已配位置""" + children: List[Resource] = [] + + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + category: str = "bottle_rack", + model: Optional[str] = None, + num_items_x: int = 3, + num_items_y: int = 4, + position_spacing: float = 35.0, + orientation: str = "horizontal", + padding_x: float = 20.0, + padding_y: float = 20.0, + ): + """初始化瓶架 + + Args: + name: 瓶架名称 + size_x: 长度 (mm) + size_y: 宽度 (mm) + size_z: 高度 (mm) + category: 类别 + model: 型号 + """ + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + category=category, + model=model, + ) + # 初始化状态 + self._unilabos_state: BottleRackState = BottleRackState( + bottle_diameter=30.0, + bottle_height=100.0, + position_spacing=position_spacing, + name_to_index={}, + ) + # 基于网格生成瓶位坐标映射(居中摆放) + # 使用内边距,避免点跑到容器外(前端渲染不按mm等比缩放时更稳妥) + origin_x = padding_x + origin_y = padding_y + self.index_to_pos = {} + for j in range(num_items_y): + for i in range(num_items_x): + idx = j * num_items_x + i + if orientation == "vertical": + # 纵向:沿 y 方向优先排列 + self.index_to_pos[idx] = Coordinate( + x=origin_x + j * position_spacing, + y=origin_y + i * position_spacing, + z=0, + ) + else: + # 横向(默认):沿 x 方向优先排列 + self.index_to_pos[idx] = Coordinate( + x=origin_x + i * position_spacing, + y=origin_y + j * position_spacing, + z=0, + ) + self.name_to_index = {} + self.name_to_pos = {} + self.num_items_x = num_items_x + self.num_items_y = num_items_y + self.orientation = orientation + self.padding_x = padding_x + self.padding_y = padding_y + + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update( + self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + + # TODO: 这里有些问题要重新写一下 + def assign_child_resource_old(self, resource: Resource, location=Coordinate.zero(), reassign=True): + capacity = self.num_items_x * self.num_items_y + assert len(self.children) < capacity, "瓶架已满,无法添加更多瓶子" + index = len(self.children) + location = self.index_to_pos.get(index, Coordinate.zero()) + self.name_to_pos[resource.name] = location + self.name_to_index[resource.name] = index + return super().assign_child_resource(resource, location, reassign) + + def assign_child_resource(self, resource: Resource, index: int): + capacity = self.num_items_x * self.num_items_y + assert 0 <= index < capacity, "无效的瓶子索引" + self.name_to_index[resource.name] = index + location = self.index_to_pos[index] + return super().assign_child_resource(resource, location) + + def unassign_child_resource(self, resource: Bottle): + super().unassign_child_resource(resource) + self.index_to_pos.pop(self.name_to_index.pop(resource.name, None), None) + + def serialize(self) -> dict: + return { + **super().serialize(), + "num_items_x": self.num_items_x, + "num_items_y": self.num_items_y, + "position_spacing": self._unilabos_state.get("position_spacing", 35.0), + "orientation": self.orientation, + "padding_x": self.padding_x, + "padding_y": self.padding_y, + } + + +class BottleState(TypedDict): + diameter: float + height: float + electrolyte_name: str + electrolyte_volume: float + max_volume: float + +class Bottle(Resource): + """瓶子类 - 容纳电解液""" + + def __init__( + self, + name: str, + category: str = "bottle", + ): + """初始化瓶子 + + Args: + name: 瓶子名称 + diameter: 直径 (mm) + height: 高度 (mm) + max_volume: 最大体积 (μL) + barcode: 二维码 + category: 类别 + model: 型号 + """ + super().__init__( + name=name, + size_x=1, + size_y=1, + size_z=1, + category=category, + ) + self._unilabos_state: BottleState = BottleState() + + def aspirate_electrolyte(self, volume: float) -> bool: + current_volume = self._unilabos_state["electrolyte_volume"] + assert current_volume > volume, f"Cannot aspirate {volume}μL, only {current_volume}μL available." + self._unilabos_state["electrolyte_volume"] -= volume + return True + + def load_state(self, state: Dict[str, Any]) -> None: + """格式不变""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + """格式不变""" + data = super().serialize_state() + data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + +class CoincellDeck(Deck): + """纽扣电池组装工作站台面类""" + + def __init__( + self, + name: str = "coin_cell_deck", + size_x: float = 1620.0, # 3.66m + size_y: float = 1270.0, # 1.23m + size_z: float = 500.0, + origin: Coordinate = Coordinate(0, 0, 0), + category: str = "coin_cell_deck", + ): + """初始化纽扣电池组装工作站台面 + + Args: + name: 台面名称 + size_x: 长度 (mm) - 3.66m + size_y: 宽度 (mm) - 1.23m + size_z: 高度 (mm) + origin: 原点坐标 + category: 类别 + """ + super().__init__( + name=name, + size_x=size_x, + size_y=size_y, + size_z=size_z, + origin=origin, + category=category, + ) + +#if __name__ == "__main__": +# # 转移极片的测试代码 +# deck = CoincellDeck("coin_cell_deck") +# ban_cao_wei = PlateSlot("ban_cao_wei", max_plates=8) +# deck.assign_child_resource(ban_cao_wei, Coordinate(x=0, y=0, z=0)) +# +# plate_1 = MaterialPlate("plate_1", 1,1,1, fill=True) +# for i, hole in enumerate(plate_1.children): +# sheet = ElectrodeSheet(f"hole_{i}_sheet_1") +# sheet._unilabos_state = { +# "diameter": 14, +# "info": "NMC", +# "mass": 5.0, +# "material_type": "positive_electrode", +# "thickness": 0.1 +# } +# hole._unilabos_state = { +# "depth": 1.0, +# "diameter": 14, +# "info": "", +# "max_sheets": 1 +# } +# hole.assign_child_resource(sheet, Coordinate.zero()) +# plate_1._unilabos_state = { +# "hole_spacing_x": 20.0, +# "hole_spacing_y": 20.0, +# "hole_diameter": 5, +# "info": "这是第一块料板" +# } +# plate_1.update_locations() +# ban_cao_wei.assign_child_resource(plate_1, Coordinate.zero()) +# # zi_dan_jia = ClipMagazine("zi_dan_jia", 1, 1, 1) +# # deck.assign_child_resource(ban_cao_wei, Coordinate(x=200, y=200, z=0)) +# +# from unilabos.resources.graphio import * +# A = tree_to_list([resource_plr_to_ulab(deck)]) +# with open("test.json", "w") as f: +# json.dump(A, f) +# +# +#def get_plate_with_14mm_hole(name=""): +# plate = MaterialPlate(name=name) +# for i in range(4): +# for j in range(4): +# hole = MaterialHole(f"{i+1}x{j+1}") +# hole._unilabos_state["diameter"] = 14 +# hole._unilabos_state["max_sheets"] = 1 +# plate.assign_child_resource(hole) +# return plate + +def create_a_liaopan(): + liaopan = MaterialPlate(name="liaopan", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) + for i in range(16): + jipian = ElectrodeSheet(name=f"jipian_{i}", size_x= 12, size_y=12, size_z=0.1) + liaopan1.children[i].assign_child_resource(jipian, location=None) + return liaopan + +def create_a_coin_cell_deck(): + deck = Deck(size_x=1200, + size_y=800, + size_z=900) + + #liaopan = TipBox64(name="liaopan") + + #创建一个4*4的物料板 + liaopan1 = MaterialPlate(name="liaopan1", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) + #把物料板放到桌子上 + deck.assign_child_resource(liaopan1, Coordinate(x=0, y=0, z=0)) + #创建一个极片 + for i in range(16): + jipian = ElectrodeSheet(name=f"jipian_{i}", size_x= 12, size_y=12, size_z=0.1) + liaopan1.children[i].assign_child_resource(jipian, location=None) + #创建一个4*4的物料板 + liaopan2 = MaterialPlate(name="liaopan2", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) + #把物料板放到桌子上 + deck.assign_child_resource(liaopan2, Coordinate(x=500, y=0, z=0)) + + #创建一个4*4的物料板 + liaopan3 = MaterialPlate(name="liaopan3", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) + #把物料板放到桌子上 + deck.assign_child_resource(liaopan3, Coordinate(x=1000, y=0, z=0)) + + print(deck) + + return deck + + +def create_a_full_coin_cell_deck(): + deck = CoincellDeck() + """======================================子弹夹============================================""" + zip_dan_jia = ClipMagazine_four("zi_dan_jia", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia, Coordinate(x=1400, y=50, z=0)) + zip_dan_jia2 = ClipMagazine_four("zi_dan_jia2", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia2, Coordinate(x=1600, y=200, z=0)) + zip_dan_jia3 = ClipMagazine("zi_dan_jia3", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia3, Coordinate(x=1500, y=200, z=0)) + zip_dan_jia4 = ClipMagazine("zi_dan_jia4", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia4, Coordinate(x=1500, y=300, z=0)) + zip_dan_jia5 = ClipMagazine("zi_dan_jia5", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia5, Coordinate(x=1600, y=300, z=0)) + zip_dan_jia6 = ClipMagazine("zi_dan_jia6", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia6, Coordinate(x=1530, y=500, z=0)) + zip_dan_jia7 = ClipMagazine("zi_dan_jia7", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia7, Coordinate(x=1180, y=400, z=0)) + zip_dan_jia8 = ClipMagazine("zi_dan_jia8", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia8, Coordinate(x=1280, y=400, z=0)) + for i in range(4): + jipian = ElectrodeSheet(name=f"zi_dan_jia_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia2.children[i].assign_child_resource(jipian, location=None) + for i in range(4): + jipian2 = ElectrodeSheet(name=f"zi_dan_jia2_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia.children[i].assign_child_resource(jipian2, location=None) + for i in range(6): + jipian3 = ElectrodeSheet(name=f"zi_dan_jia3_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia3.children[i].assign_child_resource(jipian3, location=None) + for i in range(6): + jipian4 = ElectrodeSheet(name=f"zi_dan_jia4_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia4.children[i].assign_child_resource(jipian4, location=None) + for i in range(6): + jipian5 = ElectrodeSheet(name=f"zi_dan_jia5_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia5.children[i].assign_child_resource(jipian5, location=None) + for i in range(6): + jipian6 = ElectrodeSheet(name=f"zi_dan_jia6_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia6.children[i].assign_child_resource(jipian6, location=None) + for i in range(6): + jipian7 = ElectrodeSheet(name=f"zi_dan_jia7_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia7.children[i].assign_child_resource(jipian7, location=None) + for i in range(6): + jipian8 = ElectrodeSheet(name=f"zi_dan_jia8_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia8.children[i].assign_child_resource(jipian8, location=None) + """======================================子弹夹============================================""" + #liaopan = TipBox64(name="liaopan") + """======================================物料板============================================""" + #创建一个4*4的物料板 + liaopan1 = MaterialPlate(name="liaopan1", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan1, Coordinate(x=1010, y=50, z=0)) + for i in range(16): + jipian_1 = ElectrodeSheet(name=f"{liaopan1.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + liaopan1.children[i].assign_child_resource(jipian_1, location=None) + + liaopan2 = MaterialPlate(name="liaopan2", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan2, Coordinate(x=1130, y=50, z=0)) + + liaopan3 = MaterialPlate(name="liaopan3", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan3, Coordinate(x=1250, y=50, z=0)) + + liaopan4 = MaterialPlate(name="liaopan4", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan4, Coordinate(x=1010, y=150, z=0)) + for i in range(16): + jipian_4 = ElectrodeSheet(name=f"{liaopan4.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + liaopan4.children[i].assign_child_resource(jipian_4, location=None) + liaopan5 = MaterialPlate(name="liaopan5", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan5, Coordinate(x=1130, y=150, z=0)) + liaopan6 = MaterialPlate(name="liaopan6", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan6, Coordinate(x=1250, y=150, z=0)) + #liaopan.children[3].assign_child_resource(jipian, location=None) + """======================================物料板============================================""" + """======================================瓶架,移液枪============================================""" + # 在台面上放置 3x4 瓶架、6x2 瓶架 与 64孔移液枪头盒 + bottle_rack_3x4 = BottleRack( + name="bottle_rack_3x4", + size_x=210.0, + size_y=140.0, + size_z=100.0, + num_items_x=3, + num_items_y=4, + position_spacing=35.0, + orientation="vertical", + ) + deck.assign_child_resource(bottle_rack_3x4, Coordinate(x=100, y=200, z=0)) + + bottle_rack_6x2 = BottleRack( + name="bottle_rack_6x2", + size_x=120.0, + size_y=250.0, + size_z=100.0, + num_items_x=6, + num_items_y=2, + position_spacing=35.0, + orientation="vertical", + ) + deck.assign_child_resource(bottle_rack_6x2, Coordinate(x=300, y=300, z=0)) + + bottle_rack_6x2_2 = BottleRack( + name="bottle_rack_6x2_2", + size_x=120.0, + size_y=250.0, + size_z=100.0, + num_items_x=6, + num_items_y=2, + position_spacing=35.0, + orientation="vertical", + ) + deck.assign_child_resource(bottle_rack_6x2_2, Coordinate(x=430, y=300, z=0)) + + + # 将 ElectrodeSheet 放满 3x4 与 6x2 的所有孔位 + for idx in range(bottle_rack_3x4.num_items_x * bottle_rack_3x4.num_items_y): + sheet = ElectrodeSheet(name=f"sheet_3x4_{idx}", size_x=12, size_y=12, size_z=0.1) + bottle_rack_3x4.assign_child_resource(sheet, index=idx) + + for idx in range(bottle_rack_6x2.num_items_x * bottle_rack_6x2.num_items_y): + sheet = ElectrodeSheet(name=f"sheet_6x2_{idx}", size_x=12, size_y=12, size_z=0.1) + bottle_rack_6x2.assign_child_resource(sheet, index=idx) + + tip_box = TipBox64(name="tip_box_64") + deck.assign_child_resource(tip_box, Coordinate(x=300, y=100, z=0)) + + waste_tip_box = WasteTipBox(name="waste_tip_box") + deck.assign_child_resource(waste_tip_box, Coordinate(x=300, y=200, z=0)) + """======================================瓶架,移液枪============================================""" + print(deck) + return deck + + + + +import json + +if __name__ == "__main__": + electrode1 = BatteryPressSlot() + #print(electrode1.get_size_x()) + #print(electrode1.get_size_y()) + #print(electrode1.get_size_z()) + #jipian = ElectrodeSheet() + #jipian._unilabos_state["diameter"] = 18 + #print(jipian.serialize()) + #print(jipian.serialize_state()) + + deck = CoincellDeck() + """======================================子弹夹============================================""" + zip_dan_jia = ClipMagazine_four("zi_dan_jia", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia, Coordinate(x=1400, y=50, z=0)) + zip_dan_jia2 = ClipMagazine_four("zi_dan_jia2", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia2, Coordinate(x=1600, y=200, z=0)) + zip_dan_jia3 = ClipMagazine("zi_dan_jia3", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia3, Coordinate(x=1500, y=200, z=0)) + zip_dan_jia4 = ClipMagazine("zi_dan_jia4", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia4, Coordinate(x=1500, y=300, z=0)) + zip_dan_jia5 = ClipMagazine("zi_dan_jia5", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia5, Coordinate(x=1600, y=300, z=0)) + zip_dan_jia6 = ClipMagazine("zi_dan_jia6", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia6, Coordinate(x=1530, y=500, z=0)) + zip_dan_jia7 = ClipMagazine("zi_dan_jia7", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia7, Coordinate(x=1180, y=400, z=0)) + zip_dan_jia8 = ClipMagazine("zi_dan_jia8", 80, 80, 10) + deck.assign_child_resource(zip_dan_jia8, Coordinate(x=1280, y=400, z=0)) + for i in range(4): + jipian = ElectrodeSheet(name=f"zi_dan_jia_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia2.children[i].assign_child_resource(jipian, location=None) + for i in range(4): + jipian2 = ElectrodeSheet(name=f"zi_dan_jia2_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia.children[i].assign_child_resource(jipian2, location=None) + for i in range(6): + jipian3 = ElectrodeSheet(name=f"zi_dan_jia3_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia3.children[i].assign_child_resource(jipian3, location=None) + for i in range(6): + jipian4 = ElectrodeSheet(name=f"zi_dan_jia4_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia4.children[i].assign_child_resource(jipian4, location=None) + for i in range(6): + jipian5 = ElectrodeSheet(name=f"zi_dan_jia5_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia5.children[i].assign_child_resource(jipian5, location=None) + for i in range(6): + jipian6 = ElectrodeSheet(name=f"zi_dan_jia6_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia6.children[i].assign_child_resource(jipian6, location=None) + for i in range(6): + jipian7 = ElectrodeSheet(name=f"zi_dan_jia7_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia7.children[i].assign_child_resource(jipian7, location=None) + for i in range(6): + jipian8 = ElectrodeSheet(name=f"zi_dan_jia8_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + zip_dan_jia8.children[i].assign_child_resource(jipian8, location=None) + """======================================子弹夹============================================""" + #liaopan = TipBox64(name="liaopan") + """======================================物料板============================================""" + #创建一个4*4的物料板 + liaopan1 = MaterialPlate(name="liaopan1", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan1, Coordinate(x=1010, y=50, z=0)) + for i in range(16): + jipian_1 = ElectrodeSheet(name=f"{liaopan1.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + liaopan1.children[i].assign_child_resource(jipian_1, location=None) + + liaopan2 = MaterialPlate(name="liaopan2", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan2, Coordinate(x=1130, y=50, z=0)) + + liaopan3 = MaterialPlate(name="liaopan3", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan3, Coordinate(x=1250, y=50, z=0)) + + liaopan4 = MaterialPlate(name="liaopan4", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan4, Coordinate(x=1010, y=150, z=0)) + for i in range(16): + jipian_4 = ElectrodeSheet(name=f"{liaopan4.name}_jipian_{i}", size_x=12, size_y=12, size_z=0.1) + liaopan4.children[i].assign_child_resource(jipian_4, location=None) + liaopan5 = MaterialPlate(name="liaopan5", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan5, Coordinate(x=1130, y=150, z=0)) + liaopan6 = MaterialPlate(name="liaopan6", size_x=120, size_y=100, size_z=10.0, fill=True) + deck.assign_child_resource(liaopan6, Coordinate(x=1250, y=150, z=0)) + #liaopan.children[3].assign_child_resource(jipian, location=None) + """======================================物料板============================================""" + """======================================瓶架,移液枪============================================""" + # 在台面上放置 3x4 瓶架、6x2 瓶架 与 64孔移液枪头盒 + bottle_rack_3x4 = BottleRack( + name="bottle_rack_3x4", + size_x=210.0, + size_y=140.0, + size_z=100.0, + num_items_x=3, + num_items_y=4, + position_spacing=35.0, + orientation="vertical", + ) + deck.assign_child_resource(bottle_rack_3x4, Coordinate(x=100, y=200, z=0)) + + bottle_rack_6x2 = BottleRack( + name="bottle_rack_6x2", + size_x=120.0, + size_y=250.0, + size_z=100.0, + num_items_x=6, + num_items_y=2, + position_spacing=35.0, + orientation="vertical", + ) + deck.assign_child_resource(bottle_rack_6x2, Coordinate(x=300, y=300, z=0)) + + bottle_rack_6x2_2 = BottleRack( + name="bottle_rack_6x2_2", + size_x=120.0, + size_y=250.0, + size_z=100.0, + num_items_x=6, + num_items_y=2, + position_spacing=35.0, + orientation="vertical", + ) + deck.assign_child_resource(bottle_rack_6x2_2, Coordinate(x=430, y=300, z=0)) + + + # 将 ElectrodeSheet 放满 3x4 与 6x2 的所有孔位 + for idx in range(bottle_rack_3x4.num_items_x * bottle_rack_3x4.num_items_y): + sheet = ElectrodeSheet(name=f"sheet_3x4_{idx}", size_x=12, size_y=12, size_z=0.1) + bottle_rack_3x4.assign_child_resource(sheet, index=idx) + + for idx in range(bottle_rack_6x2.num_items_x * bottle_rack_6x2.num_items_y): + sheet = ElectrodeSheet(name=f"sheet_6x2_{idx}", size_x=12, size_y=12, size_z=0.1) + bottle_rack_6x2.assign_child_resource(sheet, index=idx) + + tip_box = TipBox64(name="tip_box_64") + deck.assign_child_resource(tip_box, Coordinate(x=300, y=100, z=0)) + + waste_tip_box = WasteTipBox(name="waste_tip_box") + deck.assign_child_resource(waste_tip_box, Coordinate(x=300, y=200, z=0)) + """======================================瓶架,移液枪============================================""" + print(deck) + + + from unilabos.resources.graphio import convert_resources_from_type + from unilabos.config.config import BasicConfig + BasicConfig.ak = "beb0c15f-2279-46a1-aba5-00eaf89aef55" + BasicConfig.sk = "15d4f25e-3512-4f9c-9bfb-43ab85e7b561" + from unilabos.app.web.client import http_client + + resources = convert_resources_from_type([deck], [Resource]) + + # 检查序列化后的资源 + + json.dump({"nodes": resources, "links": []}, open("button_battery_station_resources_unilab.json", "w"), indent=2) + + + #print(resources) + http_client.remote_addr = "https://uni-lab.test.bohrium.com/api/v1" + + http_client.resource_add(resources) \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/button_battery_station_resources_unilab_big.json b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station_resources_unilab_big.json new file mode 100644 index 00000000..9468817f --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station_resources_unilab_big.json @@ -0,0 +1,1895 @@ +{ + "nodes": [ + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "liaopan1", + "liaopan2" + ], + "parent": null, + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1620.0, + "size_y": 1270.0, + "size_z": 500.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "liaopan1", + "name": "liaopan1", + "sample_id": null, + "children": [ + "liaopan1_materialhole_0_0", + "liaopan1_materialhole_0_1", + "liaopan1_materialhole_0_2", + "liaopan1_materialhole_0_3", + "liaopan1_materialhole_1_0", + "liaopan1_materialhole_1_1", + "liaopan1_materialhole_1_2", + "liaopan1_materialhole_1_3", + "liaopan1_materialhole_2_0", + "liaopan1_materialhole_2_1", + "liaopan1_materialhole_2_2", + "liaopan1_materialhole_2_3", + "liaopan1_materialhole_3_0", + "liaopan1_materialhole_3_1", + "liaopan1_materialhole_3_2", + "liaopan1_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan1_materialhole_0_0", + "B1": "liaopan1_materialhole_0_1", + "C1": "liaopan1_materialhole_0_2", + "D1": "liaopan1_materialhole_0_3", + "A2": "liaopan1_materialhole_1_0", + "B2": "liaopan1_materialhole_1_1", + "C2": "liaopan1_materialhole_1_2", + "D2": "liaopan1_materialhole_1_3", + "A3": "liaopan1_materialhole_2_0", + "B3": "liaopan1_materialhole_2_1", + "C3": "liaopan1_materialhole_2_2", + "D3": "liaopan1_materialhole_2_3", + "A4": "liaopan1_materialhole_3_0", + "B4": "liaopan1_materialhole_3_1", + "C4": "liaopan1_materialhole_3_2", + "D4": "liaopan1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan1_materialhole_0_0", + "name": "liaopan1_materialhole_0_0", + "sample_id": null, + "children": [ + "jipian_0" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_0", + "name": "jipian_0", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_1", + "name": "liaopan1_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_2", + "name": "liaopan1_materialhole_0_2", + "sample_id": null, + "children": [ + "jipian_2" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_2", + "name": "jipian_2", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_3", + "name": "liaopan1_materialhole_0_3", + "sample_id": null, + "children": [ + "jipian_3" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_3", + "name": "jipian_3", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_0", + "name": "liaopan1_materialhole_1_0", + "sample_id": null, + "children": [ + "jipian_4" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_4", + "name": "jipian_4", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_1", + "name": "liaopan1_materialhole_1_1", + "sample_id": null, + "children": [ + "jipian_5" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_5", + "name": "jipian_5", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_2", + "name": "liaopan1_materialhole_1_2", + "sample_id": null, + "children": [ + "jipian_6" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_6", + "name": "jipian_6", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_3", + "name": "liaopan1_materialhole_1_3", + "sample_id": null, + "children": [ + "jipian_7" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_7", + "name": "jipian_7", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_0", + "name": "liaopan1_materialhole_2_0", + "sample_id": null, + "children": [ + "jipian_8" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_8", + "name": "jipian_8", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_1", + "name": "liaopan1_materialhole_2_1", + "sample_id": null, + "children": [ + "jipian_9" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_9", + "name": "jipian_9", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_2", + "name": "liaopan1_materialhole_2_2", + "sample_id": null, + "children": [ + "jipian_10" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_10", + "name": "jipian_10", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_3", + "name": "liaopan1_materialhole_2_3", + "sample_id": null, + "children": [ + "jipian_11" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_11", + "name": "jipian_11", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_0", + "name": "liaopan1_materialhole_3_0", + "sample_id": null, + "children": [ + "jipian_12" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_12", + "name": "jipian_12", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_1", + "name": "liaopan1_materialhole_3_1", + "sample_id": null, + "children": [ + "jipian_13" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_13", + "name": "jipian_13", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_2", + "name": "liaopan1_materialhole_3_2", + "sample_id": null, + "children": [ + "jipian_14" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_14", + "name": "jipian_14", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_3", + "name": "liaopan1_materialhole_3_3", + "sample_id": null, + "children": [ + "jipian_15" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_15", + "name": "jipian_15", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2", + "name": "liaopan2", + "sample_id": null, + "children": [ + "liaopan2_materialhole_0_0", + "liaopan2_materialhole_0_1", + "liaopan2_materialhole_0_2", + "liaopan2_materialhole_0_3", + "liaopan2_materialhole_1_0", + "liaopan2_materialhole_1_1", + "liaopan2_materialhole_1_2", + "liaopan2_materialhole_1_3", + "liaopan2_materialhole_2_0", + "liaopan2_materialhole_2_1", + "liaopan2_materialhole_2_2", + "liaopan2_materialhole_2_3", + "liaopan2_materialhole_3_0", + "liaopan2_materialhole_3_1", + "liaopan2_materialhole_3_2", + "liaopan2_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 500, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan2_materialhole_0_0", + "B1": "liaopan2_materialhole_0_1", + "C1": "liaopan2_materialhole_0_2", + "D1": "liaopan2_materialhole_0_3", + "A2": "liaopan2_materialhole_1_0", + "B2": "liaopan2_materialhole_1_1", + "C2": "liaopan2_materialhole_1_2", + "D2": "liaopan2_materialhole_1_3", + "A3": "liaopan2_materialhole_2_0", + "B3": "liaopan2_materialhole_2_1", + "C3": "liaopan2_materialhole_2_2", + "D3": "liaopan2_materialhole_2_3", + "A4": "liaopan2_materialhole_3_0", + "B4": "liaopan2_materialhole_3_1", + "C4": "liaopan2_materialhole_3_2", + "D4": "liaopan2_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan2_materialhole_0_0", + "name": "liaopan2_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_1", + "name": "liaopan2_materialhole_0_1", + "sample_id": null, + "children": [ + "jipian_1" + ], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_1", + "name": "jipian_1", + "sample_id": null, + "children": [], + "parent": "liaopan2_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_2", + "name": "liaopan2_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_3", + "name": "liaopan2_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_0", + "name": "liaopan2_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_1", + "name": "liaopan2_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_2", + "name": "liaopan2_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_3", + "name": "liaopan2_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_0", + "name": "liaopan2_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_1", + "name": "liaopan2_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_2", + "name": "liaopan2_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_3", + "name": "liaopan2_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_0", + "name": "liaopan2_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_1", + "name": "liaopan2_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_2", + "name": "liaopan2_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_3", + "name": "liaopan2_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/celljson.json b/unilabos/devices/workstation/coin_cell_assembly/celljson.json new file mode 100644 index 00000000..ad904175 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/celljson.json @@ -0,0 +1,1332 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "coin_cell_deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "ban_cao_wei" + ], + "parent": "BatteryStation", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1620.0, + "size_y": 1270.0, + "size_z": 500.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "ban_cao_wei", + "name": "ban_cao_wei", + "sample_id": null, + "children": [ + "plate_1" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "PlateSlot", + "size_x": 0, + "size_y": 0, + "size_z": 0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "plate_slot", + "model": null, + "barcode": null, + "max_plates": 8 + }, + "data": {} + }, + { + "id": "plate_1", + "name": "plate_1", + "sample_id": null, + "children": [ + "plate_1_materialhole_0_0", + "plate_1_materialhole_0_1", + "plate_1_materialhole_0_2", + "plate_1_materialhole_0_3", + "plate_1_materialhole_1_0", + "plate_1_materialhole_1_1", + "plate_1_materialhole_1_2", + "plate_1_materialhole_1_3", + "plate_1_materialhole_2_0", + "plate_1_materialhole_2_1", + "plate_1_materialhole_2_2", + "plate_1_materialhole_2_3", + "plate_1_materialhole_3_0", + "plate_1_materialhole_3_1", + "plate_1_materialhole_3_2", + "plate_1_materialhole_3_3" + ], + "parent": "ban_cao_wei", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "plate_1_materialhole_0_0", + "B1": "plate_1_materialhole_0_1", + "C1": "plate_1_materialhole_0_2", + "D1": "plate_1_materialhole_0_3", + "A2": "plate_1_materialhole_1_0", + "B2": "plate_1_materialhole_1_1", + "C2": "plate_1_materialhole_1_2", + "D2": "plate_1_materialhole_1_3", + "A3": "plate_1_materialhole_2_0", + "B3": "plate_1_materialhole_2_1", + "C3": "plate_1_materialhole_2_2", + "D3": "plate_1_materialhole_2_3", + "A4": "plate_1_materialhole_3_0", + "B4": "plate_1_materialhole_3_1", + "C4": "plate_1_materialhole_3_2", + "D4": "plate_1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "plate_1_materialhole_0_0", + "name": "plate_1_materialhole_0_0", + "sample_id": null, + "children": [ + "hole_0_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -29.5, + "y": 30.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_0_sheet_1", + "name": "hole_0_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_0_1", + "name": "plate_1_materialhole_0_1", + "sample_id": null, + "children": [ + "hole_1_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -29.5, + "y": 10.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_1_sheet_1", + "name": "hole_1_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_0_2", + "name": "plate_1_materialhole_0_2", + "sample_id": null, + "children": [ + "hole_2_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -29.5, + "y": -9.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_2_sheet_1", + "name": "hole_2_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_0_3", + "name": "plate_1_materialhole_0_3", + "sample_id": null, + "children": [ + "hole_3_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -29.5, + "y": -29.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_3_sheet_1", + "name": "hole_3_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_1_0", + "name": "plate_1_materialhole_1_0", + "sample_id": null, + "children": [ + "hole_4_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -9.5, + "y": 30.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_4_sheet_1", + "name": "hole_4_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_1_1", + "name": "plate_1_materialhole_1_1", + "sample_id": null, + "children": [ + "hole_5_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -9.5, + "y": 10.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_5_sheet_1", + "name": "hole_5_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_1_2", + "name": "plate_1_materialhole_1_2", + "sample_id": null, + "children": [ + "hole_6_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -9.5, + "y": -9.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_6_sheet_1", + "name": "hole_6_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_1_3", + "name": "plate_1_materialhole_1_3", + "sample_id": null, + "children": [ + "hole_7_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": -9.5, + "y": -29.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_7_sheet_1", + "name": "hole_7_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_2_0", + "name": "plate_1_materialhole_2_0", + "sample_id": null, + "children": [ + "hole_8_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 10.5, + "y": 30.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_8_sheet_1", + "name": "hole_8_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_2_1", + "name": "plate_1_materialhole_2_1", + "sample_id": null, + "children": [ + "hole_9_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 10.5, + "y": 10.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_9_sheet_1", + "name": "hole_9_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_2_2", + "name": "plate_1_materialhole_2_2", + "sample_id": null, + "children": [ + "hole_10_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 10.5, + "y": -9.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_10_sheet_1", + "name": "hole_10_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_2_3", + "name": "plate_1_materialhole_2_3", + "sample_id": null, + "children": [ + "hole_11_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 10.5, + "y": -29.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_11_sheet_1", + "name": "hole_11_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_3_0", + "name": "plate_1_materialhole_3_0", + "sample_id": null, + "children": [ + "hole_12_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 30.5, + "y": 30.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_12_sheet_1", + "name": "hole_12_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_3_1", + "name": "plate_1_materialhole_3_1", + "sample_id": null, + "children": [ + "hole_13_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 30.5, + "y": 10.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_13_sheet_1", + "name": "hole_13_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_3_2", + "name": "plate_1_materialhole_3_2", + "sample_id": null, + "children": [ + "hole_14_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 30.5, + "y": -9.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_14_sheet_1", + "name": "hole_14_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + }, + { + "id": "plate_1_materialhole_3_3", + "name": "plate_1_materialhole_3_3", + "sample_id": null, + "children": [ + "hole_15_sheet_1" + ], + "parent": "plate_1", + "type": "container", + "class": "", + "position": { + "x": 30.5, + "y": -29.5, + "z": 1 + }, + "config": { + "type": "MaterialHole", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "depth": 1.0, + "diameter": 14, + "info": "", + "max_sheets": 1 + } + }, + { + "id": "hole_15_sheet_1", + "name": "hole_15_sheet_1", + "sample_id": null, + "children": [], + "parent": "plate_1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 1, + "size_y": 1, + "size_z": 1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "info": "NMC", + "mass": 5.0, + "material_type": "positive_electrode", + "thickness": 0.1 + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py index 4b8ba73d..cd44444d 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py @@ -21,7 +21,7 @@ from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode class CoinCellAssemblyWorkstation(WorkstationBase): def __init__( self, - deck: CoincellDeck, + station_resource: CoincellDeck, address: str = "192.168.1.20", port: str = "502", debug_mode: bool = True, @@ -30,12 +30,12 @@ class CoinCellAssemblyWorkstation(WorkstationBase): ): super().__init__( #桌子 - deck=deck, + station_resource=station_resource, *args, **kwargs, ) self.debug_mode = debug_mode - self.deck = deck + self.station_resource = station_resource """ 连接初始化 """ modbus_client = TCPClient(addr=address, port=port) print("modbus_client", modbus_client) @@ -60,6 +60,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): self.csv_export_thread = None self.csv_export_running = False self.csv_export_file = None + self.coin_num_N = 0 #已组装电池数量 #创建一个物料台面,包含两个极片板 #self.deck = create_a_coin_cell_deck() @@ -74,7 +75,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): self._ros_node = ros_node #self.deck = create_a_coin_cell_deck() ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{ - "resources": [self.deck] + "resources": [self.station_resource] }) # 批量操作在这里写 @@ -84,7 +85,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): async def fill_plate(self): - plate_1: MaterialPlate = self.deck.children[0].children[0] + plate_1: MaterialPlate = self.station_resource.children[0].children[0] #plate_1 return await self._ros_node.update_resource(plate_1) @@ -341,7 +342,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): def modify_deck_name(self, resource_name: str): # figure_res = self._ros_node.resource_tracker.figure_resource({"name": resource_name}) # print(f"!!! figure_res: {type(figure_res)}") - self.deck.children[1] + self.station_resource.children[1] return @property @@ -606,7 +607,8 @@ class CoinCellAssemblyWorkstation(WorkstationBase): print("waiting for start_cmd") time.sleep(1) - def func_pack_send_bottle_num(self, bottle_num: int): + def func_pack_send_bottle_num(self, bottle_num): + bottle_num = int(bottle_num) #发送电解液平台数 print("启动") while (self._unilab_rece_electrolyte_bottle_num()) == False: @@ -697,6 +699,23 @@ class CoinCellAssemblyWorkstation(WorkstationBase): print("data_electrolyte_code", data_electrolyte_code) print("data_coin_cell_code", data_coin_cell_code) #接收完信息后,读取完毕标志位置True + liaopan3 = self.station_resource.get_resource("\u7535\u6c60\u6599\u76d8") + #把物料解绑后放到另一盘上 + battery = ElectrodeSheet(name=f"battery_{self.coin_num_N}", size_x=14, size_y=14, size_z=2) + battery._unilabos_state = { + "electrolyte_name": data_coin_cell_code, + "data_electrolyte_code": data_electrolyte_code, + "open_circuit_voltage": data_open_circuit_voltage, + "assembly_pressure": data_assembly_pressure, + "electrolyte_volume": data_electrolyte_volume + } + liaopan3.children[self.coin_num_N].assign_child_resource(battery, location=None) + #print(jipian2.parent) + ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{ + "resources": [self.station_resource] + }) + + self._unilab_rec_msg_succ_cmd(True) time.sleep(1) #等待允许读取标志位置False @@ -757,6 +776,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): def func_allpack_cmd(self, elec_num, elec_use_num, file_path: str="D:\\coin_cell_data") -> bool: + elec_num, elec_use_num = int(elec_num), int(elec_use_num) summary_csv_file = os.path.join(file_path, "duandian.csv") # 如果断点文件存在,先读取之前的进度 if os.path.exists(summary_csv_file): @@ -784,20 +804,22 @@ class CoinCellAssemblyWorkstation(WorkstationBase): elec_num_N = 0 elec_use_num_N = 0 coin_num_N = 0 - - print(f"剩余电解液瓶数: {elec_num}, 已组装电池数: {elec_use_num}") - + for i in range(20): + print(f"剩余电解液瓶数: {elec_num}, 已组装电池数: {elec_use_num}") + print(f"剩余电解液瓶数: {type(elec_num)}, 已组装电池数: {type(elec_use_num)}") + print(f"剩余电解液瓶数: {type(int(elec_num))}, 已组装电池数: {type(int(elec_use_num))}") #如果是第一次运行,则进行初始化、切换自动、启动, 如果是断点重启则跳过。 if read_status_flag == False: + pass #初始化 - self.func_pack_device_init() + #self.func_pack_device_init() #切换自动 - self.func_pack_device_auto() + #self.func_pack_device_auto() #启动,小车收回 - self.func_pack_device_start() + #self.func_pack_device_start() #发送电解液瓶数量,启动搬运,多搬运没事 - self.func_pack_send_bottle_num(elec_num) + #self.func_pack_send_bottle_num(elec_num) last_i = elec_num_N last_j = elec_use_num_N for i in range(last_i, elec_num): @@ -811,27 +833,9 @@ class CoinCellAssemblyWorkstation(WorkstationBase): #读取电池组装数据并存入csv self.func_pack_get_msg_cmd(file_path) time.sleep(1) - - #这里定义物料系统 # TODO:读完再将电池数加一还是进入循环就将电池数加一需要考虑 - liaopan1 = self.deck.get_resource("liaopan1") - liaopan4 = self.deck.get_resource("liaopan4") - jipian1 = liaopan1.children[coin_num_N].children[0] - jipian4 = liaopan4.children[coin_num_N].children[0] - #print(jipian1) - #从料盘上去物料解绑后放到另一盘上 - jipian1.parent.unassign_child_resource(jipian1) - jipian4.parent.unassign_child_resource(jipian4) - - #print(jipian2.parent) - battery = Battery(name = f"battery_{coin_num_N}") - battery.assign_child_resource(jipian1, location=None) - battery.assign_child_resource(jipian4, location=None) - - zidanjia6 = self.deck.get_resource("zi_dan_jia6") - zidanjia6.children[0].assign_child_resource(battery, location=None) - + # 生成断点文件 # 生成包含elec_num_N、coin_num_N、timestamp的CSV文件 @@ -842,6 +846,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): writer.writerow([elec_num, elec_use_num, elec_num_N, elec_use_num_N, coin_num_N, timestamp]) csvfile.flush() coin_num_N += 1 + self.coin_num_N = coin_num_N elec_use_num_N += 1 elec_num_N += 1 elec_use_num_N = 0 @@ -878,34 +883,37 @@ class CoinCellAssemblyWorkstation(WorkstationBase): def fun_wuliao_test(self) -> bool: #找到data_init中构建的2个物料盘 - #liaopan1 = self.deck.get_resource("liaopan1") - #liaopan4 = self.deck.get_resource("liaopan4") - #for coin_num_N in range(16): - # liaopan1 = self.deck.get_resource("liaopan1") - # liaopan4 = self.deck.get_resource("liaopan4") - # jipian1 = liaopan1.children[coin_num_N].children[0] - # jipian4 = liaopan4.children[coin_num_N].children[0] - # #print(jipian1) - # #从料盘上去物料解绑后放到另一盘上 - # jipian1.parent.unassign_child_resource(jipian1) - # jipian4.parent.unassign_child_resource(jipian4) - # - # #print(jipian2.parent) - # battery = Battery(name = f"battery_{coin_num_N}") - # battery.assign_child_resource(jipian1, location=None) - # battery.assign_child_resource(jipian4, location=None) - # - # zidanjia6 = self.deck.get_resource("zi_dan_jia6") - # zidanjia6.children[0].assign_child_resource(battery, location=None) - # ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{ - # "resources": [self.deck] - # }) - # time.sleep(2) - for i in range(20): - print(f"输出{i}") - time.sleep(2) + liaopan1 = self.station_resource.get_resource("liaopan1") + liaopan2 = self.station_resource.get_resource("liaopan2") + for i in range(16): + #找到liaopan1上每一个jipian + jipian_linshi = liaopan1.children[i].children[0] + #把物料解绑后放到另一盘上 + print("极片:", jipian_linshi) + jipian_linshi.parent.unassign_child_resource(jipian_linshi) + liaopan2.children[i].assign_child_resource(jipian_linshi, location=None) + ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{ + "resources": [self.station_resource] + }) + time.sleep(4) + """ + liaopan3 = self.station_resource.get_resource("\u7535\u6c60\u6599\u76d8") + for i in range(16): + battery = ElectrodeSheet(name=f"battery_{i}", size_x=16, size_y=16, size_z=2) + battery._unilabos_state = { + "diameter": 20.0, + "height": 20.0, + "assembly_pressure": i, + "electrolyte_volume": 20.0, + "electrolyte_name": f"DP{i}" + } + liaopan3.children[i].assign_child_resource(battery, location=None) - + ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{ + "resources": [self.station_resource] + }) + time.sleep(4) + """ # 数据读取与输出 def func_read_data_and_output(self, file_path: str="D:\\coin_cell_data"): # 检查CSV导出是否正在运行,已运行则跳出,防止同时启动两个while循环 @@ -1119,24 +1127,52 @@ if __name__ == "__main__": #print("success") #创建一个物料台面 - #deck = create_a_coin_cell_deck() + deck = create_a_coin_cell_deck() + #deck = create_a_full_coin_cell_deck() + ##在台面上找到料盘和极片 #liaopan1 = deck.get_resource("liaopan1") #liaopan2 = deck.get_resource("liaopan2") #jipian1 = liaopan1.children[1].children[0] -# - ##print(jipian1) +## + #print(jipian1) ##把物料解绑后放到另一盘上 #jipian1.parent.unassign_child_resource(jipian1) #liaopan2.children[1].assign_child_resource(jipian1, location=None) ##print(jipian2.parent) + + liaopan1 = deck.get_resource("liaopan1") + liaopan2 = deck.get_resource("liaopan2") + for i in range(16): + #找到liaopan1上每一个jipian + jipian_linshi = liaopan1.children[i].children[0] + #把物料解绑后放到另一盘上 + print("极片:", jipian_linshi) + jipian_linshi.parent.unassign_child_resource(jipian_linshi) + liaopan2.children[i].assign_child_resource(jipian_linshi, location=None) + + from unilabos.resources.graphio import resource_ulab_to_plr, convert_resources_to_type + #with open("./button_battery_station_resources_unilab.json", "r", encoding="utf-8") as f: + # bioyond_resources_unilab = json.load(f) + #print(f"成功读取 JSON 文件,包含 {len(bioyond_resources_unilab)} 个资源") + #ulab_resources = convert_resources_to_type(bioyond_resources_unilab, List[PLRResource]) + #print(f"转换结果类型: {type(ulab_resources)}") + #print(ulab_resources) - with open("./button_battery_decks_unilab.json", "r", encoding="utf-8") as f: - bioyond_resources_unilab = json.load(f) - print(f"成功读取 JSON 文件,包含 {len(bioyond_resources_unilab)} 个资源") - ulab_resources = convert_resources_to_type(bioyond_resources_unilab, List[PLRResource]) - print(f"转换结果类型: {type(ulab_resources)}") - print(ulab_resources) + + from unilabos.resources.graphio import convert_resources_from_type + from unilabos.config.config import BasicConfig + BasicConfig.ak = "beb0c15f-2279-46a1-aba5-00eaf89aef55" + BasicConfig.sk = "15d4f25e-3512-4f9c-9bfb-43ab85e7b561" + from unilabos.app.web.client import http_client + + resources = convert_resources_from_type([deck], [Resource]) + json.dump({"nodes": resources, "links": []}, open("button_battery_station_resources_unilab.json", "w"), indent=2) + + #print(resources) + http_client.remote_addr = "https://uni-lab.test.bohrium.com/api/v1" + + http_client.resource_add(resources) \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_0910.csv b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_0910.csv new file mode 100644 index 00000000..e8407159 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_0910.csv @@ -0,0 +1,44 @@ +Name,DataType,InitValue,Comment,Attribute,DeviceType,Address +COIL_SYS_START_CMD,BOOL,,豸,,coil,8010 +COIL_SYS_STOP_CMD,BOOL,,豸ֹͣ,,coil,8020 +COIL_SYS_RESET_CMD,BOOL,,豸λ,,coil,8030 +COIL_SYS_HAND_CMD,BOOL,,豸ֶģʽ,,coil,8040 +COIL_SYS_AUTO_CMD,BOOL,,豸Զģʽ,,coil,8050 +COIL_SYS_INIT_CMD,BOOL,,豸ʼģʽ,,coil,8060 +COIL_UNILAB_SEND_MSG_SUCC_CMD,BOOL,,UNILAB䷽,,coil,8700 +COIL_UNILAB_REC_MSG_SUCC_CMD,BOOL,,UNILABܲ,,coil,8710 +COIL_SYS_START_STATUS,BOOL,,豸,,coil,8210 +COIL_SYS_STOP_STATUS,BOOL,,豸ֹͣ,,coil,8220 +COIL_SYS_RESET_STATUS,BOOL,,豸λ,,coil,8230 +COIL_SYS_HAND_STATUS,BOOL,,豸ֶģʽ,,coil,8240 +COIL_SYS_AUTO_STATUS,BOOL,,豸Զģʽ,,coil,8250 +COIL_SYS_INIT_STATUS,BOOL,,豸ʼ,,coil,8260 +COIL_REQUEST_REC_MSG_STATUS,BOOL,,豸䷽,,coil,8510 +COIL_REQUEST_SEND_MSG_STATUS,BOOL,,豸Ͳ,,coil,8500 +REG_MSG_ELECTROLYTE_USE_NUM,INT16,,ƿҺʹô,,hold_register,11000 +REG_MSG_ELECTROLYTE_NUM,INT16,,Һʹƿ,,hold_register,11002 +REG_MSG_ELECTROLYTE_VOLUME,INT16,,Һȡ,,hold_register,11004 +REG_MSG_ASSEMBLY_TYPE,INT16,,װƬѵʽ,,hold_register,11006 +REG_MSG_ASSEMBLY_PRESSURE,INT16,,װѹ,,hold_register,11008 +REG_DATA_ASSEMBLY_COIN_CELL_NUM,INT16,,ǰװ,,hold_register,10000 +REG_DATA_OPEN_CIRCUIT_VOLTAGE,FLOAT32,,ǰصѹ,,hold_register,10002 +REG_DATA_AXIS_X_POS,FLOAT32,,ҺXᵱǰλ,,hold_register,10004 +REG_DATA_AXIS_Y_POS,FLOAT32,,ҺZᵱǰλ,,hold_register,10006 +REG_DATA_AXIS_Z_POS,FLOAT32,,ҺYᵱǰλ,,hold_register,10008 +REG_DATA_POLE_WEIGHT,FLOAT32,,ǰƬ,,hold_register,10010 +REG_DATA_ASSEMBLY_PER_TIME,FLOAT32,,ǰŵװʱ,,hold_register,10012 +REG_DATA_ASSEMBLY_PRESSURE,INT16,,ǰװѹ,,hold_register,10014 +REG_DATA_ELECTROLYTE_VOLUME,INT16,,ǰҺע,,hold_register,10016 +REG_DATA_COIN_NUM,INT16,,ǰ,,hold_register,10018 +REG_DATA_ELECTROLYTE_CODE,STRING,,Һάк,,hold_register,10020 +REG_DATA_COIN_CELL_CODE,STRING,,ضάк,,hold_register,10030 +REG_DATA_STACK_VISON_CODE,STRING,,϶ѵͼƬ,,hold_register,12004 +REG_DATA_GLOVE_BOX_PRESSURE,FLOAT32,,ѹ,,hold_register,10050 +REG_DATA_GLOVE_BOX_WATER_CONTENT,FLOAT32,,ˮ,,hold_register,10052 +REG_DATA_GLOVE_BOX_O2_CONTENT,FLOAT32,,,,hold_register,10054 +UNILAB_SEND_ELECTROLYTE_BOTTLE_NUM,BOOL,,Unilabȷѷ͵Һƿź,,coil,8720 +UNILAB_RECE_ELECTROLYTE_BOTTLE_NUM,BOOL,,UnilabɽܵҺƿ,,coil,8520 +REG_MSG_ELECTROLYTE_NUM_USED,INT16,,Һװƽ,,hold_register,496 +REG_DATA_ELECTROLYTE_USE_NUM,INT16,,ǰװƽ,,hold_register,10000 +UNILAB_SEND_FINISHED_CMD,BOOL,,Unilabյź,,coil,8730 +UNILAB_RECE_FINISHED_CMD,BOOL,,֪unilabź,,coil,8530 diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv new file mode 100644 index 00000000..149a6833 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv @@ -0,0 +1,46 @@ +Name,DataType,InitValue,Comment,Attribute,DeviceType,Address, +COIL_SYS_START_CMD,BOOL,,,,coil,8010, +COIL_SYS_STOP_CMD,BOOL,,,,coil,8020, +COIL_SYS_RESET_CMD,BOOL,,,,coil,8030, +COIL_SYS_HAND_CMD,BOOL,,,,coil,8040, +COIL_SYS_AUTO_CMD,BOOL,,,,coil,8050, +COIL_SYS_INIT_CMD,BOOL,,,,coil,8060, +COIL_UNILAB_SEND_MSG_SUCC_CMD,BOOL,,,,coil,8700, +COIL_UNILAB_REC_MSG_SUCC_CMD,BOOL,,,,coil,8710,unilab_rec_msg_succ_cmd +COIL_SYS_START_STATUS,BOOL,,,,coil,8210, +COIL_SYS_STOP_STATUS,BOOL,,,,coil,8220, +COIL_SYS_RESET_STATUS,BOOL,,,,coil,8230, +COIL_SYS_HAND_STATUS,BOOL,,,,coil,8240, +COIL_SYS_AUTO_STATUS,BOOL,,,,coil,8250, +COIL_SYS_INIT_STATUS,BOOL,,,,coil,8260, +COIL_REQUEST_REC_MSG_STATUS,BOOL,,,,coil,8500, +COIL_REQUEST_SEND_MSG_STATUS,BOOL,,,,coil,8510,request_send_msg_status +REG_MSG_ELECTROLYTE_USE_NUM,INT16,,,,hold_register,11000, +REG_MSG_ELECTROLYTE_NUM,INT16,,,,hold_register,11002,unilab_send_msg_electrolyte_num +REG_MSG_ELECTROLYTE_VOLUME,INT16,,,,hold_register,11004,unilab_send_msg_electrolyte_vol +REG_MSG_ASSEMBLY_TYPE,INT16,,,,hold_register,11006,unilab_send_msg_assembly_type +REG_MSG_ASSEMBLY_PRESSURE,INT16,,,,hold_register,11008,unilab_send_msg_assembly_pressure +REG_DATA_ASSEMBLY_COIN_CELL_NUM,INT16,,,,hold_register,10000,data_assembly_coin_cell_num +REG_DATA_OPEN_CIRCUIT_VOLTAGE,FLOAT32,,,,hold_register,10002,data_open_circuit_voltage +REG_DATA_AXIS_X_POS,FLOAT32,,,,hold_register,10004, +REG_DATA_AXIS_Y_POS,FLOAT32,,,,hold_register,10006, +REG_DATA_AXIS_Z_POS,FLOAT32,,,,hold_register,10008, +REG_DATA_POLE_WEIGHT,FLOAT32,,,,hold_register,10010,data_pole_weight +REG_DATA_ASSEMBLY_PER_TIME,FLOAT32,,,,hold_register,10012,data_assembly_time +REG_DATA_ASSEMBLY_PRESSURE,INT16,,,,hold_register,10014,data_assembly_pressure +REG_DATA_ELECTROLYTE_VOLUME,INT16,,,,hold_register,10016,data_electrolyte_volume +REG_DATA_COIN_NUM,INT16,,,,hold_register,10018,data_coin_num +REG_DATA_ELECTROLYTE_CODE,STRING,,,,hold_register,10020,data_electrolyte_code() +REG_DATA_COIN_CELL_CODE,STRING,,,,hold_register,10030,data_coin_cell_code() +REG_DATA_STACK_VISON_CODE,STRING,,,,hold_register,12004,data_stack_vision_code() +REG_DATA_GLOVE_BOX_PRESSURE,FLOAT32,,,,hold_register,10050,data_glove_box_pressure +REG_DATA_GLOVE_BOX_WATER_CONTENT,FLOAT32,,,,hold_register,10052,data_glove_box_water_content +REG_DATA_GLOVE_BOX_O2_CONTENT,FLOAT32,,,,hold_register,10054,data_glove_box_o2_content +UNILAB_SEND_ELECTROLYTE_BOTTLE_NUM,BOOL,,,,coil,8720, +UNILAB_RECE_ELECTROLYTE_BOTTLE_NUM,BOOL,,,,coil,8520, +REG_MSG_ELECTROLYTE_NUM_USED,INT16,,,,hold_register,496, +REG_DATA_ELECTROLYTE_USE_NUM,INT16,,,,hold_register,10000, +UNILAB_SEND_FINISHED_CMD,BOOL,,,,coil,8730, +UNILAB_RECE_FINISHED_CMD,BOOL,,,,coil,8530, +REG_DATA_ASSEMBLY_TYPE,INT16,,,,hold_register,10018,ASSEMBLY_TYPE7or8 +COIL_ALUMINUM_FOIL,BOOL,,,,coil,8340, diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig.json new file mode 100644 index 00000000..31fb0033 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig.json @@ -0,0 +1,1925 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "deck", + "name": "deck", + "sample_id": null, + "children": [ + "liaopan1", + "liaopan2" + ], + "parent": null, + "type": "deck", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "Deck", + "size_x": 1200, + "size_y": 800, + "size_z": 900, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "deck", + "barcode": null + }, + "data": {} + }, + { + "id": "liaopan1", + "name": "liaopan1", + "sample_id": null, + "children": [ + "liaopan1_materialhole_0_0", + "liaopan1_materialhole_0_1", + "liaopan1_materialhole_0_2", + "liaopan1_materialhole_0_3", + "liaopan1_materialhole_1_0", + "liaopan1_materialhole_1_1", + "liaopan1_materialhole_1_2", + "liaopan1_materialhole_1_3", + "liaopan1_materialhole_2_0", + "liaopan1_materialhole_2_1", + "liaopan1_materialhole_2_2", + "liaopan1_materialhole_2_3", + "liaopan1_materialhole_3_0", + "liaopan1_materialhole_3_1", + "liaopan1_materialhole_3_2", + "liaopan1_materialhole_3_3" + ], + "parent": "deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan1_materialhole_0_0", + "B1": "liaopan1_materialhole_0_1", + "C1": "liaopan1_materialhole_0_2", + "D1": "liaopan1_materialhole_0_3", + "A2": "liaopan1_materialhole_1_0", + "B2": "liaopan1_materialhole_1_1", + "C2": "liaopan1_materialhole_1_2", + "D2": "liaopan1_materialhole_1_3", + "A3": "liaopan1_materialhole_2_0", + "B3": "liaopan1_materialhole_2_1", + "C3": "liaopan1_materialhole_2_2", + "D3": "liaopan1_materialhole_2_3", + "A4": "liaopan1_materialhole_3_0", + "B4": "liaopan1_materialhole_3_1", + "C4": "liaopan1_materialhole_3_2", + "D4": "liaopan1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan1_materialhole_0_0", + "name": "liaopan1_materialhole_0_0", + "sample_id": null, + "children": [ + "jipian_0" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_0", + "name": "jipian_0", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_1", + "name": "liaopan1_materialhole_0_1", + "sample_id": null, + "children": [ + "jipian_1" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_1", + "name": "jipian_1", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_2", + "name": "liaopan1_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_3", + "name": "liaopan1_materialhole_0_3", + "sample_id": null, + "children": [ + "jipian_3" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_3", + "name": "jipian_3", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_0", + "name": "liaopan1_materialhole_1_0", + "sample_id": null, + "children": [ + "jipian_4" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_4", + "name": "jipian_4", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_1", + "name": "liaopan1_materialhole_1_1", + "sample_id": null, + "children": [ + "jipian_5" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_5", + "name": "jipian_5", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_2", + "name": "liaopan1_materialhole_1_2", + "sample_id": null, + "children": [ + "jipian_6" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_6", + "name": "jipian_6", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_3", + "name": "liaopan1_materialhole_1_3", + "sample_id": null, + "children": [ + "jipian_7" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_7", + "name": "jipian_7", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_0", + "name": "liaopan1_materialhole_2_0", + "sample_id": null, + "children": [ + "jipian_8" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_8", + "name": "jipian_8", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_1", + "name": "liaopan1_materialhole_2_1", + "sample_id": null, + "children": [ + "jipian_9" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_9", + "name": "jipian_9", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_2", + "name": "liaopan1_materialhole_2_2", + "sample_id": null, + "children": [ + "jipian_10" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_10", + "name": "jipian_10", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_3", + "name": "liaopan1_materialhole_2_3", + "sample_id": null, + "children": [ + "jipian_11" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_11", + "name": "jipian_11", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_0", + "name": "liaopan1_materialhole_3_0", + "sample_id": null, + "children": [ + "jipian_12" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_12", + "name": "jipian_12", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_1", + "name": "liaopan1_materialhole_3_1", + "sample_id": null, + "children": [ + "jipian_13" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_13", + "name": "jipian_13", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_2", + "name": "liaopan1_materialhole_3_2", + "sample_id": null, + "children": [ + "jipian_14" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_14", + "name": "jipian_14", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_3", + "name": "liaopan1_materialhole_3_3", + "sample_id": null, + "children": [ + "jipian_15" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_15", + "name": "jipian_15", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2", + "name": "liaopan2", + "sample_id": null, + "children": [ + "liaopan2_materialhole_0_0", + "liaopan2_materialhole_0_1", + "liaopan2_materialhole_0_2", + "liaopan2_materialhole_0_3", + "liaopan2_materialhole_1_0", + "liaopan2_materialhole_1_1", + "liaopan2_materialhole_1_2", + "liaopan2_materialhole_1_3", + "liaopan2_materialhole_2_0", + "liaopan2_materialhole_2_1", + "liaopan2_materialhole_2_2", + "liaopan2_materialhole_2_3", + "liaopan2_materialhole_3_0", + "liaopan2_materialhole_3_1", + "liaopan2_materialhole_3_2", + "liaopan2_materialhole_3_3" + ], + "parent": "deck", + "type": "container", + "class": "", + "position": { + "x": 500, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan2_materialhole_0_0", + "B1": "liaopan2_materialhole_0_1", + "C1": "liaopan2_materialhole_0_2", + "D1": "liaopan2_materialhole_0_3", + "A2": "liaopan2_materialhole_1_0", + "B2": "liaopan2_materialhole_1_1", + "C2": "liaopan2_materialhole_1_2", + "D2": "liaopan2_materialhole_1_3", + "A3": "liaopan2_materialhole_2_0", + "B3": "liaopan2_materialhole_2_1", + "C3": "liaopan2_materialhole_2_2", + "D3": "liaopan2_materialhole_2_3", + "A4": "liaopan2_materialhole_3_0", + "B4": "liaopan2_materialhole_3_1", + "C4": "liaopan2_materialhole_3_2", + "D4": "liaopan2_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan2_materialhole_0_0", + "name": "liaopan2_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_1", + "name": "liaopan2_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_2", + "name": "liaopan2_materialhole_0_2", + "sample_id": null, + "children": [ + "jipian_2" + ], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_2", + "name": "jipian_2", + "sample_id": null, + "children": [], + "parent": "liaopan2_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_3", + "name": "liaopan2_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_0", + "name": "liaopan2_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_1", + "name": "liaopan2_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_2", + "name": "liaopan2_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_3", + "name": "liaopan2_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_0", + "name": "liaopan2_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_1", + "name": "liaopan2_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_2", + "name": "liaopan2_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_3", + "name": "liaopan2_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_0", + "name": "liaopan2_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_1", + "name": "liaopan2_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_2", + "name": "liaopan2_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_3", + "name": "liaopan2_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig2.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig2.json new file mode 100644 index 00000000..79baec06 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig2.json @@ -0,0 +1,1925 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "deck", + "name": "deck", + "sample_id": null, + "children": [ + "liaopan1", + "liaopan2" + ], + "parent": null, + "type": "deck", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "Deck", + "size_x": 1200, + "size_y": 800, + "size_z": 900, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "deck", + "barcode": null + }, + "data": {} + }, + { + "id": "liaopan1", + "name": "liaopan1", + "sample_id": null, + "children": [ + "liaopan1_materialhole_0_0", + "liaopan1_materialhole_0_1", + "liaopan1_materialhole_0_2", + "liaopan1_materialhole_0_3", + "liaopan1_materialhole_1_0", + "liaopan1_materialhole_1_1", + "liaopan1_materialhole_1_2", + "liaopan1_materialhole_1_3", + "liaopan1_materialhole_2_0", + "liaopan1_materialhole_2_1", + "liaopan1_materialhole_2_2", + "liaopan1_materialhole_2_3", + "liaopan1_materialhole_3_0", + "liaopan1_materialhole_3_1", + "liaopan1_materialhole_3_2", + "liaopan1_materialhole_3_3" + ], + "parent": "deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan1_materialhole_0_0", + "B1": "liaopan1_materialhole_0_1", + "C1": "liaopan1_materialhole_0_2", + "D1": "liaopan1_materialhole_0_3", + "A2": "liaopan1_materialhole_1_0", + "B2": "liaopan1_materialhole_1_1", + "C2": "liaopan1_materialhole_1_2", + "D2": "liaopan1_materialhole_1_3", + "A3": "liaopan1_materialhole_2_0", + "B3": "liaopan1_materialhole_2_1", + "C3": "liaopan1_materialhole_2_2", + "D3": "liaopan1_materialhole_2_3", + "A4": "liaopan1_materialhole_3_0", + "B4": "liaopan1_materialhole_3_1", + "C4": "liaopan1_materialhole_3_2", + "D4": "liaopan1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan1_materialhole_0_0", + "name": "liaopan1_materialhole_0_0", + "sample_id": null, + "children": [ + "jipian_0" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_0", + "name": "jipian_0", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_1", + "name": "liaopan1_materialhole_0_1", + "sample_id": null, + "children": [ + "jipian_1" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_1", + "name": "jipian_1", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_2", + "name": "liaopan1_materialhole_0_2", + "sample_id": null, + "children": [ + "jipian_2" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_2", + "name": "jipian_2", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_3", + "name": "liaopan1_materialhole_0_3", + "sample_id": null, + "children": [ + "jipian_3" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_3", + "name": "jipian_3", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_0", + "name": "liaopan1_materialhole_1_0", + "sample_id": null, + "children": [ + "jipian_4" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_4", + "name": "jipian_4", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_1", + "name": "liaopan1_materialhole_1_1", + "sample_id": null, + "children": [ + "jipian_5" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_5", + "name": "jipian_5", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_2", + "name": "liaopan1_materialhole_1_2", + "sample_id": null, + "children": [ + "jipian_6" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_6", + "name": "jipian_6", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_3", + "name": "liaopan1_materialhole_1_3", + "sample_id": null, + "children": [ + "jipian_7" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_7", + "name": "jipian_7", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_0", + "name": "liaopan1_materialhole_2_0", + "sample_id": null, + "children": [ + "jipian_8" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_8", + "name": "jipian_8", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_1", + "name": "liaopan1_materialhole_2_1", + "sample_id": null, + "children": [ + "jipian_9" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_9", + "name": "jipian_9", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_2", + "name": "liaopan1_materialhole_2_2", + "sample_id": null, + "children": [ + "jipian_10" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_10", + "name": "jipian_10", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_3", + "name": "liaopan1_materialhole_2_3", + "sample_id": null, + "children": [ + "jipian_11" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_11", + "name": "jipian_11", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_0", + "name": "liaopan1_materialhole_3_0", + "sample_id": null, + "children": [ + "jipian_12" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_12", + "name": "jipian_12", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_1", + "name": "liaopan1_materialhole_3_1", + "sample_id": null, + "children": [ + "jipian_13" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_13", + "name": "jipian_13", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_2", + "name": "liaopan1_materialhole_3_2", + "sample_id": null, + "children": [ + "jipian_14" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_14", + "name": "jipian_14", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_3", + "name": "liaopan1_materialhole_3_3", + "sample_id": null, + "children": [ + "jipian_15" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_15", + "name": "jipian_15", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2", + "name": "liaopan2", + "sample_id": null, + "children": [ + "liaopan2_materialhole_0_0", + "liaopan2_materialhole_0_1", + "liaopan2_materialhole_0_2", + "liaopan2_materialhole_0_3", + "liaopan2_materialhole_1_0", + "liaopan2_materialhole_1_1", + "liaopan2_materialhole_1_2", + "liaopan2_materialhole_1_3", + "liaopan2_materialhole_2_0", + "liaopan2_materialhole_2_1", + "liaopan2_materialhole_2_2", + "liaopan2_materialhole_2_3", + "liaopan2_materialhole_3_0", + "liaopan2_materialhole_3_1", + "liaopan2_materialhole_3_2", + "liaopan2_materialhole_3_3" + ], + "parent": "deck", + "type": "container", + "class": "", + "position": { + "x": 500, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan2_materialhole_0_0", + "B1": "liaopan2_materialhole_0_1", + "C1": "liaopan2_materialhole_0_2", + "D1": "liaopan2_materialhole_0_3", + "A2": "liaopan2_materialhole_1_0", + "B2": "liaopan2_materialhole_1_1", + "C2": "liaopan2_materialhole_1_2", + "D2": "liaopan2_materialhole_1_3", + "A3": "liaopan2_materialhole_2_0", + "B3": "liaopan2_materialhole_2_1", + "C3": "liaopan2_materialhole_2_2", + "D3": "liaopan2_materialhole_2_3", + "A4": "liaopan2_materialhole_3_0", + "B4": "liaopan2_materialhole_3_1", + "C4": "liaopan2_materialhole_3_2", + "D4": "liaopan2_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan2_materialhole_0_0", + "name": "liaopan2_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_1", + "name": "liaopan2_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_2", + "name": "liaopan2_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_3", + "name": "liaopan2_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_0", + "name": "liaopan2_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_1", + "name": "liaopan2_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_2", + "name": "liaopan2_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_3", + "name": "liaopan2_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_0", + "name": "liaopan2_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_1", + "name": "liaopan2_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_2", + "name": "liaopan2_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_3", + "name": "liaopan2_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_0", + "name": "liaopan2_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_1", + "name": "liaopan2_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_2", + "name": "liaopan2_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_3", + "name": "liaopan2_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3.json new file mode 100644 index 00000000..39e72a79 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3.json @@ -0,0 +1,1925 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "coin_cell_deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "liaopan1", + "liaopan2" + ], + "parent": null, + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1620.0, + "size_y": 1270.0, + "size_z": 500.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "liaopan1", + "name": "liaopan1", + "sample_id": null, + "children": [ + "liaopan1_materialhole_0_0", + "liaopan1_materialhole_0_1", + "liaopan1_materialhole_0_2", + "liaopan1_materialhole_0_3", + "liaopan1_materialhole_1_0", + "liaopan1_materialhole_1_1", + "liaopan1_materialhole_1_2", + "liaopan1_materialhole_1_3", + "liaopan1_materialhole_2_0", + "liaopan1_materialhole_2_1", + "liaopan1_materialhole_2_2", + "liaopan1_materialhole_2_3", + "liaopan1_materialhole_3_0", + "liaopan1_materialhole_3_1", + "liaopan1_materialhole_3_2", + "liaopan1_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan1_materialhole_0_0", + "B1": "liaopan1_materialhole_0_1", + "C1": "liaopan1_materialhole_0_2", + "D1": "liaopan1_materialhole_0_3", + "A2": "liaopan1_materialhole_1_0", + "B2": "liaopan1_materialhole_1_1", + "C2": "liaopan1_materialhole_1_2", + "D2": "liaopan1_materialhole_1_3", + "A3": "liaopan1_materialhole_2_0", + "B3": "liaopan1_materialhole_2_1", + "C3": "liaopan1_materialhole_2_2", + "D3": "liaopan1_materialhole_2_3", + "A4": "liaopan1_materialhole_3_0", + "B4": "liaopan1_materialhole_3_1", + "C4": "liaopan1_materialhole_3_2", + "D4": "liaopan1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan1_materialhole_0_0", + "name": "liaopan1_materialhole_0_0", + "sample_id": null, + "children": [ + "jipian_0" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_0", + "name": "jipian_0", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_1", + "name": "liaopan1_materialhole_0_1", + "sample_id": null, + "children": [ + "jipian_1" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_1", + "name": "jipian_1", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_2", + "name": "liaopan1_materialhole_0_2", + "sample_id": null, + "children": [ + "jipian_2" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_2", + "name": "jipian_2", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_3", + "name": "liaopan1_materialhole_0_3", + "sample_id": null, + "children": [ + "jipian_3" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_3", + "name": "jipian_3", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_0", + "name": "liaopan1_materialhole_1_0", + "sample_id": null, + "children": [ + "jipian_4" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_4", + "name": "jipian_4", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_1", + "name": "liaopan1_materialhole_1_1", + "sample_id": null, + "children": [ + "jipian_5" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_5", + "name": "jipian_5", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_2", + "name": "liaopan1_materialhole_1_2", + "sample_id": null, + "children": [ + "jipian_6" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_6", + "name": "jipian_6", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_3", + "name": "liaopan1_materialhole_1_3", + "sample_id": null, + "children": [ + "jipian_7" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_7", + "name": "jipian_7", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_0", + "name": "liaopan1_materialhole_2_0", + "sample_id": null, + "children": [ + "jipian_8" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_8", + "name": "jipian_8", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_1", + "name": "liaopan1_materialhole_2_1", + "sample_id": null, + "children": [ + "jipian_9" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_9", + "name": "jipian_9", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_2", + "name": "liaopan1_materialhole_2_2", + "sample_id": null, + "children": [ + "jipian_10" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_10", + "name": "jipian_10", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_3", + "name": "liaopan1_materialhole_2_3", + "sample_id": null, + "children": [ + "jipian_11" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_11", + "name": "jipian_11", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_0", + "name": "liaopan1_materialhole_3_0", + "sample_id": null, + "children": [ + "jipian_12" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_12", + "name": "jipian_12", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_1", + "name": "liaopan1_materialhole_3_1", + "sample_id": null, + "children": [ + "jipian_13" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_13", + "name": "jipian_13", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_2", + "name": "liaopan1_materialhole_3_2", + "sample_id": null, + "children": [ + "jipian_14" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_14", + "name": "jipian_14", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_3", + "name": "liaopan1_materialhole_3_3", + "sample_id": null, + "children": [ + "jipian_15" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_15", + "name": "jipian_15", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2", + "name": "liaopan2", + "sample_id": null, + "children": [ + "liaopan2_materialhole_0_0", + "liaopan2_materialhole_0_1", + "liaopan2_materialhole_0_2", + "liaopan2_materialhole_0_3", + "liaopan2_materialhole_1_0", + "liaopan2_materialhole_1_1", + "liaopan2_materialhole_1_2", + "liaopan2_materialhole_1_3", + "liaopan2_materialhole_2_0", + "liaopan2_materialhole_2_1", + "liaopan2_materialhole_2_2", + "liaopan2_materialhole_2_3", + "liaopan2_materialhole_3_0", + "liaopan2_materialhole_3_1", + "liaopan2_materialhole_3_2", + "liaopan2_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 500, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan2_materialhole_0_0", + "B1": "liaopan2_materialhole_0_1", + "C1": "liaopan2_materialhole_0_2", + "D1": "liaopan2_materialhole_0_3", + "A2": "liaopan2_materialhole_1_0", + "B2": "liaopan2_materialhole_1_1", + "C2": "liaopan2_materialhole_1_2", + "D2": "liaopan2_materialhole_1_3", + "A3": "liaopan2_materialhole_2_0", + "B3": "liaopan2_materialhole_2_1", + "C3": "liaopan2_materialhole_2_2", + "D3": "liaopan2_materialhole_2_3", + "A4": "liaopan2_materialhole_3_0", + "B4": "liaopan2_materialhole_3_1", + "C4": "liaopan2_materialhole_3_2", + "D4": "liaopan2_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan2_materialhole_0_0", + "name": "liaopan2_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_1", + "name": "liaopan2_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_2", + "name": "liaopan2_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_3", + "name": "liaopan2_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_0", + "name": "liaopan2_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_1", + "name": "liaopan2_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_2", + "name": "liaopan2_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_3", + "name": "liaopan2_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_0", + "name": "liaopan2_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_1", + "name": "liaopan2_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_2", + "name": "liaopan2_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_3", + "name": "liaopan2_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_0", + "name": "liaopan2_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_1", + "name": "liaopan2_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_2", + "name": "liaopan2_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_3", + "name": "liaopan2_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3a.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3a.json new file mode 100644 index 00000000..3a42ddab --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3a.json @@ -0,0 +1,1956 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "coin_cell_deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "liaopan1", + "liaopan2", + "battery1" + ], + "parent": null, + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1200, + "size_y": 800, + "size_z": 900, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "liaopan1", + "name": "liaopan1", + "sample_id": null, + "children": [ + "liaopan1_materialhole_0_0", + "liaopan1_materialhole_0_1", + "liaopan1_materialhole_0_2", + "liaopan1_materialhole_0_3", + "liaopan1_materialhole_1_0", + "liaopan1_materialhole_1_1", + "liaopan1_materialhole_1_2", + "liaopan1_materialhole_1_3", + "liaopan1_materialhole_2_0", + "liaopan1_materialhole_2_1", + "liaopan1_materialhole_2_2", + "liaopan1_materialhole_2_3", + "liaopan1_materialhole_3_0", + "liaopan1_materialhole_3_1", + "liaopan1_materialhole_3_2", + "liaopan1_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan1_materialhole_0_0", + "B1": "liaopan1_materialhole_0_1", + "C1": "liaopan1_materialhole_0_2", + "D1": "liaopan1_materialhole_0_3", + "A2": "liaopan1_materialhole_1_0", + "B2": "liaopan1_materialhole_1_1", + "C2": "liaopan1_materialhole_1_2", + "D2": "liaopan1_materialhole_1_3", + "A3": "liaopan1_materialhole_2_0", + "B3": "liaopan1_materialhole_2_1", + "C3": "liaopan1_materialhole_2_2", + "D3": "liaopan1_materialhole_2_3", + "A4": "liaopan1_materialhole_3_0", + "B4": "liaopan1_materialhole_3_1", + "C4": "liaopan1_materialhole_3_2", + "D4": "liaopan1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan1_materialhole_0_0", + "name": "liaopan1_materialhole_0_0", + "sample_id": null, + "children": [ + "jipian_0" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_0", + "name": "jipian_0", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_1", + "name": "liaopan1_materialhole_0_1", + "sample_id": null, + "children": [ + "jipian_1" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_1", + "name": "jipian_1", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_2", + "name": "liaopan1_materialhole_0_2", + "sample_id": null, + "children": [ + "jipian_2" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_2", + "name": "jipian_2", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_3", + "name": "liaopan1_materialhole_0_3", + "sample_id": null, + "children": [ + "jipian_3" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_3", + "name": "jipian_3", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_0", + "name": "liaopan1_materialhole_1_0", + "sample_id": null, + "children": [ + "jipian_4" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_4", + "name": "jipian_4", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_1", + "name": "liaopan1_materialhole_1_1", + "sample_id": null, + "children": [ + "jipian_5" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_5", + "name": "jipian_5", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_2", + "name": "liaopan1_materialhole_1_2", + "sample_id": null, + "children": [ + "jipian_6" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_6", + "name": "jipian_6", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_3", + "name": "liaopan1_materialhole_1_3", + "sample_id": null, + "children": [ + "jipian_7" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_7", + "name": "jipian_7", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_0", + "name": "liaopan1_materialhole_2_0", + "sample_id": null, + "children": [ + "jipian_8" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_8", + "name": "jipian_8", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_1", + "name": "liaopan1_materialhole_2_1", + "sample_id": null, + "children": [ + "jipian_9" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_9", + "name": "jipian_9", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_2", + "name": "liaopan1_materialhole_2_2", + "sample_id": null, + "children": [ + "jipian_10" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_10", + "name": "jipian_10", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_3", + "name": "liaopan1_materialhole_2_3", + "sample_id": null, + "children": [ + "jipian_11" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_11", + "name": "jipian_11", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_0", + "name": "liaopan1_materialhole_3_0", + "sample_id": null, + "children": [ + "jipian_12" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_12", + "name": "jipian_12", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_1", + "name": "liaopan1_materialhole_3_1", + "sample_id": null, + "children": [ + "jipian_13" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_13", + "name": "jipian_13", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_2", + "name": "liaopan1_materialhole_3_2", + "sample_id": null, + "children": [ + "jipian_14" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_14", + "name": "jipian_14", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_3", + "name": "liaopan1_materialhole_3_3", + "sample_id": null, + "children": [ + "jipian_15" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian_15", + "name": "jipian_15", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2", + "name": "liaopan2", + "sample_id": null, + "children": [ + "liaopan2_materialhole_0_0", + "liaopan2_materialhole_0_1", + "liaopan2_materialhole_0_2", + "liaopan2_materialhole_0_3", + "liaopan2_materialhole_1_0", + "liaopan2_materialhole_1_1", + "liaopan2_materialhole_1_2", + "liaopan2_materialhole_1_3", + "liaopan2_materialhole_2_0", + "liaopan2_materialhole_2_1", + "liaopan2_materialhole_2_2", + "liaopan2_materialhole_2_3", + "liaopan2_materialhole_3_0", + "liaopan2_materialhole_3_1", + "liaopan2_materialhole_3_2", + "liaopan2_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 500, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan2_materialhole_0_0", + "B1": "liaopan2_materialhole_0_1", + "C1": "liaopan2_materialhole_0_2", + "D1": "liaopan2_materialhole_0_3", + "A2": "liaopan2_materialhole_1_0", + "B2": "liaopan2_materialhole_1_1", + "C2": "liaopan2_materialhole_1_2", + "D2": "liaopan2_materialhole_1_3", + "A3": "liaopan2_materialhole_2_0", + "B3": "liaopan2_materialhole_2_1", + "C3": "liaopan2_materialhole_2_2", + "D3": "liaopan2_materialhole_2_3", + "A4": "liaopan2_materialhole_3_0", + "B4": "liaopan2_materialhole_3_1", + "C4": "liaopan2_materialhole_3_2", + "D4": "liaopan2_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan2_materialhole_0_0", + "name": "liaopan2_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_1", + "name": "liaopan2_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_2", + "name": "liaopan2_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_3", + "name": "liaopan2_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_0", + "name": "liaopan2_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_1", + "name": "liaopan2_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_2", + "name": "liaopan2_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_3", + "name": "liaopan2_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_0", + "name": "liaopan2_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_1", + "name": "liaopan2_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_2", + "name": "liaopan2_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_3", + "name": "liaopan2_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_0", + "name": "liaopan2_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_1", + "name": "liaopan2_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_2", + "name": "liaopan2_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_3", + "name": "liaopan2_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "battery1", + "name": "battery1", + "sample_id": null, + "children": [], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 600, + "y": 600, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 30, + "size_y": 30, + "size_z": 30, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "battery", + "model": null, + "barcode": null + }, + "data": {} + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3b.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3b.json new file mode 100644 index 00000000..78a60810 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3b.json @@ -0,0 +1,2589 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "coin_cell_deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "liaopan1", + "liaopan2", + "\u7535\u6c60\u6599\u76d8" + ], + "parent": null, + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1200, + "size_y": 800, + "size_z": 900, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "liaopan1", + "name": "liaopan1", + "sample_id": null, + "children": [ + "liaopan1_materialhole_0_0", + "liaopan1_materialhole_0_1", + "liaopan1_materialhole_0_2", + "liaopan1_materialhole_0_3", + "liaopan1_materialhole_1_0", + "liaopan1_materialhole_1_1", + "liaopan1_materialhole_1_2", + "liaopan1_materialhole_1_3", + "liaopan1_materialhole_2_0", + "liaopan1_materialhole_2_1", + "liaopan1_materialhole_2_2", + "liaopan1_materialhole_2_3", + "liaopan1_materialhole_3_0", + "liaopan1_materialhole_3_1", + "liaopan1_materialhole_3_2", + "liaopan1_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan1_materialhole_0_0", + "B1": "liaopan1_materialhole_0_1", + "C1": "liaopan1_materialhole_0_2", + "D1": "liaopan1_materialhole_0_3", + "A2": "liaopan1_materialhole_1_0", + "B2": "liaopan1_materialhole_1_1", + "C2": "liaopan1_materialhole_1_2", + "D2": "liaopan1_materialhole_1_3", + "A3": "liaopan1_materialhole_2_0", + "B3": "liaopan1_materialhole_2_1", + "C3": "liaopan1_materialhole_2_2", + "D3": "liaopan1_materialhole_2_3", + "A4": "liaopan1_materialhole_3_0", + "B4": "liaopan1_materialhole_3_1", + "C4": "liaopan1_materialhole_3_2", + "D4": "liaopan1_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan1_materialhole_0_0", + "name": "liaopan1_materialhole_0_0", + "sample_id": null, + "children": [ + "jipian1_0" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_0", + "name": "jipian1_0", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_1", + "name": "liaopan1_materialhole_0_1", + "sample_id": null, + "children": [ + "jipian1_1" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_1", + "name": "jipian1_1", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_2", + "name": "liaopan1_materialhole_0_2", + "sample_id": null, + "children": [ + "jipian1_2" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_2", + "name": "jipian1_2", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_0_3", + "name": "liaopan1_materialhole_0_3", + "sample_id": null, + "children": [ + "jipian1_3" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_3", + "name": "jipian1_3", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_0_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_0", + "name": "liaopan1_materialhole_1_0", + "sample_id": null, + "children": [ + "jipian1_4" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_4", + "name": "jipian1_4", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_1", + "name": "liaopan1_materialhole_1_1", + "sample_id": null, + "children": [ + "jipian1_5" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_5", + "name": "jipian1_5", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_2", + "name": "liaopan1_materialhole_1_2", + "sample_id": null, + "children": [ + "jipian1_6" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_6", + "name": "jipian1_6", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_1_3", + "name": "liaopan1_materialhole_1_3", + "sample_id": null, + "children": [ + "jipian1_7" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_7", + "name": "jipian1_7", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_1_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_0", + "name": "liaopan1_materialhole_2_0", + "sample_id": null, + "children": [ + "jipian1_8" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_8", + "name": "jipian1_8", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_1", + "name": "liaopan1_materialhole_2_1", + "sample_id": null, + "children": [ + "jipian1_9" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_9", + "name": "jipian1_9", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_2", + "name": "liaopan1_materialhole_2_2", + "sample_id": null, + "children": [ + "jipian1_10" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_10", + "name": "jipian1_10", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_2_3", + "name": "liaopan1_materialhole_2_3", + "sample_id": null, + "children": [ + "jipian1_11" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_11", + "name": "jipian1_11", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_2_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_0", + "name": "liaopan1_materialhole_3_0", + "sample_id": null, + "children": [ + "jipian1_12" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_12", + "name": "jipian1_12", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_1", + "name": "liaopan1_materialhole_3_1", + "sample_id": null, + "children": [ + "jipian1_13" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_13", + "name": "jipian1_13", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_1", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_2", + "name": "liaopan1_materialhole_3_2", + "sample_id": null, + "children": [ + "jipian1_14" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_14", + "name": "jipian1_14", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_2", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan1_materialhole_3_3", + "name": "liaopan1_materialhole_3_3", + "sample_id": null, + "children": [ + "jipian1_15" + ], + "parent": "liaopan1", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "jipian1_15", + "name": "jipian1_15", + "sample_id": null, + "children": [], + "parent": "liaopan1_materialhole_3_3", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 12, + "size_y": 12, + "size_z": 0.1, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 14, + "thickness": 0.1, + "mass": 0.5, + "material_type": "copper", + "info": null + } + }, + { + "id": "liaopan2", + "name": "liaopan2", + "sample_id": null, + "children": [ + "liaopan2_materialhole_0_0", + "liaopan2_materialhole_0_1", + "liaopan2_materialhole_0_2", + "liaopan2_materialhole_0_3", + "liaopan2_materialhole_1_0", + "liaopan2_materialhole_1_1", + "liaopan2_materialhole_1_2", + "liaopan2_materialhole_1_3", + "liaopan2_materialhole_2_0", + "liaopan2_materialhole_2_1", + "liaopan2_materialhole_2_2", + "liaopan2_materialhole_2_3", + "liaopan2_materialhole_3_0", + "liaopan2_materialhole_3_1", + "liaopan2_materialhole_3_2", + "liaopan2_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 500, + "y": 0, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 120.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "liaopan2_materialhole_0_0", + "B1": "liaopan2_materialhole_0_1", + "C1": "liaopan2_materialhole_0_2", + "D1": "liaopan2_materialhole_0_3", + "A2": "liaopan2_materialhole_1_0", + "B2": "liaopan2_materialhole_1_1", + "C2": "liaopan2_materialhole_1_2", + "D2": "liaopan2_materialhole_1_3", + "A3": "liaopan2_materialhole_2_0", + "B3": "liaopan2_materialhole_2_1", + "C3": "liaopan2_materialhole_2_2", + "D3": "liaopan2_materialhole_2_3", + "A4": "liaopan2_materialhole_3_0", + "B4": "liaopan2_materialhole_3_1", + "C4": "liaopan2_materialhole_3_2", + "D4": "liaopan2_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "liaopan2_materialhole_0_0", + "name": "liaopan2_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_1", + "name": "liaopan2_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_2", + "name": "liaopan2_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_0_3", + "name": "liaopan2_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_0", + "name": "liaopan2_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_1", + "name": "liaopan2_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_2", + "name": "liaopan2_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_1_3", + "name": "liaopan2_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_0", + "name": "liaopan2_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_1", + "name": "liaopan2_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_2", + "name": "liaopan2_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_2_3", + "name": "liaopan2_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_0", + "name": "liaopan2_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 84.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_1", + "name": "liaopan2_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 60.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_2", + "name": "liaopan2_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 36.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "liaopan2_materialhole_3_3", + "name": "liaopan2_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "liaopan2", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 12.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8", + "name": "\u7535\u6c60\u6599\u76d8", + "sample_id": null, + "children": [ + "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "\u7535\u6c60\u6599\u76d8_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 300, + "y": 300, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 150.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "B1": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "C1": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "D1": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "A2": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "B2": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "C2": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "D2": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "A3": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "B3": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "C3": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "D3": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "A4": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "B4": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "C4": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "D4": "\u7535\u6c60\u6599\u76d8_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "sample_id": null, + "children": [ + "battery1" + ], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 99.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "battery1", + "name": "battery1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "ElectrodeSheet", + "size_x": 16, + "size_y": 16, + "size_z": 2, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "electrode_sheet", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20.0, + "height": 20.0, + "assembly_pressure": 20.0, + "electrolyte_volume": 20.0, + "electrolyte_name": "DP001" + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 75.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 51.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 27.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 99.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 75.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 51.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 27.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 99.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 75.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 51.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 27.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 99.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 75.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 51.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 27.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json new file mode 100644 index 00000000..630faa58 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json @@ -0,0 +1,691 @@ +{ + "nodes": [ + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "bettery_station_registry", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", + "protocol_type": [], + "station_resource": { + "data": { + "_resource_child_name": "coin_cell_deck", + "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" + } + }, + + "address": "192.168.1.20", + "port": 502 + }, + "data": {} + }, + { + "id": "coin_cell_deck", + "name": "coin_cell_deck", + "sample_id": null, + "children": [ + "\u7535\u6c60\u6599\u76d8" + ], + "parent": null, + "type": "container", + "class": "", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "type": "CoincellDeck", + "size_x": 1000, + "size_y": 1000, + "size_z": 900, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "coin_cell_deck", + "barcode": null + }, + "data": {} + }, + { + "id": "\u7535\u6c60\u6599\u76d8", + "name": "\u7535\u6c60\u6599\u76d8", + "sample_id": null, + "children": [ + "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "\u7535\u6c60\u6599\u76d8_materialhole_3_3" + ], + "parent": "coin_cell_deck", + "type": "container", + "class": "", + "position": { + "x": 100, + "y": 100, + "z": 0 + }, + "config": { + "type": "MaterialPlate", + "size_x": 120.8, + "size_y": 160.5, + "size_z": 10.0, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_plate", + "model": null, + "barcode": null, + "ordering": { + "A1": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "B1": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "C1": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "D1": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "A2": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "B2": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "C2": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "D2": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "A3": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "B3": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "C3": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "D3": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "A4": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "B4": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "C4": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "D4": "\u7535\u6c60\u6599\u76d8_materialhole_3_3" + } + }, + "data": {} + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_0_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 12.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_1_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 36.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_2_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 60.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_0", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 104.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_1", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 80.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_2", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 56.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + }, + { + "id": "\u7535\u6c60\u6599\u76d8_materialhole_3_3", + "name": "\u7535\u6c60\u6599\u76d8_materialhole_3_3", + "sample_id": null, + "children": [], + "parent": "\u7535\u6c60\u6599\u76d8", + "type": "container", + "class": "", + "position": { + "x": 84.4, + "y": 32.25, + "z": 10.0 + }, + "config": { + "type": "MaterialHole", + "size_x": 16, + "size_y": 16, + "size_z": 16, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + }, + "category": "material_hole", + "model": null, + "barcode": null + }, + "data": { + "diameter": 20, + "depth": 10, + "max_sheets": 1, + "info": null + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig4.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig4.json index 7e371327..0ba79b72 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig4.json +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig4.json @@ -16,9 +16,9 @@ }, "config": { "debug_mode": false, - "_comment": "protocol_type接外部工站固定写法字段,一般为空,deck写法也固定", + "_comment": "protocol_type接外部工站固定写法字段,一般为空,station_resource写法也固定", "protocol_type": [], - "deck": { + "station_resource": { "data": { "_resource_child_name": "coin_cell_deck", "_resource_type": "unilabos.devices.workstation.coin_cell_assembly.button_battery_station:CoincellDeck" diff --git a/unilabos/devices/workstation/coin_cell_assembly/work_station.yaml b/unilabos/devices/workstation/coin_cell_assembly/work_station.yaml new file mode 100644 index 00000000..29e84374 --- /dev/null +++ b/unilabos/devices/workstation/coin_cell_assembly/work_station.yaml @@ -0,0 +1,6674 @@ +bettery_station_registry: + category: + - work_station + class: + action_value_mappings: + auto-change_hole_sheet_to_2: + feedback: {} + goal: {} + goal_default: + hole: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + hole: + type: object + required: + - hole + type: object + result: {} + required: + - goal + title: change_hole_sheet_to_2参数 + type: object + type: UniLabJsonCommandAsync + auto-fill_plate: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: fill_plate参数 + type: object + type: UniLabJsonCommandAsync + auto-fun_wuliao_test: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: fun_wuliao_test参数 + type: object + type: UniLabJsonCommand + auto-func_allpack_cmd: + feedback: {} + goal: {} + goal_default: + elec_num: null + elec_use_num: null + file_path: D:\coin_cell_data + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + elec_num: + type: string + elec_use_num: + type: string + file_path: + default: D:\coin_cell_data + type: string + required: + - elec_num + - elec_use_num + type: object + result: {} + required: + - goal + title: func_allpack_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_get_csv_export_status: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_get_csv_export_status参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_auto: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_auto参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_init: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_init参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_start: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_start参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_stop: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_stop参数 + type: object + type: UniLabJsonCommand + auto-func_pack_get_msg_cmd: + feedback: {} + goal: {} + goal_default: + file_path: D:\coin_cell_data + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + default: D:\coin_cell_data + type: string + required: [] + type: object + result: {} + required: + - goal + title: func_pack_get_msg_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_pack_send_bottle_num: + feedback: {} + goal: {} + goal_default: + bottle_num: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + bottle_num: + type: integer + required: + - bottle_num + type: object + result: {} + required: + - goal + title: func_pack_send_bottle_num参数 + type: object + type: UniLabJsonCommand + auto-func_pack_send_finished_cmd: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_send_finished_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_pack_send_msg_cmd: + feedback: {} + goal: {} + goal_default: + elec_use_num: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + elec_use_num: + type: string + required: + - elec_use_num + type: object + result: {} + required: + - goal + title: func_pack_send_msg_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_read_data_and_output: + feedback: {} + goal: {} + goal_default: + file_path: D:\coin_cell_data + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + default: D:\coin_cell_data + type: string + required: [] + type: object + result: {} + required: + - goal + title: func_read_data_and_output参数 + type: object + type: UniLabJsonCommand + auto-func_stop_read_data: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_stop_read_data参数 + type: object + type: UniLabJsonCommand + auto-modify_deck_name: + feedback: {} + goal: {} + goal_default: + resource_name: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + resource_name: + type: string + required: + - resource_name + type: object + result: {} + required: + - goal + title: modify_deck_name参数 + type: object + type: UniLabJsonCommand + auto-post_init: + feedback: {} + goal: {} + goal_default: + ros_node: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ros_node: + type: object + required: + - ros_node + type: object + result: {} + required: + - goal + title: post_init参数 + type: object + type: UniLabJsonCommand + module: unilabos.devices.workstation.coin_cell_assembly.coin_cell_assembly:CoinCellAssemblyWorkstation + status_types: + data_assembly_coin_cell_num: int + data_assembly_pressure: int + data_assembly_time: float + data_axis_x_pos: float + data_axis_y_pos: float + data_axis_z_pos: float + data_coin_cell_code: str + data_coin_num: int + data_electrolyte_code: str + data_electrolyte_volume: int + data_glove_box_o2_content: float + data_glove_box_pressure: float + data_glove_box_water_content: float + data_open_circuit_voltage: float + data_pole_weight: float + request_rec_msg_status: bool + request_send_msg_status: bool + sys_mode: str + sys_status: str + type: python + config_info: [] + description: '' + handles: [] + icon: '' + init_param_schema: + config: + properties: + address: + default: 192.168.1.20 + type: string + debug_mode: + default: true + type: boolean + port: + default: '502' + type: string + station_resource: + type: object + required: + - station_resource + type: object + data: + properties: + data_assembly_coin_cell_num: + type: integer + data_assembly_pressure: + type: integer + data_assembly_time: + type: number + data_axis_x_pos: + type: number + data_axis_y_pos: + type: number + data_axis_z_pos: + type: number + data_coin_cell_code: + type: string + data_coin_num: + type: integer + data_electrolyte_code: + type: string + data_electrolyte_volume: + type: integer + data_glove_box_o2_content: + type: number + data_glove_box_pressure: + type: number + data_glove_box_water_content: + type: number + data_open_circuit_voltage: + type: number + data_pole_weight: + type: number + request_rec_msg_status: + type: boolean + request_send_msg_status: + type: boolean + sys_mode: + type: string + sys_status: + type: string + required: + - sys_status + - sys_mode + - request_rec_msg_status + - request_send_msg_status + - data_assembly_coin_cell_num + - data_assembly_time + - data_open_circuit_voltage + - data_axis_x_pos + - data_axis_y_pos + - data_axis_z_pos + - data_pole_weight + - data_assembly_pressure + - data_electrolyte_volume + - data_coin_num + - data_coin_cell_code + - data_electrolyte_code + - data_glove_box_pressure + - data_glove_box_o2_content + - data_glove_box_water_content + type: object + version: 1.0.0 +workstation: + category: + - work_station + class: + action_value_mappings: + AGVTransferProtocol: + feedback: {} + goal: + from_repo: from_repo + from_repo_position: from_repo_position + to_repo: to_repo + to_repo_position: to_repo_position + goal_default: + from_repo: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + from_repo_position: '' + to_repo: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + to_repo_position: '' + handles: {} + result: {} + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: AGVTransfer_Feedback + type: object + goal: + properties: + from_repo: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: from_repo + type: object + from_repo_position: + type: string + to_repo: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: to_repo + type: object + to_repo_position: + type: string + required: + - from_repo + - from_repo_position + - to_repo + - to_repo_position + title: AGVTransfer_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: AGVTransfer_Result + type: object + required: + - goal + title: AGVTransfer + type: object + type: AGVTransfer + AddProtocol: + feedback: {} + goal: + amount: amount + equiv: equiv + event: event + mass: mass + mol: mol + purpose: purpose + rate_spec: rate_spec + ratio: ratio + reagent: reagent + stir: stir + stir_speed: stir_speed + time: time + vessel: vessel + viscous: viscous + volume: volume + goal_default: + amount: '' + equiv: '' + event: '' + mass: '' + mol: '' + purpose: '' + rate_spec: '' + ratio: '' + reagent: '' + stir: false + stir_speed: 0.0 + time: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + viscous: false + volume: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: reagent + data_source: handle + data_type: resource + handler_key: reagent + label: Reagent + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_status: + type: string + progress: + type: number + required: + - progress + - current_status + title: Add_Feedback + type: object + goal: + properties: + amount: + type: string + equiv: + type: string + event: + type: string + mass: + type: string + mol: + type: string + purpose: + type: string + rate_spec: + type: string + ratio: + type: string + reagent: + type: string + stir: + type: boolean + stir_speed: + type: number + time: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + viscous: + type: boolean + volume: + type: string + required: + - vessel + - reagent + - volume + - mass + - amount + - time + - stir + - stir_speed + - viscous + - purpose + - event + - mol + - rate_spec + - equiv + - ratio + title: Add_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Add_Result + type: object + required: + - goal + title: Add + type: object + type: Add + AdjustPHProtocol: + feedback: {} + goal: + ph_value: ph_value + reagent: reagent + settling_time: settling_time + stir: stir + stir_speed: stir_speed + stir_time: stir_time + vessel: vessel + volume: volume + goal_default: + ph_value: 0.0 + reagent: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: reagent + data_source: handle + data_type: resource + handler_key: reagent + label: Reagent + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: AdjustPH_Feedback + type: object + goal: + properties: + ph_value: + type: number + reagent: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - ph_value + - reagent + title: AdjustPH_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: AdjustPH_Result + type: object + required: + - goal + title: AdjustPH + type: object + type: AdjustPH + CentrifugeProtocol: + feedback: {} + goal: + speed: speed + temp: temp + time: time + vessel: vessel + goal_default: + speed: 0.0 + temp: 0.0 + time: 0.0 + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_speed: + type: number + current_status: + type: string + current_temp: + type: number + progress: + type: number + required: + - progress + - current_speed + - current_temp + - current_status + title: Centrifuge_Feedback + type: object + goal: + properties: + speed: + type: number + temp: + type: number + time: + type: number + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - speed + - time + - temp + title: Centrifuge_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Centrifuge_Result + type: object + required: + - goal + title: Centrifuge + type: object + type: Centrifuge + CleanProtocol: + feedback: {} + goal: + repeats: repeats + solvent: solvent + temp: temp + vessel: vessel + volume: volume + goal_default: + repeats: 0 + solvent: '' + temp: 0.0 + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: 0.0 + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Solvent + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_device: + type: string + status: + type: string + time_remaining: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_remaining + type: object + time_spent: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_spent + type: object + required: + - status + - current_device + - time_spent + - time_remaining + title: Clean_Feedback + type: object + goal: + properties: + repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + solvent: + type: string + temp: + type: number + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: number + required: + - vessel + - solvent + - volume + - temp + - repeats + title: Clean_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: Clean_Result + type: object + required: + - goal + title: Clean + type: object + type: Clean + CleanVesselProtocol: + feedback: {} + goal: + repeats: repeats + solvent: solvent + temp: temp + vessel: vessel + volume: volume + goal_default: + repeats: 0 + solvent: '' + temp: 0.0 + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: 0.0 + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Solvent + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: CleanVessel_Feedback + type: object + goal: + properties: + repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + solvent: + type: string + temp: + type: number + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: number + required: + - vessel + - solvent + - volume + - temp + - repeats + title: CleanVessel_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: CleanVessel_Result + type: object + required: + - goal + title: CleanVessel + type: object + type: CleanVessel + DissolveProtocol: + feedback: {} + goal: + amount: amount + event: event + mass: mass + mol: mol + reagent: reagent + solvent: solvent + stir_speed: stir_speed + temp: temp + time: time + vessel: vessel + volume: volume + goal_default: + amount: '' + event: '' + mass: '' + mol: '' + reagent: '' + solvent: '' + stir_speed: 0.0 + temp: '' + time: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Solvent + - data_key: reagent + data_source: handle + data_type: resource + handler_key: reagent + label: Reagent + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: Dissolve_Feedback + type: object + goal: + properties: + amount: + type: string + event: + type: string + mass: + type: string + mol: + type: string + reagent: + type: string + solvent: + type: string + stir_speed: + type: number + temp: + type: string + time: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: string + required: + - vessel + - solvent + - volume + - amount + - temp + - time + - stir_speed + - mass + - mol + - reagent + - event + title: Dissolve_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Dissolve_Result + type: object + required: + - goal + title: Dissolve + type: object + type: Dissolve + DryProtocol: + feedback: {} + goal: + compound: compound + vessel: vessel + goal_default: + compound: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: Dry_Feedback + type: object + goal: + properties: + compound: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - compound + - vessel + title: Dry_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Dry_Result + type: object + required: + - goal + title: Dry + type: object + type: Dry + EvacuateAndRefillProtocol: + feedback: {} + goal: + gas: gas + vessel: vessel + goal_default: + gas: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_device: + type: string + status: + type: string + time_remaining: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_remaining + type: object + time_spent: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_spent + type: object + required: + - status + - current_device + - time_spent + - time_remaining + title: EvacuateAndRefill_Feedback + type: object + goal: + properties: + gas: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - gas + title: EvacuateAndRefill_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: EvacuateAndRefill_Result + type: object + required: + - goal + title: EvacuateAndRefill + type: object + type: EvacuateAndRefill + EvaporateProtocol: + feedback: {} + goal: + pressure: pressure + solvent: solvent + stir_speed: stir_speed + temp: temp + time: time + vessel: vessel + goal_default: + pressure: 0.0 + solvent: '' + stir_speed: 0.0 + temp: 0.0 + time: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Evaporation Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Eluting Solvent + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: VesselOut + label: Evaporation Vessel + placeholder_keys: + vessel: unilabos_nodes + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_device: + type: string + status: + type: string + time_remaining: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_remaining + type: object + time_spent: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_spent + type: object + required: + - status + - current_device + - time_spent + - time_remaining + title: Evaporate_Feedback + type: object + goal: + properties: + pressure: + type: number + solvent: + type: string + stir_speed: + type: number + temp: + type: number + time: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - pressure + - temp + - time + - stir_speed + - solvent + title: Evaporate_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: Evaporate_Result + type: object + required: + - goal + title: Evaporate + type: object + type: Evaporate + FilterProtocol: + feedback: {} + goal: + continue_heatchill: continue_heatchill + filtrate_vessel: filtrate_vessel + stir: stir + stir_speed: stir_speed + temp: temp + vessel: vessel + volume: volume + goal_default: + continue_heatchill: false + filtrate_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + stir: false + stir_speed: 0.0 + temp: 0.0 + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: 0.0 + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: filtrate_vessel + data_source: handle + data_type: resource + handler_key: FiltrateVessel + label: Filtrate Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + - data_key: filtrate_vessel + data_source: executor + data_type: resource + handler_key: FiltrateOut + label: Filtrate Vessel + placeholder_keys: + filtrate_vessel: unilabos_resources + vessel: unilabos_nodes + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_status: + type: string + current_temp: + type: number + filtered_volume: + type: number + progress: + type: number + required: + - progress + - current_temp + - filtered_volume + - current_status + title: Filter_Feedback + type: object + goal: + properties: + continue_heatchill: + type: boolean + filtrate_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: filtrate_vessel + type: object + stir: + type: boolean + stir_speed: + type: number + temp: + type: number + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: number + required: + - vessel + - filtrate_vessel + - stir + - stir_speed + - temp + - continue_heatchill + - volume + title: Filter_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Filter_Result + type: object + required: + - goal + title: Filter + type: object + type: Filter + FilterThroughProtocol: + feedback: {} + goal: + eluting_repeats: eluting_repeats + eluting_solvent: eluting_solvent + eluting_volume: eluting_volume + filter_through: filter_through + from_vessel: from_vessel + residence_time: residence_time + to_vessel: to_vessel + goal_default: + eluting_repeats: 0 + eluting_solvent: '' + eluting_volume: 0.0 + filter_through: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + from_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + residence_time: 0.0 + to_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVessel + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVessel + label: To Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Eluting Solvent + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVesselOut + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVesselOut + label: To Vessel + placeholder_keys: + from_vessel: unilabos_resources + to_vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: FilterThrough_Feedback + type: object + goal: + properties: + eluting_repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + eluting_solvent: + type: string + eluting_volume: + type: number + filter_through: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: filter_through + type: object + from_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: from_vessel + type: object + residence_time: + type: number + to_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: to_vessel + type: object + required: + - from_vessel + - to_vessel + - filter_through + - eluting_solvent + - eluting_volume + - eluting_repeats + - residence_time + title: FilterThrough_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: FilterThrough_Result + type: object + required: + - goal + title: FilterThrough + type: object + type: FilterThrough + HeatChillProtocol: + feedback: {} + goal: + pressure: pressure + purpose: purpose + reflux_solvent: reflux_solvent + stir: stir + stir_speed: stir_speed + temp: temp + temp_spec: temp_spec + time: time + time_spec: time_spec + vessel: vessel + goal_default: + pressure: '' + purpose: '' + reflux_solvent: '' + stir: false + stir_speed: 0.0 + temp: 0.0 + temp_spec: '' + time: '' + time_spec: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: HeatChill_Feedback + type: object + goal: + properties: + pressure: + type: string + purpose: + type: string + reflux_solvent: + type: string + stir: + type: boolean + stir_speed: + type: number + temp: + type: number + temp_spec: + type: string + time: + type: string + time_spec: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - temp + - time + - temp_spec + - time_spec + - pressure + - reflux_solvent + - stir + - stir_speed + - purpose + title: HeatChill_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: HeatChill_Result + type: object + required: + - goal + title: HeatChill + type: object + type: HeatChill + HeatChillStartProtocol: + feedback: {} + goal: + purpose: purpose + temp: temp + vessel: vessel + goal_default: + purpose: '' + temp: 0.0 + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: HeatChillStart_Feedback + type: object + goal: + properties: + purpose: + type: string + temp: + type: number + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - temp + - purpose + title: HeatChillStart_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: HeatChillStart_Result + type: object + required: + - goal + title: HeatChillStart + type: object + type: HeatChillStart + HeatChillStopProtocol: + feedback: {} + goal: + vessel: vessel + goal_default: + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: HeatChillStop_Feedback + type: object + goal: + properties: + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + title: HeatChillStop_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: HeatChillStop_Result + type: object + required: + - goal + title: HeatChillStop + type: object + type: HeatChillStop + HydrogenateProtocol: + feedback: {} + goal: + temp: temp + time: time + vessel: vessel + goal_default: + temp: '' + time: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: Hydrogenate_Feedback + type: object + goal: + properties: + temp: + type: string + time: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - temp + - time + - vessel + title: Hydrogenate_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Hydrogenate_Result + type: object + required: + - goal + title: Hydrogenate + type: object + type: Hydrogenate + PumpTransferProtocol: + feedback: {} + goal: + amount: amount + event: event + flowrate: flowrate + from_vessel: from_vessel + rate_spec: rate_spec + rinsing_repeats: rinsing_repeats + rinsing_solvent: rinsing_solvent + rinsing_volume: rinsing_volume + solid: solid + through: through + time: time + to_vessel: to_vessel + transfer_flowrate: transfer_flowrate + viscous: viscous + volume: volume + goal_default: + amount: '' + event: '' + flowrate: 0.0 + from_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + rate_spec: '' + rinsing_repeats: 0 + rinsing_solvent: '' + rinsing_volume: 0.0 + solid: false + through: '' + time: 0.0 + to_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + transfer_flowrate: 0.0 + viscous: false + volume: 0.0 + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVessel + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVessel + label: To Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Rinsing Solvent + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVesselOut + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVesselOut + label: To Vessel + placeholder_keys: + from_vessel: unilabos_nodes + to_vessel: unilabos_nodes + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_device: + type: string + status: + type: string + time_remaining: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_remaining + type: object + time_spent: + properties: + nanosec: + maximum: 4294967295 + minimum: 0 + type: integer + sec: + maximum: 2147483647 + minimum: -2147483648 + type: integer + required: + - sec + - nanosec + title: time_spent + type: object + required: + - status + - current_device + - time_spent + - time_remaining + title: PumpTransfer_Feedback + type: object + goal: + properties: + amount: + type: string + event: + type: string + flowrate: + type: number + from_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: from_vessel + type: object + rate_spec: + type: string + rinsing_repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + rinsing_solvent: + type: string + rinsing_volume: + type: number + solid: + type: boolean + through: + type: string + time: + type: number + to_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: to_vessel + type: object + transfer_flowrate: + type: number + viscous: + type: boolean + volume: + type: number + required: + - from_vessel + - to_vessel + - volume + - amount + - time + - viscous + - rinsing_solvent + - rinsing_volume + - rinsing_repeats + - solid + - flowrate + - transfer_flowrate + - rate_spec + - event + - through + title: PumpTransfer_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: PumpTransfer_Result + type: object + required: + - goal + title: PumpTransfer + type: object + type: PumpTransfer + RecrystallizeProtocol: + feedback: {} + goal: + ratio: ratio + solvent1: solvent1 + solvent2: solvent2 + vessel: vessel + volume: volume + goal_default: + ratio: '' + solvent1: '' + solvent2: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: solvent1 + data_source: handle + data_type: resource + handler_key: solvent1 + label: Solvent 1 + - data_key: solvent2 + data_source: handle + data_type: resource + handler_key: solvent2 + label: Solvent 2 + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: Recrystallize_Feedback + type: object + goal: + properties: + ratio: + type: string + solvent1: + type: string + solvent2: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: string + required: + - ratio + - solvent1 + - solvent2 + - vessel + - volume + title: Recrystallize_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Recrystallize_Result + type: object + required: + - goal + title: Recrystallize + type: object + type: Recrystallize + ResetHandlingProtocol: + feedback: {} + goal: + solvent: solvent + goal_default: + solvent: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Solvent + output: [] + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: ResetHandling_Feedback + type: object + goal: + properties: + solvent: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - solvent + - vessel + title: ResetHandling_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: ResetHandling_Result + type: object + required: + - goal + title: ResetHandling + type: object + type: ResetHandling + RunColumnProtocol: + feedback: {} + goal: + column: column + from_vessel: from_vessel + to_vessel: to_vessel + goal_default: + column: '' + from_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + pct1: '' + pct2: '' + ratio: '' + rf: '' + solvent1: '' + solvent2: '' + to_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVessel + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVessel + label: To Vessel + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVesselOut + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVesselOut + label: To Vessel + placeholder_keys: + column: unilabos_devices + from_vessel: unilabos_resources + to_vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: RunColumn_Feedback + type: object + goal: + properties: + column: + type: string + from_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: from_vessel + type: object + pct1: + type: string + pct2: + type: string + ratio: + type: string + rf: + type: string + solvent1: + type: string + solvent2: + type: string + to_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: to_vessel + type: object + required: + - from_vessel + - to_vessel + - column + - rf + - pct1 + - pct2 + - solvent1 + - solvent2 + - ratio + title: RunColumn_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: RunColumn_Result + type: object + required: + - goal + title: RunColumn + type: object + type: RunColumn + SeparateProtocol: + feedback: {} + goal: + from_vessel: from_vessel + product_phase: product_phase + purpose: purpose + repeats: repeats + separation_vessel: separation_vessel + settling_time: settling_time + solvent: solvent + solvent_volume: solvent_volume + stir_speed: stir_speed + stir_time: stir_time + through: through + to_vessel: to_vessel + waste_phase_to_vessel: waste_phase_to_vessel + goal_default: + from_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + product_phase: '' + product_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + purpose: '' + repeats: 0 + separation_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + settling_time: 0.0 + solvent: '' + solvent_volume: '' + stir_speed: 0.0 + stir_time: 0.0 + through: '' + to_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: '' + waste_phase_to_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + waste_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVessel + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVessel + label: To Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Solvent + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVesselOut + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVesselOut + label: To Vessel + placeholder_keys: + from_vessel: unilabos_resources + to_vessel: unilabos_resources + waste_phase_to_vessel: unilabos_resources + waste_vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: Separate_Feedback + type: object + goal: + properties: + from_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: from_vessel + type: object + product_phase: + type: string + product_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: product_vessel + type: object + purpose: + type: string + repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + separation_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: separation_vessel + type: object + settling_time: + type: number + solvent: + type: string + solvent_volume: + type: string + stir_speed: + type: number + stir_time: + type: number + through: + type: string + to_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: to_vessel + type: object + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: string + waste_phase_to_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: waste_phase_to_vessel + type: object + waste_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: waste_vessel + type: object + required: + - vessel + - purpose + - product_phase + - from_vessel + - separation_vessel + - to_vessel + - waste_phase_to_vessel + - product_vessel + - waste_vessel + - solvent + - solvent_volume + - volume + - through + - repeats + - stir_time + - stir_speed + - settling_time + title: Separate_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Separate_Result + type: object + required: + - goal + title: Separate + type: object + type: Separate + StartStirProtocol: + feedback: {} + goal: + purpose: purpose + stir_speed: stir_speed + vessel: vessel + goal_default: + purpose: '' + stir_speed: 0.0 + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_speed: + type: number + current_status: + type: string + progress: + type: number + required: + - progress + - current_speed + - current_status + title: StartStir_Feedback + type: object + goal: + properties: + purpose: + type: string + stir_speed: + type: number + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - stir_speed + - purpose + title: StartStir_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: StartStir_Result + type: object + required: + - goal + title: StartStir + type: object + type: StartStir + StirProtocol: + feedback: {} + goal: + event: event + settling_time: settling_time + stir_speed: stir_speed + stir_time: stir_time + time: time + time_spec: time_spec + vessel: vessel + goal_default: + event: '' + settling_time: '' + stir_speed: 0.0 + stir_time: 0.0 + time: '' + time_spec: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: Stir_Feedback + type: object + goal: + properties: + event: + type: string + settling_time: + type: string + stir_speed: + type: number + stir_time: + type: number + time: + type: string + time_spec: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + - time + - event + - time_spec + - stir_time + - stir_speed + - settling_time + title: Stir_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Stir_Result + type: object + required: + - goal + title: Stir + type: object + type: Stir + StopStirProtocol: + feedback: {} + goal: + vessel: vessel + goal_default: + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + output: + - data_key: vessel + data_source: executor + data_type: resource + handler_key: VesselOut + label: Vessel + placeholder_keys: + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_status: + type: string + progress: + type: number + required: + - progress + - current_status + title: StopStir_Feedback + type: object + goal: + properties: + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + required: + - vessel + title: StopStir_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: StopStir_Result + type: object + required: + - goal + title: StopStir + type: object + type: StopStir + TransferProtocol: + feedback: {} + goal: + amount: amount + from_vessel: from_vessel + rinsing_repeats: rinsing_repeats + rinsing_solvent: rinsing_solvent + rinsing_volume: rinsing_volume + solid: solid + time: time + to_vessel: to_vessel + viscous: viscous + volume: volume + goal_default: + amount: '' + from_vessel: '' + rinsing_repeats: 0 + rinsing_solvent: '' + rinsing_volume: 0.0 + solid: false + time: 0.0 + to_vessel: '' + viscous: false + volume: 0.0 + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVessel + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVessel + label: To Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Rinsing Solvent + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: FromVesselOut + label: From Vessel + - data_key: vessel + data_source: executor + data_type: resource + handler_key: ToVesselOut + label: To Vessel + placeholder_keys: + from_vessel: unilabos_nodes + to_vessel: unilabos_nodes + result: {} + schema: + description: '' + properties: + feedback: + properties: + current_status: + type: string + progress: + type: number + transferred_volume: + type: number + required: + - progress + - transferred_volume + - current_status + title: Transfer_Feedback + type: object + goal: + properties: + amount: + type: string + from_vessel: + type: string + rinsing_repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + rinsing_solvent: + type: string + rinsing_volume: + type: number + solid: + type: boolean + time: + type: number + to_vessel: + type: string + viscous: + type: boolean + volume: + type: number + required: + - from_vessel + - to_vessel + - volume + - amount + - time + - viscous + - rinsing_solvent + - rinsing_volume + - rinsing_repeats + - solid + title: Transfer_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: Transfer_Result + type: object + required: + - goal + title: Transfer + type: object + type: Transfer + WashSolidProtocol: + feedback: {} + goal: + filtrate_vessel: filtrate_vessel + repeats: repeats + solvent: solvent + stir: stir + stir_speed: stir_speed + temp: temp + time: time + vessel: vessel + volume: volume + goal_default: + event: '' + filtrate_vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + mass: '' + repeats: 0 + repeats_spec: '' + solvent: '' + stir: false + stir_speed: 0.0 + temp: 0.0 + time: '' + vessel: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + volume: '' + volume_spec: '' + handles: + input: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: Vessel + label: Vessel + - data_key: solvent + data_source: handle + data_type: resource + handler_key: solvent + label: Solvent + - data_key: filtrate_vessel + data_source: handle + data_type: resource + handler_key: filtrate_vessel + label: Filtrate Vessel + output: + - data_key: vessel + data_source: handle + data_type: resource + handler_key: VesselOut + label: Vessel Out + - data_key: filtrate_vessel + data_source: executor + data_type: resource + handler_key: filtrate_vessel_out + label: Filtrate Vessel + placeholder_keys: + filtrate_vessel: unilabos_resources + vessel: unilabos_resources + result: {} + schema: + description: '' + properties: + feedback: + properties: + progress: + type: number + status: + type: string + required: + - status + - progress + title: WashSolid_Feedback + type: object + goal: + properties: + event: + type: string + filtrate_vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: filtrate_vessel + type: object + mass: + type: string + repeats: + maximum: 2147483647 + minimum: -2147483648 + type: integer + repeats_spec: + type: string + solvent: + type: string + stir: + type: boolean + stir_speed: + type: number + temp: + type: number + time: + type: string + vessel: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: vessel + type: object + volume: + type: string + volume_spec: + type: string + required: + - vessel + - solvent + - volume + - filtrate_vessel + - temp + - stir + - stir_speed + - time + - repeats + - volume_spec + - repeats_spec + - mass + - event + title: WashSolid_Goal + type: object + result: + properties: + message: + type: string + return_info: + type: string + success: + type: boolean + required: + - success + - message + - return_info + title: WashSolid_Result + type: object + required: + - goal + title: WashSolid + type: object + type: WashSolid + auto-create_ros_action_server: + feedback: {} + goal: {} + goal_default: + action_name: null + action_value_mapping: null + handles: {} + result: {} + schema: + description: create_ros_action_server的参数schema + properties: + feedback: {} + goal: + properties: + action_name: + type: string + action_value_mapping: + type: string + required: + - action_name + - action_value_mapping + type: object + result: {} + required: + - goal + title: create_ros_action_server参数 + type: object + type: UniLabJsonCommand + auto-execute_single_action: + feedback: {} + goal: {} + goal_default: + action_kwargs: null + action_name: null + device_id: null + handles: {} + result: {} + schema: + description: execute_single_action的参数schema + properties: + feedback: {} + goal: + properties: + action_kwargs: + type: string + action_name: + type: string + device_id: + type: string + required: + - device_id + - action_name + - action_kwargs + type: object + result: {} + required: + - goal + title: execute_single_action参数 + type: object + type: UniLabJsonCommandAsync + auto-initialize_device: + feedback: {} + goal: {} + goal_default: + device_config: null + device_id: null + handles: {} + result: {} + schema: + description: initialize_device的参数schema + properties: + feedback: {} + goal: + properties: + device_config: + type: string + device_id: + type: string + required: + - device_id + - device_config + type: object + result: {} + required: + - goal + title: initialize_device参数 + type: object + type: UniLabJsonCommand + module: unilabos.ros.nodes.presets.workstation:ROS2WorkstationNode + status_types: {} + type: ros2 + config_info: [] + description: Workstation + handles: [] + icon: '' + init_param_schema: + config: + properties: + action_value_mappings: + type: object + children: + type: object + device_id: + type: string + driver_instance: + type: string + hardware_interface: + type: object + print_publish: + default: true + type: string + protocol_type: + items: + type: string + type: array + resource_tracker: + type: string + status_types: + type: object + required: + - protocol_type + - children + - driver_instance + - device_id + - status_types + - action_value_mappings + - hardware_interface + type: object + data: + properties: {} + required: [] + type: object + version: 1.0.0 +workstation.example: + category: + - work_station + class: + action_value_mappings: {} + module: unilabos.devices.workstation.workstation_base:WorkstationExample + status_types: {} + type: python + config_info: [] + description: '' + handles: [] + icon: '' + init_param_schema: + config: + properties: + station_resource: + type: object + required: + - station_resource + type: object + data: + properties: {} + required: [] + type: object + version: 1.0.0 diff --git a/unilabos/devices/workstation/workstation_base.py b/unilabos/devices/workstation/workstation_base.py index 1988249f..65b34a93 100644 --- a/unilabos/devices/workstation/workstation_base.py +++ b/unilabos/devices/workstation/workstation_base.py @@ -112,17 +112,17 @@ class ResourceSynchronizer(ABC): self.workstation = workstation @abstractmethod - def sync_from_external(self) -> bool: + async def sync_from_external(self) -> bool: """从外部系统同步物料到本地deck""" pass @abstractmethod - def sync_to_external(self, plr_resource: PLRResource) -> bool: + async def sync_to_external(self, plr_resource: PLRResource) -> bool: """将本地物料同步到外部系统""" pass @abstractmethod - def handle_external_change(self, change_info: Dict[str, Any]) -> bool: + async def handle_external_change(self, change_info: Dict[str, Any]) -> bool: """处理外部系统的变更通知""" pass @@ -147,15 +147,21 @@ class WorkstationBase(ABC): def __init__( self, - deck: Deck, + station_resource: PLRResource, *args, **kwargs, # 必须有kwargs ): + # 基本配置 + print(station_resource) + self.deck_config = station_resource + # PLR 物料系统 - self.deck: Optional[Deck] = deck + self.deck: Optional[Deck] = None self.plr_resources: Dict[str, PLRResource] = {} - self.resource_synchronizer = None # type: Optional[ResourceSynchronizer] + # 资源同步器(可选) + # self.resource_synchronizer = ResourceSynchronizer(self) # 要在driver中自行初始化,只有workstation用 + # 硬件接口 self.hardware_interface: Union[Any, str] = None @@ -168,10 +174,97 @@ class WorkstationBase(ABC): # 支持的工作流(静态预定义) self.supported_workflows: Dict[str, WorkflowInfo] = {} - def post_init(self, ros_node: ROS2WorkstationNode) -> None: # 初始化物料系统 - self._ros_node = ros_node - self._ros_node.update_resource([self.deck]) + self._initialize_material_system() + + # 注册支持的工作流 + # self._register_supported_workflows() + + # logger.info(f"工作站 {device_id} 初始化完成(简化版)") + + def _initialize_material_system(self): + """初始化物料系统 - 使用 graphio 转换""" + try: + from unilabos.resources.graphio import resource_ulab_to_plr + + # # 1. 合并 deck_config 和 children 创建完整的资源树 + # complete_resource_config = self._create_complete_resource_config() + + # # 2. 使用 graphio 转换为 PLR 资源 + # self.deck = resource_ulab_to_plr(complete_resource_config, plr_model=True) + + # # 3. 建立资源映射 + # self._build_resource_mappings(self.deck) + + # # 4. 如果有资源同步器,执行初始同步 + # if self.resource_synchronizer: + # # 这里可以异步执行,暂时跳过 + # pass + + # logger.info(f"工作站 {self.device_id} 物料系统初始化成功,创建了 {len(self.plr_resources)} 个资源") + pass + except Exception as e: + # logger.error(f"工作站 {self.device_id} 物料系统初始化失败: {e}") + raise + + def _create_complete_resource_config(self) -> Dict[str, Any]: + """创建完整的资源配置 - 合并 deck_config 和 children""" + # 创建主 deck 配置 + deck_resource = { + "id": f"{self.device_id}_deck", + "name": f"{self.device_id}_deck", + "type": "deck", + "position": {"x": 0, "y": 0, "z": 0}, + "config": { + "size_x": self.deck_config.get("size_x", 1000.0), + "size_y": self.deck_config.get("size_y", 1000.0), + "size_z": self.deck_config.get("size_z", 100.0), + **{k: v for k, v in self.deck_config.items() if k not in ["size_x", "size_y", "size_z"]}, + }, + "data": {}, + "children": [], + "parent": None, + } + + # 添加子资源 + if self._children: + children_list = [] + for child_id, child_config in self._children.items(): + child_resource = self._normalize_child_resource(child_id, child_config, deck_resource["id"]) + children_list.append(child_resource) + deck_resource["children"] = children_list + + return deck_resource + + def _normalize_child_resource(self, resource_id: str, config: Dict[str, Any], parent_id: str) -> Dict[str, Any]: + """标准化子资源配置""" + return { + "id": resource_id, + "name": config.get("name", resource_id), + "type": config.get("type", "container"), + "position": self._normalize_position(config.get("position", {})), + "config": config.get("config", {}), + "data": config.get("data", {}), + "children": [], # 简化版本:只支持一层子资源 + "parent": parent_id, + } + + def _normalize_position(self, position: Any) -> Dict[str, float]: + """标准化位置信息""" + if isinstance(position, dict): + return { + "x": float(position.get("x", 0)), + "y": float(position.get("y", 0)), + "z": float(position.get("z", 0)), + } + elif isinstance(position, (list, tuple)) and len(position) >= 2: + return { + "x": float(position[0]), + "y": float(position[1]), + "z": float(position[2]) if len(position) > 2 else 0.0, + } + else: + return {"x": 0.0, "y": 0.0, "z": 0.0} def _build_resource_mappings(self, deck: Deck): """递归构建资源映射""" @@ -191,12 +284,12 @@ class WorkstationBase(ABC): def set_hardware_interface(self, hardware_interface: Union[Any, str]): """设置硬件接口""" self.hardware_interface = hardware_interface - logger.info(f"工作站 {self._ros_node.device_id} 硬件接口设置: {type(hardware_interface).__name__}") + logger.info(f"工作站 {self.device_id} 硬件接口设置: {type(hardware_interface).__name__}") def set_workstation_node(self, workstation_node: "ROS2WorkstationNode"): """设置协议节点引用(用于代理模式)""" self._ros_node = workstation_node - logger.info(f"工作站 {self._ros_node.device_id} 关联协议节点") + logger.info(f"工作站 {self.device_id} 关联协议节点") # ============ 设备操作接口 ============ @@ -255,21 +348,21 @@ class WorkstationBase(ABC): """按类型查找资源""" return [res for res in self.plr_resources.values() if isinstance(res, resource_type)] - def sync_with_external_system(self) -> bool: + async def sync_with_external_system(self) -> bool: """与外部物料系统同步""" if not self.resource_synchronizer: - logger.info(f"工作站 {self._ros_node.device_id} 没有配置资源同步器") + logger.info(f"工作站 {self.device_id} 没有配置资源同步器") return True try: - success = self.resource_synchronizer.sync_from_external() + success = await self.resource_synchronizer.sync_from_external() if success: - logger.info(f"工作站 {self._ros_node.device_id} 外部同步成功") + logger.info(f"工作站 {self.device_id} 外部同步成功") else: - logger.warning(f"工作站 {self._ros_node.device_id} 外部同步失败") + logger.warning(f"工作站 {self.device_id} 外部同步失败") return success except Exception as e: - logger.error(f"工作站 {self._ros_node.device_id} 外部同步异常: {e}") + logger.error(f"工作站 {self.device_id} 外部同步异常: {e}") return False # ============ 简化的工作流控制 ============ @@ -287,23 +380,23 @@ class WorkstationBase(ABC): if success: self.current_workflow_status = WorkflowStatus.RUNNING - logger.info(f"工作站 {self._ros_node.device_id} 工作流 {workflow_name} 启动成功") + logger.info(f"工作站 {self.device_id} 工作流 {workflow_name} 启动成功") else: self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self._ros_node.device_id} 工作流 {workflow_name} 启动失败") + logger.error(f"工作站 {self.device_id} 工作流 {workflow_name} 启动失败") return success except Exception as e: self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self._ros_node.device_id} 执行工作流失败: {e}") + logger.error(f"工作站 {self.device_id} 执行工作流失败: {e}") return False def stop_workflow(self, emergency: bool = False) -> bool: """停止工作流""" try: if self.current_workflow_status in [WorkflowStatus.IDLE, WorkflowStatus.STOPPED]: - logger.warning(f"工作站 {self._ros_node.device_id} 没有正在运行的工作流") + logger.warning(f"工作站 {self.device_id} 没有正在运行的工作流") return True self.current_workflow_status = WorkflowStatus.STOPPING @@ -313,16 +406,16 @@ class WorkstationBase(ABC): if success: self.current_workflow_status = WorkflowStatus.STOPPED - logger.info(f"工作站 {self._ros_node.device_id} 工作流停止成功 (紧急: {emergency})") + logger.info(f"工作站 {self.device_id} 工作流停止成功 (紧急: {emergency})") else: self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self._ros_node.device_id} 工作流停止失败") + logger.error(f"工作站 {self.device_id} 工作流停止失败") return success except Exception as e: self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self._ros_node.device_id} 停止工作流失败: {e}") + logger.error(f"工作站 {self.device_id} 停止工作流失败: {e}") return False # ============ 状态属性 ============ @@ -348,7 +441,49 @@ class WorkstationBase(ABC): return 0.0 return time.time() - self.workflow_start_time + # ============ 抽象方法 - 子类必须实现 ============ -class ProtocolNode(WorkstationBase): - def __init__(self, deck: Optional[PLRResource], *args, **kwargs): - super().__init__(deck, *args, **kwargs) + # @abstractmethod + # def _register_supported_workflows(self): + # """注册支持的工作流 - 子类必须实现""" + # pass + + # @abstractmethod + # def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool: + # """执行工作流的具体实现 - 子类必须实现""" + # pass + + # @abstractmethod + # def _stop_workflow_impl(self, emergency: bool = False) -> bool: + # """停止工作流的具体实现 - 子类必须实现""" + # pass + +class WorkstationExample(WorkstationBase): + """工作站示例实现""" + + def _register_supported_workflows(self): + """注册支持的工作流""" + self.supported_workflows["example_workflow"] = WorkflowInfo( + name="example_workflow", + description="这是一个示例工作流", + estimated_duration=300.0, + required_materials=["sample_plate"], + output_product="processed_plate", + parameters_schema={"param1": "string", "param2": "integer"}, + ) + + def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool: + """执行工作流的具体实现""" + if workflow_name not in self.supported_workflows: + logger.error(f"工作站 {self.device_id} 不支持工作流: {workflow_name}") + return False + + # 这里添加实际的工作流逻辑 + logger.info(f"工作站 {self.device_id} 正在执行工作流: {workflow_name} with parameters {parameters}") + return True + + def _stop_workflow_impl(self, emergency: bool = False) -> bool: + """停止工作流的具体实现""" + # 这里添加实际的停止逻辑 + logger.info(f"工作站 {self.device_id} 正在停止工作流 (紧急: {emergency})") + return True \ No newline at end of file diff --git a/unilabos/devices/workstation/workstation_http_service.py b/unilabos/devices/workstation/workstation_http_service.py index 4565edea..3805d2ce 100644 --- a/unilabos/devices/workstation/workstation_http_service.py +++ b/unilabos/devices/workstation/workstation_http_service.py @@ -149,22 +149,6 @@ class WorkstationHTTPHandler(BaseHTTPRequestHandler): ) self._send_response(error_response) - def do_OPTIONS(self): - """处理OPTIONS请求 - CORS预检请求""" - try: - # 发送CORS响应头 - self.send_response(200) - self.send_header('Access-Control-Allow-Origin', '*') - self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') - self.send_header('Access-Control-Allow-Headers', 'Content-Type, Authorization') - self.send_header('Access-Control-Max-Age', '86400') - self.end_headers() - - except Exception as e: - logger.error(f"OPTIONS请求处理失败: {e}") - self.send_response(500) - self.end_headers() - def _handle_step_finish_report(self, request_data: Dict[str, Any]) -> HttpResponse: """处理步骤完成报送(统一LIMS协议规范)""" try: @@ -222,7 +206,7 @@ class WorkstationHTTPHandler(BaseHTTPRequestHandler): # 验证data字段内容 data = request_data['data'] - data_required_fields = ['orderCode', 'orderName', 'sampleId', 'startTime', 'endTime', 'status'] + data_required_fields = ['orderCode', 'orderName', 'sampleId', 'startTime', 'endTime', 'Status'] if data_missing_fields := [field for field in data_required_fields if field not in data]: return HttpResponse( success=False, @@ -243,7 +227,7 @@ class WorkstationHTTPHandler(BaseHTTPRequestHandler): "0": "待生产", "2": "进样", "10": "开始", "20": "完成", "-2": "异常停止", "-3": "人工停止" } - status_desc = status_names.get(str(data['status']), f"状态{data['status']}") + status_desc = status_names.get(str(data['Status']), f"状态{data['Status']}") return HttpResponse( success=True, @@ -396,21 +380,6 @@ class WorkstationHTTPHandler(BaseHTTPRequestHandler): """处理物料变更报送""" try: # 验证必需字段 - if 'brand' in request_data: - if request_data['brand'] == "bioyond": # 奔曜 - error_msg = request_data["text"] - logger.info(f"收到奔曜错误处理报送: {error_msg}") - return HttpResponse( - success=True, - message=f"错误处理报送已收到: {error_msg}", - acknowledgment_id=f"ERROR_{int(time.time() * 1000)}_{error_msg.get('action_id', 'unknown')}", - data=None - ) - else: - return HttpResponse( - success=False, - message=f"缺少厂家信息(brand字段)" - ) required_fields = ['workstation_id', 'timestamp', 'resource_id', 'change_type'] if missing_fields := [field for field in required_fields if field not in request_data]: return HttpResponse( @@ -438,45 +407,23 @@ class WorkstationHTTPHandler(BaseHTTPRequestHandler): def _handle_error_handling_report(self, request_data: Dict[str, Any]) -> HttpResponse: """处理错误处理报送""" try: - # 检查是否为奔曜格式的错误报送 - if 'brand' in request_data and str(request_data['brand']).lower() == "bioyond": - # 奔曜格式处理 - if 'text' not in request_data: - return HttpResponse( - success=False, - message="奔曜格式缺少text字段" - ) - - error_data = request_data["text"] - logger.info(f"收到奔曜错误处理报送: {error_data}") - - # 调用工作站的处理方法 - result = self.workstation.handle_external_error(error_data) - + # 验证必需字段 + required_fields = ['workstation_id', 'timestamp', 'error_type', 'error_message'] + if missing_fields := [field for field in required_fields if field not in request_data]: return HttpResponse( - success=True, - message=f"错误处理报送已收到: 任务{error_data.get('task', 'unknown')}, 错误代码{error_data.get('code', 'unknown')}", - acknowledgment_id=f"ERROR_{int(time.time() * 1000)}_{error_data.get('task', 'unknown')}", - data=result - ) - else: - # 标准格式处理 - required_fields = ['workstation_id', 'timestamp', 'error_type', 'error_message'] - if missing_fields := [field for field in required_fields if field not in request_data]: - return HttpResponse( - success=False, - message=f"缺少必要字段: {', '.join(missing_fields)}" - ) - - # 调用工作站的处理方法 - result = self.workstation.handle_external_error(request_data) - - return HttpResponse( - success=True, - message=f"错误处理报送已处理: {request_data['error_type']} - {request_data['error_message']}", - acknowledgment_id=f"ERROR_{int(time.time() * 1000)}_{request_data.get('action_id', 'unknown')}", - data=result + success=False, + message=f"缺少必要字段: {', '.join(missing_fields)}" ) + + # 调用工作站的处理方法 + result = self.workstation.handle_external_error(request_data) + + return HttpResponse( + success=True, + message=f"错误处理报送已处理: {request_data['error_type']} - {request_data['error_message']}", + acknowledgment_id=f"ERROR_{int(time.time() * 1000)}_{request_data.get('action_id', 'unknown')}", + data=result + ) except Exception as e: logger.error(f"处理错误处理报送失败: {e}") @@ -601,19 +548,13 @@ class WorkstationHTTPService: """停止HTTP服务""" try: if self.running and self.server: - logger.info("正在停止工作站HTTP报送服务...") self.running = False - - # 停止serve_forever循环 self.server.shutdown() + self.server.server_close() - # 等待服务器线程结束 if self.server_thread and self.server_thread.is_alive(): self.server_thread.join(timeout=5.0) - # 关闭服务器套接字 - self.server.server_close() - logger.info("工作站HTTP报送服务已停止") except Exception as e: @@ -622,13 +563,11 @@ class WorkstationHTTPService: def _run_server(self): """运行HTTP服务器""" try: - # 使用serve_forever()让服务持续运行 - self.server.serve_forever() + while self.running: + self.server.handle_request() except Exception as e: if self.running: # 只在非正常停止时记录错误 logger.error(f"HTTP服务运行错误: {e}") - finally: - logger.info("HTTP服务器线程已退出") @property def is_running(self) -> bool: @@ -664,49 +603,3 @@ __all__ = [ 'MaterialChangeReport', 'TaskExecutionReport' ] - - -if __name__ == "__main__": - # 简单测试HTTP服务 - class DummyWorkstation: - device_id = "WS-001" - - def process_step_finish_report(self, report_request): - return {"processed": True} - - def process_sample_finish_report(self, report_request): - return {"processed": True} - - def process_order_finish_report(self, report_request, used_materials): - return {"processed": True} - - def process_material_change_report(self, report_data): - return {"processed": True} - - def handle_external_error(self, error_data): - return {"handled": True} - - workstation = DummyWorkstation() - http_service = WorkstationHTTPService(workstation) - - try: - http_service.start() - print(f"测试服务器已启动: {http_service.service_url}") - print("按 Ctrl+C 停止服务器") - print("服务将持续运行,等待接收HTTP请求...") - - # 保持服务器运行 - 使用更好的等待机制 - try: - while http_service.is_running: - time.sleep(1) - except KeyboardInterrupt: - print("\n接收到停止信号...") - - except KeyboardInterrupt: - print("\n正在停止服务器...") - http_service.stop() - print("服务器已停止") - except Exception as e: - print(f"服务器运行错误: {e}") - http_service.stop() -