From 7a245da379d62f651e687c9325cb6fa63d93c8f5 Mon Sep 17 00:00:00 2001 From: noiosoooo9999 Date: Sun, 11 Jan 2026 11:08:41 +0800 Subject: [PATCH 01/25] addworkstation --- .../eit_synthesis_station/batch_in_tray.xlsx | Bin 0 -> 5939 bytes .../eit_synthesis_station/chemical_list.xlsx | Bin 0 -> 210502 bytes .../eit_synthesis_station/config/__init__.py | 24 + .../eit_synthesis_station/config/constants.py | 187 ++ .../eit_synthesis_station/config/setting.py | 88 + .../controller/__init__.py | 7 + .../controller/station_controller.py | 2069 +++++++++++++++++ .../eit_synthesis_station/driver/__init.py | 27 + .../driver/api_client.py | 615 +++++ .../driver/exceptions.py | 71 + .../reaction_template.xlsx | Bin 0 -> 11144 bytes .../eit_synthesis_station/requirements.txt | 3 + .../eit_synthesis_station/station_manager.py | 738 ++++++ 13 files changed, 3829 insertions(+) create mode 100644 unilabos/devices/workstation/eit_synthesis_station/batch_in_tray.xlsx create mode 100644 unilabos/devices/workstation/eit_synthesis_station/chemical_list.xlsx create mode 100644 unilabos/devices/workstation/eit_synthesis_station/config/__init__.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/config/constants.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/config/setting.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/controller/__init__.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/controller/station_controller.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/driver/__init.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/driver/api_client.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/driver/exceptions.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station/reaction_template.xlsx create mode 100644 unilabos/devices/workstation/eit_synthesis_station/requirements.txt create mode 100644 unilabos/devices/workstation/eit_synthesis_station/station_manager.py diff --git a/unilabos/devices/workstation/eit_synthesis_station/batch_in_tray.xlsx b/unilabos/devices/workstation/eit_synthesis_station/batch_in_tray.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6a5ec24a46801c984c654030c59e664fbfae9b0c GIT binary patch literal 5939 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 literal 0 HcmV?d00001 diff --git a/unilabos/devices/workstation/eit_synthesis_station/chemical_list.xlsx b/unilabos/devices/workstation/eit_synthesis_station/chemical_list.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..48421ee54d49c9e2f7e0fa6df1b2127065dd8a60 GIT binary patch literal 210502 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 qg9W?)PJL8uxl-kJ+e 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 "Settings": + """ + 功能: + 从环境变量读取配置, 便于部署与 CI. + 参数: + 无. + 返回: + Settings. + 环境变量: + SYN_STATION_BASE_URL, SYN_STATION_USERNAME, SYN_STATION_PASSWORD, + SYN_STATION_TIMEOUT_S, SYN_STATION_VERIFY_SSL, SYN_STATION_LOG_LEVEL. + """ + base_url = os.getenv("SYN_STATION_BASE_URL", Settings.base_url) + username = os.getenv("SYN_STATION_USERNAME", Settings.username) + password = os.getenv("SYN_STATION_PASSWORD", Settings.password) + + timeout_s_str = os.getenv("SYN_STATION_TIMEOUT_S", str(Settings.timeout_s)) + try: + timeout_s = float(timeout_s_str) + except ValueError: + timeout_s = Settings.timeout_s + + verify_ssl_str = os.getenv("SYN_STATION_VERIFY_SSL", str(Settings.verify_ssl)) + verify_ssl = verify_ssl_str.strip().lower() in ("1", "true", "yes", "y", "on") + + log_level = os.getenv("SYN_STATION_LOG_LEVEL", Settings.log_level) + + return Settings( + base_url=base_url, + username=username, + password=password, + timeout_s=timeout_s, + verify_ssl=verify_ssl, + log_level=log_level, + ) + + +def configure_logging(level: str = "INFO") -> None: + """ + 功能: + 配置全局 logging, 统一输出格式. + 参数: + level: 日志级别, 例如 "DEBUG", "INFO". + 返回: + 无. + """ + numeric_level = getattr(logging, level.upper(), logging.INFO) + + root = logging.getLogger() + root.setLevel(numeric_level) + + if not root.handlers: + handler = logging.StreamHandler() + formatter = logging.Formatter( + fmt="%(asctime)s %(levelname)s %(name)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + handler.setFormatter(formatter) + root.addHandler(handler) diff --git a/unilabos/devices/workstation/eit_synthesis_station/controller/__init__.py b/unilabos/devices/workstation/eit_synthesis_station/controller/__init__.py new file mode 100644 index 00000000..15980a58 --- /dev/null +++ b/unilabos/devices/workstation/eit_synthesis_station/controller/__init__.py @@ -0,0 +1,7 @@ +""" +功能: + business 层导出. +""" +from .station_controller import SynthesisStationController + +__all__ = ["LabAutomationController", "PayloadFactory"] diff --git a/unilabos/devices/workstation/eit_synthesis_station/controller/station_controller.py b/unilabos/devices/workstation/eit_synthesis_station/controller/station_controller.py new file mode 100644 index 00000000..3c76b3c8 --- /dev/null +++ b/unilabos/devices/workstation/eit_synthesis_station/controller/station_controller.py @@ -0,0 +1,2069 @@ +import json +import logging +import time +from pathlib import Path +from typing import Any, Callable, Dict, List, Optional, Tuple +from collections import OrderedDict + +from config.constants import TaskStatus,StationState,DeviceModuleStatus, ResourceCode, TraySpec,TRAY_CODE_DISPLAY_NAME +from config.setting import Settings, configure_logging +from driver.api_client import ApiClient +from driver.exceptions import AuthorizationExpiredError, ValidationError +import re +import math + +import uuid + +JsonDict = Dict[str, Any] + +class SynthesisStationController: + """ + 功能: + 【上层逻辑】面向用户的控制器,提供自动登录、401 自动重登、状态轮询等。 + """ + + def __init__(self, settings: Optional[Settings] = None): + self._settings = settings or Settings.from_env() + self._client = ApiClient(self._settings) + self._logger = logging.getLogger(self.__class__.__name__) + + @property + def client(self) -> ApiClient: + return self._client + + # ---------- 自动登录 ------------- + def login(self) -> Tuple[str, str]: + """ + 功能: + 登录并缓存 token. + 参数: + 无. + 返回: + (token_type, access_token). + """ + resp = self._client.login(self._settings.username, self._settings.password) + token_type = str(resp.get("token_type", "Bearer")) + access_token = str(resp.get("access_token", "")) + self._client.set_token(token_type, access_token) + self._logger.debug("登录成功, token_type=%s", token_type) + return token_type, access_token + + def ensure_login(self) -> None: + """ + 功能: + 确保已登录,未登录则自动登录。 + 参数: + 无. + 返回: + 无. + """ + if not self._client.access_token: + self.login() + + def _call_with_relogin(self, func: Callable, *args, **kwargs): + """ + 功能: + 捕获 401,自动重登后重试一次。 + 参数: + func: 需要包装的函数. + *args, **kwargs: 透传参数. + 返回: + func 的返回值. + """ + self.ensure_login() + try: + return func(*args, **kwargs) + except AuthorizationExpiredError: + self._logger.warning("检测到登录失效, 自动重新登录并重试一次") + self.login() + return func(*args, **kwargs) + + def _extract_station_state(self, state_info: JsonDict) -> Optional[int]: + """ + 功能: + 从站点状态响应中提取状态码。 + 参数: + state_info: station_state 响应。 + 返回: + Optional[int], 状态码。 + """ + for key in ("state", "status"): + if key in state_info and isinstance(state_info.get(key), int): + return int(state_info[key]) + for outer in ("result", "data"): + obj = state_info.get(outer) + if isinstance(obj, dict): + for key in ("state", "status"): + if isinstance(obj.get(key), int): + return int(obj[key]) + return None + + def _assert_success(self, resp: JsonDict, action: str) -> None: + """ + 功能: + 校验接口响应的 code 与 msg 是否成功。 + 参数: + resp: 接口返回字典. + action: 当前动作名称, 用于异常信息. + 返回: + 无, 不满足条件则抛出 ValidationError. + """ + code = resp.get("code", 200) + msg = resp.get("msg", "") + if code != 200 or str(msg).lower() != "success": + raise ValidationError(f"{action}失败, code={code}, msg={msg}, resp={resp}") + + # ---------- 设备初始化 ---------- + def device_init( + self, + device_id: Optional[List[str]] = None, + *, + poll_interval_s: float = 1.0, + timeout_s: float = 600.0, + ) -> JsonDict: + """ + 功能: + 触发设备初始化, 然后轮询站点状态直到空闲。 + 参数: + device_id: 设备id列表, 当前忽略, 始终传空JSON。 + poll_interval_s: 轮询间隔秒数。 + timeout_s: 超时秒数, 超时抛出 TimeoutError。 + 返回: + Dict, 初始化接口原始响应。 + """ + resp = self._call_with_relogin(self._client.device_init, {}) # 传空JSON + self._assert_success(resp, "设备初始化") + self._logger.info("设备开始初始化") + start_ts = time.time() + + while True: + state = self.station_state() + if state is not None: + if state == int(StationState.IDLE): + self._logger.info("设备初始化完成") + return resp + else: + self._logger.warning("无法解析站点状态, resp=%s", state) + + if time.time() - start_ts > timeout_s: + raise TimeoutError(f"设备初始化等待空闲超时, last_state={state}") + + time.sleep(poll_interval_s) + + # ---------- 获取设备状态 ------------ + def station_state(self) -> int: + """ + 功能: + 获取工站整体状态码. + 参数: + 无. + 返回: + int, 工站状态码. + """ + resp = self._call_with_relogin(self._client.station_state) + # 兼容不同层级的状态字段 + for key in ("state", "status"): + if isinstance(resp.get(key), int): + return int(resp[key]) + for outer in ("result", "data"): + obj = resp.get(outer) + if isinstance(obj, dict): + for key in ("state", "status"): + if isinstance(obj.get(key), int): + return int(obj[key]) + + raise ValidationError(f"无法解析站点状态码, resp={resp}") + + def get_glovebox_env(self) -> JsonDict: + """ + 功能: + 调用 batch_list_device_runtimes 获取手套箱环境数据,并提取时间、箱压、水值、氧值 + 参数: + 无 + 返回: + Dict[str, Any], 包含 time、box_pressure、water_content、oxygen_content + """ + # 固定查询设备代码 352(手套箱环境) + resp = self._call_with_relogin(self._client.batch_list_device_runtimes, ["352"]) + data_container = resp.get("result") or resp.get("data") or resp + + if not isinstance(data_container, list) or len(data_container) == 0: + raise ValidationError(f"响应缺少环境数据, resp={resp}") + + first_item = data_container[0] # 多组相同数据,取第一组 + time_val = first_item.get("time") + box_pressure = first_item.get("box_pressure") + water_content = first_item.get("water_content") + oxygen_content = first_item.get("oxygen_content") + + result = { + "time": time_val, + "box_pressure": box_pressure, + "water_content": water_content, + "oxygen_content": oxygen_content, + } + self._logger.info("手套箱环境数据: %s", result) + return result + + def list_device_info(self) -> JsonDict: + """ + 功能: + 获取站点设备模块列表。暂不使用,使用get_all_device_info. + 参数: + 无. + 返回: + Dict, 接口响应. + """ + resp = self._call_with_relogin(self._client.list_device_info) + self._assert_success(resp, "获取站点设备模块列表") + return resp + + def get_all_device_info(self) -> JsonDict: + """ + 功能: + 获取全部设备信息,仅返回 station_data 字段 + 参数: + 无 + 返回: + Dict[str, Any], 包含 station_data 的字典 + """ + resp = self._call_with_relogin(self._client.get_all_device_info) + self._assert_success(resp, "获取全部设备信息") + + station_data = resp.get("station_data") or resp.get("data") or resp.get("result") + if not isinstance(station_data, list): + raise ValidationError(f"响应缺少 station_data, resp={resp}") + + return {"station_data": station_data} + + def list_device_status(self) -> List[JsonDict]: + """ + 功能: + 基于 get_all_device_info 提取设备名称与状态(状态名替换数值). + 参数: + 无. + 返回: + List[Dict], 包含 device_name、status(名称)、status_code(数值). + """ + raw = self.get_all_device_info() + station_list = raw.get("station_data") or raw.get("data") or raw.get("result") or [] + if not isinstance(station_list, list): + raise ValidationError(f"station_data 格式异常, resp={raw}") + + device_status: List[JsonDict] = [] + for station_item in station_list: + for dev in station_item.get("device_info", []): + status_val = dev.get("status") + status_name = ( + DeviceModuleStatus(status_val).name + if isinstance(status_val, int) and status_val in DeviceModuleStatus._value2member_map_ + else "UNKNOWN" + ) + device_status.append( + { + "device_name": dev.get("device_name"), + "status": status_name, # 如 AVAILABLE + "status_code": status_val, # 数值保留以便排查 + } + ) + self._logger.info("设备状态汇总完成, 数量=%s", len(device_status)) + return device_status + + # ---------- 获取站内资源信息 ---------- + def get_resource_info(self) -> List[JsonDict]: + """ + 功能: + 调用资源详情接口, 将打平的 resource_list 聚合为按资源位置展示的表格行. + 聚合协议: + 1) 资源位置: 取 layout_code 或 source_layout_code 的冒号前缀, 例如 "N-1:-1" 与 "N-1:0" 聚合为 "N-1". + 2) slot == -1: 表示托盘本体, 读取 resource_type 作为托盘型号, 并补充 resource_type_name. + 3) slot != -1: 表示托盘坑位, 统计数量作为 count. + 4) substance_details: 仅收集 substance 非空的坑位, 使用列表返回; 若无物质, 返回空列表. + 每个元素包含 slot, well, substance, value 字段. + 参数: + 无. + 返回: + List[JsonDict], 每个元素结构如下: + { + "layout_code": str, 资源位置, + "count": int, 坑位数量, + "substance_details": List[Dict], 物质详情列表, 无则 [], + "resource_type": int or None, 托盘型号编码, + "resource_type_name": str, 托盘型号中文名, + } + """ + response = self._call_with_relogin(self._client.get_resource_info, {}) + self._assert_success(response, "获取资源信息") + + resource_list = self._extract_resource_list(response) + rows = self._format_resource_rows(resource_list) + + self._logger.info("获取资源信息成功") + return rows + + def _extract_resource_list(self, response: JsonDict) -> List[JsonDict]: + """ + 功能: + 从不同响应包裹层中提取 resource_list, 兼容直接返回或嵌套在 result/data 中的情况. + 参数: + response: JsonDict, 接口原始响应. + 返回: + List[JsonDict], 资源明细列表. + """ + if "resource_list" in response: + resource_list = response.get("resource_list") + if resource_list is not None: + return resource_list + + for outer_key in ("result", "data"): + outer_obj = response.get(outer_key) + if isinstance(outer_obj, dict) and "resource_list" in outer_obj: + resource_list = outer_obj.get("resource_list") + if resource_list is not None: + return resource_list + + raise ValidationError(f"响应缺少 resource_list 字段, resp={response}") + + def _format_resource_rows(self, resource_list: List[JsonDict]) -> List[JsonDict]: + """ + 功能: + 将资源明细列表按资源位置聚合为返回行, 并生成 substance_details 列表. + 参数: + resource_list: List[JsonDict], 资源明细列表. + 返回: + List[JsonDict], 聚合后的行列表. + """ + grouped_by_layout: "OrderedDict[str, Dict[str, Any]]" = OrderedDict() + + for item in resource_list: + layout_code = self._get_layout_code(item) + if layout_code is None: + continue + if layout_code == "": + continue + if ":" not in layout_code: + continue + + layout_prefix, slot_text = layout_code.split(":", 1) + slot_index = self._safe_int(slot_text) + if slot_index is None: + continue + + if layout_prefix not in grouped_by_layout: + grouped_by_layout[layout_prefix] = {"tray_item": None, "media_items": []} + + if slot_index == -1: + grouped_by_layout[layout_prefix]["tray_item"] = item + else: + grouped_by_layout[layout_prefix]["media_items"].append(item) + + rows: List[JsonDict] = [] + for layout_prefix, group in grouped_by_layout.items(): + tray_item = group.get("tray_item") + media_items: List[JsonDict] = group.get("media_items", []) + + tray_code = self._get_tray_code(tray_item) + tray_name = self._get_tray_name(tray_code) + + rows.append( + { + "layout_code": layout_prefix, + "count": len(media_items), + "substance_details": self._build_substance_details(tray_code, media_items), + "resource_type": tray_code, + "resource_type_name": tray_name, + } + ) + + return rows + + def _get_layout_code(self, item: JsonDict) -> Optional[str]: + """ + 功能: + 获取明细中的布局编码字段, 兼容 layout_code 与 source_layout_code. + 参数: + item: JsonDict, 单条资源明细. + 返回: + Optional[str], 布局编码字符串. + """ + layout_code = item.get("layout_code") + if layout_code is not None and str(layout_code) != "": + return str(layout_code) + + source_layout_code = item.get("source_layout_code") + if source_layout_code is not None and str(source_layout_code) != "": + return str(source_layout_code) + + return None + + def _get_tray_code(self, tray_item: Optional[JsonDict]) -> Optional[int]: + """ + 功能: + 从托盘本体记录(slot == -1)中解析托盘编码(resource_type). + 参数: + tray_item: Optional[JsonDict], 托盘本体记录. + 返回: + Optional[int], 托盘编码. + """ + if tray_item is None: + return None + return self._safe_int(tray_item.get("resource_type")) + + def _get_tray_name(self, tray_code: Optional[int]) -> str: + """ + 功能: + 根据托盘编码获取中文名称, 未命中时返回空字符串. + 参数: + tray_code: Optional[int], 托盘编码. + 返回: + str, 托盘中文名. + """ + if tray_code is None: + return "" + tray_name = TRAY_CODE_DISPLAY_NAME.get(tray_code) + if tray_name is None: + return "" + return tray_name + + def _build_substance_details(self, tray_code: Optional[int], media_items: List[JsonDict]) -> List[JsonDict]: + """ + 功能: + 生成 substance_details 列表, 每个元素表示一个有物质的坑位. + 若无物质, 返回空列表. + 参数: + tray_code: Optional[int], 托盘编码, 用于 slot 到 well 的映射. + media_items: List[JsonDict], 坑位明细列表. + 返回: + List[JsonDict], 物质详情列表, 结构: + { + "slot": int or None, + "well": str, + "substance": str, + "value": str, + } + """ + if tray_code is None: + return [] + + tray_spec = self._get_tray_spec(tray_code) + details: List[JsonDict] = [] + + for item in media_items: + # 只输出实际有物质的坑位, 避免空坑位污染返回数据. + substance = item.get("substance") + if substance is None: + continue + if str(substance).strip() == "": + continue + + slot_index = self._extract_slot_index(item) + well_text = self._slot_to_well_text(slot_index, tray_spec) + value_text = self._extract_amount_with_unit(item, tray_code) + + details.append( + { + "slot": slot_index, + "well": well_text, + "substance": str(substance).strip(), + "value": value_text, + } + ) + + return details + + def _get_tray_spec(self, tray_code: int) -> Optional[Tuple[int, int]]: + """ + 功能: + 根据托盘编码获取 TraySpec 中的规格定义, 规格格式为 (col, row). + 参数: + tray_code: int, 托盘编码. + 返回: + Optional[Tuple[int, int]], 托盘规格(列数, 行数), 未匹配则 None. + """ + try: + enum_name = ResourceCode(tray_code).name + except Exception: + return None + + tray_spec = getattr(TraySpec, enum_name, None) + if tray_spec is None: + return None + return tray_spec + + def _extract_slot_index(self, item: JsonDict) -> Optional[int]: + """ + 功能: + 从 layout_code/source_layout_code 中提取 slot 序号. + 参数: + item: JsonDict, 单条坑位明细. + 返回: + Optional[int], slot 序号. + """ + layout_code = self._get_layout_code(item) + if layout_code is None: + return None + if layout_code == "": + return None + if ":" not in layout_code: + return None + + _, slot_text = layout_code.split(":", 1) + return self._safe_int(slot_text) + + def _slot_to_well_text(self, slot_index: Optional[int], tray_spec: Optional[Tuple[int, int]]) -> str: + """ + 功能: + 将 slot 序号按行优先映射为井位文本, 规则 A1, A2...B1. + 参数: + slot_index: Optional[int], slot 序号. + tray_spec: Optional[Tuple[int, int]], (col, row) 托盘规格. + 返回: + str, 井位文本, 无法映射时返回 "-". + """ + if slot_index is None: + return "-" + if tray_spec is None: + return str(slot_index) + + col_count, row_count = tray_spec + if col_count <= 0: + return str(slot_index) + if row_count <= 0: + return str(slot_index) + + row_index = slot_index // col_count + col_index = slot_index % col_count + 1 + + if row_index >= row_count: + return str(slot_index) + + return f"{chr(ord('A') + row_index)}{col_index}" + + def _extract_amount_with_unit(self, item: JsonDict, tray_code: Optional[int] = None) -> str: + """ + 功能: + 根据托盘类型提取可展示的数值与单位; 粉桶托盘优先读取重量, 试剂瓶托盘优先读取体积, 其他类型按通用顺序。 + 参数: + item: JsonDict, 单条槽位明细. + tray_code: Optional[int], 托盘资源编码, 用于确定重量或体积优先级。 + 返回: + str, 数值与单位拼接后的字符串, 例如 "5000mg". + """ + unit = item.get("unit") + unit_text = "" + if unit is not None: + if str(unit).strip() != "": + unit_text = str(unit).strip() + + powder_tray_code = int(ResourceCode.POWDER_BUCKET_TRAY_30ML) + bottle_tray_codes = { + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML), + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML), + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML), + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML), + } + + if tray_code is not None and tray_code == powder_tray_code: + amount_fields = ( + "available_weight", + "cur_weight", + "initial_weight" + ) + elif tray_code is not None and tray_code in bottle_tray_codes: + amount_fields = ( + "available_volume", + "cur_volume", + "initial_volume", + ) + else: + amount_fields = ( + "available_weight", + "available_volume", + "cur_weight", + "cur_volume", + "initial_weight", + "initial_volume", + ) + + amount_value = None + for field_name in amount_fields: + if field_name in item: + candidate = item.get(field_name) + if candidate is not None: + amount_value = candidate + break + + amount_text = self._format_number(amount_value) + if unit_text == "": + return amount_text + return f"{amount_text}{unit_text}" + + def _format_number(self, value: Any) -> str: + """ + 功能: + 将数值格式化为展示字符串, 整数不带小数, 小数去除尾随 0. + 参数: + value: Any, 输入数值. + 返回: + str, 格式化后的数字字符串, value 为空时返回 "0". + """ + if value is None: + return "0" + + try: + number_value = float(value) + except Exception: + return str(value) + + if abs(number_value - round(number_value)) < 1e-9: + return str(int(round(number_value))) + + text = f"{number_value:.6f}".rstrip("0").rstrip(".") + if text == "": + return "0" + return text + + def _safe_int(self, value: Any) -> Optional[int]: + """ + 功能: + 安全转换为 int, 转换失败返回 None. + 参数: + value: Any, 输入值. + 返回: + Optional[int], 转换结果. + """ + try: + return int(value) + except Exception: + return None + + # ---------- 编辑站内化学品 ---------- + def get_chemical_list( + self, + *, + query_key: Optional[str] = None, + limit: int = 20, + ) -> JsonDict: + """ + 功能: + 调用底层接口获取化学品列表,返回 chemical_sums 和 chemical_list + 参数: + query_key: 可选字符串,用于模糊查询 + limit: 返回条数,传负数则不传该参数 + 返回: + Dict[str, Any],包含 chemical_sums 和 chemical_list + """ + params = { + "query_key": query_key, + "sort": "desc", # 固定默认排序 + "offset": 0, # 固定起始偏移 + "limit": limit, + } + filtered_params = { + key: val + for key, val in params.items() + if val is not None and not (key == "limit" and isinstance(val, int) and val < 0) + } + + resp = self._call_with_relogin(self._client.get_chemical_list, **filtered_params) + data = resp if "chemical_list" in resp else (resp.get("result") or resp.get("data") or resp) + + chemical_sums = data.get("chemical_sums") + chemical_list = data.get("chemical_list", []) + return {"chemical_sums": chemical_sums, "chemical_list": chemical_list} + + def get_all_chemical_list(self) -> JsonDict: + """ + 功能: + 一次性获取全部化学品列表并返回包含 chemical_sums 和 chemical_list 的字典 + 参数: + 无 + 返回: + Dict[str, Any], 包含 chemical_sums 和 chemical_list + """ + first_resp = self.get_chemical_list() # 默认 limit=20 + first_data = first_resp.get("result") or first_resp.get("data") or first_resp + total = first_data.get("chemical_sums") + if not isinstance(total, int): + raise ValidationError(f"响应缺少 chemical_sums, resp={first_resp}") + + first_list = first_data.get("chemical_list") or [] + if len(first_list) >= total: + return {"chemical_sums": total, "chemical_list": first_list} + + full_resp = self.get_chemical_list(limit=total) # 用总数作为 limit + full_data = full_resp.get("result") or full_resp.get("data") or full_resp + full_list = full_data.get("chemical_list") or [] + return {"chemical_sums": total, "chemical_list": full_list} + + def add_chemical(self, payload: JsonDict) -> JsonDict: + return self._call_with_relogin(self._client.add_chemical, payload) + + def update_chemical(self, payload: JsonDict) -> JsonDict: + return self._call_with_relogin(self._client.update_chemical, payload) + + def delete_chemical(self, chemical_id: int) -> JsonDict: + """ + 功能: + 删除单个化学品 + 参数: + chemical_id: int, 化学品 id + 返回: + Dict[str, Any], 接口响应 + """ + resp = self._call_with_relogin(self._client.delete_chemical, chemical_id) + try: + self._assert_success(resp, "删除化学品") + self._logger.info("删除化学品成功: chemical_id=%s, resp=%s", chemical_id, resp) + except ValidationError as exc: + self._logger.info("删除化学品失败: chemical_id=%s, resp=%s", chemical_id, resp) + raise exc + return resp + + def sync_chemicals_from_data(self, items: List[JsonDict], *, overwrite: bool = False, limit: int = 20000) -> None: + """ + 功能: + 接收化学品数据列表,逐条查询是否存在,按需新增或更新 + 参数: + items: List[Dict], 包含 name, cas, state 等字段的字典列表 + overwrite: Bool, 是否覆盖更新 + limit: 查询 limit + 返回: + None + """ + if not items: + self._logger.info("输入数据为空, 退出同步") + return + + for item in items: + name = item.get("name") + cas = item.get("cas") + candidates: List[JsonDict] = [] + + # 先按名称查询 + if name: + name_resp = self.get_chemical_list(query_key=name, limit=limit) + candidates.extend(name_resp.get("chemical_list", [])) + + # 如有 CAS 再按 CAS 查询 + if cas: + cas_resp = self.get_chemical_list(query_key=cas, limit=limit) + candidates.extend(cas_resp.get("chemical_list", [])) + + # 按 fid 去重 + unique = {} + for chem in candidates: + fid = chem.get("fid") + if fid is not None: + unique[fid] = chem + matched = list(unique.values()) + + if not matched: + self.add_chemical(item) + self._logger.info("新增化学品: %s", name) + continue + + if not overwrite: + self._logger.info("已存在化学品, 跳过: %s", name) + continue + + target = matched[0] + payload = dict(item) + payload["fid"] = target["fid"] + self.update_chemical(payload) + self._logger.info("已覆盖更新化学品: %s, fid=%s", name, target["fid"]) + + # ---------- 化合物库管理 ---------- + def check_chemical_library_data(self, rows: List[JsonDict], headers: List[str]) -> Dict[str, List[str]]: + """ + 功能: + 校验化学品库数据完整性,检查表头、重复项与必填字段,返回错误和警告列表 + 参数: + rows: List[Dict[str, Any]], 文件数据行,键名需与表头一致 + headers: List[str], 原始表头列表 + 返回: + Dict[str, List[str]], 包含 errors 与 warnings + """ + required_headers = [ + "cas_number", "substance", "storage_location", + "molecular_weight", "physical_state", "density (g/mL)" + ] + header= [str(col).strip() for col in headers] + + errors: List[str] = [] + warnings: List[str] = [] + + # 汇总缺失表头 + missing_headers = [col for col in required_headers if col not in header] + if len(missing_headers) > 0: + errors.append(f"表头缺少字段: {', '.join(missing_headers)}") + + substance_count: Dict[str, int] = {} + missing_state_names: List[str] = [] + solid_missing_mw: List[str] = [] + liquid_missing: List[str] = [] + + for idx, row in enumerate(rows, start=2): + # 逐行检查必填项 + substance = str(row.get("substance") or "").strip() + physical_state = str(row.get("physical_state") or "").strip() + cas_number = str(row.get("cas_number") or "").strip() + molecular_weight = str(row.get("molecular_weight") or "").strip() + density = str(row.get("density (g/mL)") or "").strip() + + if substance != "": + substance_count[substance] = substance_count.get(substance, 0) + 1 + + if physical_state == "": + label = substance if substance != "" else f"第{idx}行" + missing_state_names.append(label) + + state_lower = physical_state.lower() + if cas_number != "" and state_lower == "solid" and molecular_weight == "": + target = substance if substance != "" else cas_number + solid_missing_mw.append(target) + + if state_lower == "liquid": + if molecular_weight == "" or density == "": + target = substance if substance != "" else cas_number if cas_number != "" else f"第{idx}行" + liquid_missing.append(target) + + duplicated_substance = [name for name, count in substance_count.items() if count > 1] + if len(duplicated_substance) > 0: + errors.append(f"substance 出现重复: {', '.join(duplicated_substance)}") + + if len(missing_state_names) > 0: + errors.append(f"physical_state 未填写: {', '.join(missing_state_names)}") + + if len(solid_missing_mw) > 0: + warnings.append(f"存在 solid 且有 CAS 但缺少 molecular_weight: {', '.join(solid_missing_mw)}") + + if len(liquid_missing) > 0: + warnings.append(f"physical_state 为 liquid 但缺少 molecular_weight 或 density: {', '.join(liquid_missing)}") + + if len(errors) > 0: + self._logger.error("化学品库校验失败, 错误=%s, 警告=%s", len(errors), len(warnings)) + else: + self._logger.info("化学品库校验通过, 警告=%s", len(warnings)) + + return {"errors": errors, "warnings": warnings} + + def deduplicate_chemical_library_data(self, rows: List[JsonDict], headers: List[str]) -> List[JsonDict]: + """ + 功能: + 依据 substance 自动去重化学品库,合并 brand/package_size/storage_location,处理其他字段差异 + 参数: + rows: List[Dict[str, Any]], 表格行数据,键名与表头一致 + headers: List[str], 原始表头列表,用于保持输出列顺序 + 返回: + List[Dict[str, Any]], 去重后的数据 + """ + if not headers or "substance" not in [str(h).strip().lower() for h in headers]: + self._logger.error("表头缺少 substance,无法去重") + return rows + + brand_fields = {"brand", "package_size", "storage_location"} + header_info = [(str(h).strip(), str(h).strip().lower()) for h in headers] + dedup_map: Dict[str, Dict[str, List[str]]] = {} + order: List[str] = [] + + def _clean(val: Any) -> str: + if val is None: + return "" + if isinstance(val, float): + if math.isnan(val): + return "" + if val.is_integer(): + return str(int(val)) # 374.0 -> "374" + return str(val).rstrip("0").rstrip(".") # 去掉尾随0或小数点 + if isinstance(val, int): + return str(val) + text = str(val).strip() + return "" if text.lower() == "nan" else text + + for row in rows: + substance = _clean(row.get("substance") or row.get("Substance")) + if substance == "": + self._logger.warning("发现缺少 substance 的行,已跳过") + continue + if substance not in dedup_map: + dedup_map[substance] = {info[1]: [] for info in header_info} + order.append(substance) + + field_store = dedup_map[substance] + for orig, key in header_info: + val = _clean(row.get(orig)) + if val == "": + continue + # 合并品牌/包装/库位,跳过空值 + if key in brand_fields: + if val not in field_store[key]: + field_store[key].append(val) + continue + # 其他字段保留不同值以便冲突时加括号 + if val not in field_store[key]: + field_store[key].append(val) + + result: List[JsonDict] = [] + for substance in order: + field_store = dedup_map[substance] + out_row: JsonDict = {} + for orig, key in header_info: + vals = field_store.get(key, []) + if key == "substance": + out_row[orig] = substance + elif key in brand_fields: + out_row[orig] = ";".join(vals) + else: + if len(vals) == 0: + out_row[orig] = "" + elif len(vals) == 1: + out_row[orig] = vals[0] + else: + out_row[orig] = f"({';'.join(vals)})" + result.append(out_row) + + self._logger.info("化合物库去重完成,原始行数=%s,去重后=%s", len(rows), len(result)) + return result + + def align_chemicals_from_data(self, rows: List[JsonDict], *, auto_delete: bool = False) -> List[JsonDict]: + """ + 功能: + 对齐工站化学品: 以传入数据为准校正工站数据,并回填 chemical_id/fid 到返回数据中 + 参数: + rows: List[Dict], 包含 substance/name, cas, physical_state 等字段 + auto_delete: bool, 是否删除工站内多余的化学品 + 返回: + List[Dict]: 更新后的数据列表 (包含回填的 ID) + """ + if not rows: + return [] + + # 获取工站所有数据 + station_data = self.get_all_chemical_list() + station_list = station_data.get("chemical_list") or [] + station_by_name = { + str(item.get("name") or "").strip(): item for item in station_list if item.get("name") + } + + updated = 0 + added = 0 + fid_by_substance: Dict[str, int] = {} + + # 遍历输入行进行同步 + for row in rows: + # 兼容字段名:substance 或 name + name = str(row.get("substance") or row.get("name") or "").strip() + if not name: + continue + + cas_file = str(row.get("cas_number") or row.get("cas") or "").strip() + state_file = str(row.get("physical_state") or row.get("state") or "").strip() + + existing = station_by_name.get(name) + + # Case A: 工站不存在 -> 新增 + if existing is None: + payload = {"name": name} + if cas_file: payload["cas"] = cas_file + if state_file: payload["state"] = state_file + + try: + resp = self.add_chemical(payload) + fid = resp.get("fid") or resp.get("chemical_id") + if isinstance(fid, int): + fid_by_substance[name] = fid + added += 1 + except Exception as e: + self._logger.error(f"新增失败 {name}: {e}") + continue + + # Case B: 工站存在 -> 检查更新 + fid = existing.get("fid") + fid_by_substance[name] = fid if isinstance(fid, int) else None + + payload = {k: v for k, v in existing.items() if v is not None} + payload["fid"] = fid + payload["name"] = existing.get("name") # 保持原名 + + need_update = False + cas_station = str(existing.get("cas") or "").strip() + if cas_file and cas_file != cas_station: + payload["cas"] = cas_file + need_update = True + + state_station = str(existing.get("state") or "").strip() + if state_file and state_file != state_station: + payload["state"] = state_file + need_update = True + + if need_update: + self.update_chemical(payload) + updated += 1 + + # 自动删除逻辑 + if auto_delete: + file_names = { + str(r.get("substance") or r.get("name") or "").strip() for r in rows + } + for item in station_list: + s_name = str(item.get("name") or "").strip() + s_fid = item.get("fid") + if s_name and s_name not in file_names and isinstance(s_fid, int): + self.delete_chemical(s_fid) + + # 回写 ID 到原数据结构 + result_rows = [] + for row in rows: + new_row = row.copy() + name = str(new_row.get("substance") or new_row.get("name") or "").strip() + fid = fid_by_substance.get(name) + if isinstance(fid, int): + new_row["chemical_id"] = fid # 统一回填到 chemical_id 字段 + result_rows.append(new_row) + + self._logger.info("化学品对齐逻辑执行完毕: 更新=%s, 新增=%s", updated, added) + return result_rows + + # ---------- 上料函数 ---------- + def _well_to_slot_index(self, well: str, tray_spec: Optional[Tuple[int, int]]) -> Optional[int]: + """ + 功能: + 将井位文本(如 A1、B2)转换为 slot 序号,按行优先编号。 + 参数: + well: str, 井位文本,格式 字母+数字,如 A1。 + tray_spec: Optional[Tuple[int, int]], (列数, 行数)。 + 返回: + Optional[int], 对应的 slot 序号,无法解析时返回 None。 + """ + if tray_spec is None: + return None + if not well or len(well) < 2: + return None + row_char = well[0].upper() + if not row_char.isalpha(): + return None + try: + col_count, row_count = tray_spec + col_index = int(well[1:]) # 1-based + row_index = ord(row_char) - ord("A") # 0-based + if col_index < 1 or col_index > col_count: + return None + if row_index < 0 or row_index >= row_count: + return None + return row_index * col_count + (col_index - 1) + except Exception: + return None + + def _normalize_tray_code_text(self, raw: Any) -> str: + """ + 功能: + 从类似“50 μL Tip 头托盘(201000815)”提取括号内的编码; 如果没有括号则返回原文本. + 参数: + raw: Any, 单元格原始值. + 返回: + str, 纯数字编码字符串或原文本. + """ + if raw is None: + return "" + text = str(raw).strip() + if "(" in text and ")" in text: + inside = text[text.rfind("(") + 1:text.rfind(")")] + digits = "".join(ch for ch in inside if ch.isdigit()) + if digits != "": + return digits + return text + + def _split_amount_unit(self, text: str) -> Tuple[float, str]: + """ + 功能: + 将类似 '2mL' 或 '500mg' 的文本拆为数值和单位. + 参数: + text: str, 输入文本. + 返回: + (float, str), 数值与单位, 无法解析数值时返回 0. + """ + number_part = "" + unit_part = "" + for ch in str(text): + if ch.isdigit() or ch == ".": + number_part += ch + else: + unit_part += ch + try: + value = float(number_part) if number_part != "" else 0.0 + except Exception: + value = 0.0 + unit = unit_part.strip() if unit_part.strip() != "" else "mL" + return value, unit + + def batch_in_tray(self, resource_req_list: List[JsonDict]) -> JsonDict: + return self._call_with_relogin(self._client.batch_in_tray, resource_req_list) + + def build_batch_in_tray_payload(self, rows: List[Tuple[str, str, str]]) -> List[JsonDict]: + """ + 功能: + 将清洗后的上料数据转换为 API Payload + 参数: + rows: List[Tuple], 每个元素为 (position, tray_type_text, content_text) + 返回: + List[Dict]: resource_req_list + """ + # 内部映射表 + tray_to_media = { + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): ( + str(int(ResourceCode.REAGENT_BOTTLE_2ML)), True, "volume", "mL" + ), + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): ( + str(int(ResourceCode.REAGENT_BOTTLE_8ML)), True, "volume", "mL" + ), + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): ( + str(int(ResourceCode.REAGENT_BOTTLE_40ML)), True, "volume", "mL" + ), + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML): ( + str(int(ResourceCode.REAGENT_BOTTLE_125ML)), True, "volume", "mL" + ), + int(ResourceCode.POWDER_BUCKET_TRAY_30ML): ( + str(int(ResourceCode.POWDER_BUCKET_30ML)), False, "weight", "mg" + ), + } + no_substance_trays = { + int(ResourceCode.TIP_TRAY_50UL), int(ResourceCode.TIP_TRAY_1ML), int(ResourceCode.TIP_TRAY_5ML), + int(ResourceCode.REACTION_SEAL_CAP_TRAY), int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY), + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY), + int(ResourceCode.REACTION_TUBE_TRAY_2ML), int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML), + } + consumable_map = { + int(ResourceCode.TIP_TRAY_50UL): int(ResourceCode.TIP_50UL), + int(ResourceCode.TIP_TRAY_1ML): int(ResourceCode.TIP_1ML), + int(ResourceCode.TIP_TRAY_5ML): int(ResourceCode.TIP_5ML), + int(ResourceCode.REACTION_SEAL_CAP_TRAY): int(ResourceCode.REACTION_SEAL_CAP), + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY): int(ResourceCode.FLASH_FILTER_INNER_BOTTLE), + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE), + int(ResourceCode.REACTION_TUBE_TRAY_2ML): int(ResourceCode.REACTION_TUBE_2ML), + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML): int(ResourceCode.TEST_TUBE_MAGNET_2ML), + } + + # 简单的内存缓存 + chem_cache: Dict[str, Optional[int]] = {} + + def _resolve_fid(sub_name: str) -> int: + if sub_name in chem_cache: + if chem_cache[sub_name] is None: raise ValidationError(f"未找到化学品: {sub_name}") + return chem_cache[sub_name] + + # 调用 API 查询 + resp = self.get_chemical_list(query_key=sub_name, limit=10) + lst = resp.get("chemical_list", []) + for c in lst: + if str(c.get("name")).strip() == sub_name: + fid = c.get("fid") or c.get("chemical_id") + chem_cache[sub_name] = fid + return fid + + # 模糊匹配 fallback + if lst: + fid = lst[0].get("fid") or lst[0].get("chemical_id") + chem_cache[sub_name] = fid + return fid + + chem_cache[sub_name] = None + raise ValidationError(f"未找到化学品: {sub_name}") + + resource_req_list: List[JsonDict] = [] + + for position, tray_type_raw, content in rows: + tray_layout = str(position).strip() + if not tray_layout: continue + + tray_code_text = self._normalize_tray_code_text(tray_type_raw) + tray_code_int = self._safe_int(tray_code_text) + if tray_code_int is None: continue + + resource_list: List[JsonDict] = [] + # 托盘本体 + resource_list.append({ + "layout_code": f"{tray_layout}:-1", + "resource_type": str(tray_code_int), + }) + + # 分支 A: 无物质耗材 (Tip头等) + if tray_code_int in no_substance_trays: + qty = self._safe_int(content) or 0 + if qty <= 0: continue + + # 限制最大容量 + spec = self._get_tray_spec(tray_code_int) + cap = (spec[0] * spec[1]) if spec else qty + qty = min(qty, cap) + + res_type = str(consumable_map.get(tray_code_int, tray_code_int)) + for idx in range(qty): + resource_list.append({ + "layout_code": f"{tray_layout}:{idx}", + "resource_type": res_type, + "with_cap": False + }) + + # 分支 B: 有物质容器 (试剂瓶/粉桶) + else: + entries = [seg.strip() for seg in str(content).split(";") if seg.strip()] + media_code, with_cap, amt_kind, def_unit = tray_to_media.get( + tray_code_int, (str(tray_code_int), False, "volume", "mL") + ) + tray_spec = self._get_tray_spec(tray_code_int) + + for seg in entries: + parts = [p.strip() for p in seg.split("|")] + if len(parts) < 3: continue + + slot_raw, substance, amt_str = parts[0], parts[1], parts[2] + + slot_idx = self._safe_int(slot_raw) + if slot_idx is None: + slot_idx = self._well_to_slot_index(slot_raw, tray_spec) + if slot_idx is None: continue + + val, unit = self._split_amount_unit(amt_str) + if not unit: unit = def_unit + + fid = _resolve_fid(substance) + + media_item = { + "layout_code": f"{tray_layout}:{slot_idx}", + "resource_type": media_code, + "with_cap": with_cap, + "substance": substance, + "unit": unit, + "chemical_id": fid + } + + if amt_kind == "volume": + media_item["initial_volume"] = val + else: + media_item["initial_weight"] = val + + resource_list.append(media_item) + + resource_req_list.append({ + "remark": "", + "resource_list": resource_list + }) + + self._logger.info("已生成上料 Payload, 包含 %s 个托盘", len(resource_req_list)) + return resource_req_list + + # ---------- 下料函数 ---------- + def batch_out_tray(self, layout_codes: List[str], move_type: str = "main_out") -> JsonDict: + """ + 功能: + 批量下料, 接收托盘位置编码列表并调用出料接口. + 参数: + layout_codes: List[str], 资源位置编码列表, 如 ["N-4", "N-5"]. + move_type: 下料方式, 默认 "main_out". + 返回: + Dict, 接口响应. + """ + if layout_codes is None: + raise ValidationError("layout_codes 不能为空") + if len(layout_codes) == 0: + raise ValidationError("layout_codes 不能为空") + + layout_list: List[JsonDict] = [] + for code in layout_codes: + if code is None: + continue + text = str(code).strip() + if text == "": + continue + layout_list.append({"layout_code": text}) # 构造 API 需要的格式 + + if len(layout_list) == 0: + raise ValidationError("layout_codes 解析后为空") + + resp = self._call_with_relogin(self._client.batch_out_tray, layout_list, move_type) + self._assert_success(resp, "批量下料") + self._logger.info("批量下料开始执行") + return resp + + # ---------- 清空站内资源(慎用) ---------- + def clear_tray_shelf(self) -> JsonDict: + """ + 功能: + 清空站内托盘货架. + 参数: + 无. + 返回: + Dict, 接口响应. + """ + return self._call_with_relogin(self._client.clear_tray_shelf) + + # ---------- 开关外舱门 ---------- + def open_close_door(self, op: str, *, station: str = "FSY", door_num: int = 0) -> JsonDict: + """ + 功能: + 打开/关闭过渡舱门 + 参数: + op: "open" 或 "close". + station: 站点编码,默认 "FSY". + door_num: 门编号,默认 0. + 返回: + Dict, 接口响应. + """ + return self._call_with_relogin(self._client.open_close_door, station, op, door_num) + + # ---------- 任务模块 ---------- 未完成 + def add_task(self, payload: JsonDict) -> JsonDict: + return self._call_with_relogin(self._client.add_task, payload) + + def start_task(self, task_id: int) -> JsonDict: + return self._call_with_relogin(self._client.start_task, task_id) + + def stop_task(self, task_id: int) -> JsonDict: + return self._call_with_relogin(self._client.stop_task, task_id) + + def cancel_task(self, task_id: int) -> JsonDict: + return self._call_with_relogin(self._client.cancel_task, task_id) + + def delete_task(self, task_id: int) -> JsonDict: + return self._call_with_relogin(self._client.delete_task, task_id) + + def get_task_info(self, task_id: int) -> JsonDict: + return self._call_with_relogin(self._client.get_task_info, task_id) + + def get_task_list( + self, + *, + sort: str = "desc", + offset: int = 0, + limit: int = 20, + status: Optional[List[int]] = None, + ) -> JsonDict: + """ + 功能: + 获取任务列表, 对应 GetTaskList + 参数: + sort: 排序方式, 默认按创建时间倒序 + offset: 数据起点 + limit: 数据限制 + status: 任务状态列表, 例如 [0, 1] + 返回: + Dict, 接口响应 + """ + body: JsonDict = { + "sort": sort, + "offset": offset, + "limit": limit, + } + if status is not None: + body["status"] = status # 传递状态过滤 + return self._call_with_relogin(self._client.get_task_list, body) + + def _extract_task_sums(self, resp: JsonDict) -> Optional[int]: + """ + 功能: + 从任务列表响应中提取 task_sums 总数 + 参数: + resp: 任务列表接口响应 + 返回: + Optional[int], 任务总数 + """ + if "task_sums" in resp and isinstance(resp.get("task_sums"), int): + return int(resp["task_sums"]) + for outer in ("result", "data"): + outer_obj = resp.get(outer) + if isinstance(outer_obj, dict) and isinstance(outer_obj.get("task_sums"), int): + return int(outer_obj["task_sums"]) + return None + + def get_all_tasks(self) -> JsonDict: + """ + 功能: + 获取全部任务列表, 先用一次 GetTaskList 读取 task_sums, 再用 limit 拉全量 + 参数: + 无 + 返回: + Dict, 包含完整任务列表 + """ + first_resp = self.get_task_list(limit=1, offset=0, sort="desc") + task_sums = self._extract_task_sums(first_resp) + if task_sums is None: + raise ValidationError(f"GetTaskList 未返回 task_sums, resp={first_resp}") + self._logger.info("开始获取全部任务列表, total=%s", task_sums) # 记录预期条数 + return self.get_task_list(limit=task_sums, offset=0, sort="desc") + + def _extract_task_status(self, task_info: JsonDict) -> Optional[int]: + """ + 功能: + 从 GetTaskInfo 返回中提取 status, 兼容不同字段层级。 + 参数: + task_info: 任务详情响应. + 返回: + Optional[int], 解析出的状态码. + """ + if "status" in task_info and isinstance(task_info.get("status"), int): + return int(task_info["status"]) + + for key in ("result", "data"): + obj = task_info.get(key) + if isinstance(obj, dict) and isinstance(obj.get("status"), int): + return int(obj["status"]) + + return None + + def wait_task( + self, + task_id: int, + *, + timeout_s: float = 3600.0, + poll_interval_s: float = 2.0, + done_status: Optional[List[int]] = None, + fail_status: Optional[List[int]] = None, + ) -> int: + """ + 功能: + 轮询任务状态直到结束或超时。 + 参数: + task_id: 任务 id. + timeout_s: 超时秒数. + poll_interval_s: 轮询间隔秒数. + done_status: 视为完成的状态集合, 默认 [COMPLETED]. + fail_status: 视为失败的状态集合, 默认 [FAILED, STOPPED]. + 返回: + int, 最终 status. + """ + done = done_status or [int(TaskStatus.COMPLETED)] + fail = fail_status or [int(TaskStatus.FAILED), int(TaskStatus.STOPPED)] + + start_ts = time.time() + last_status = None + + while True: + info = self.get_task_info(task_id) + status = self._extract_task_status(info) + + if status is not None and status != last_status: + self._logger.info("任务状态变化 task_id=%s, status=%s", task_id, status) + last_status = status + + if status in done: + return int(status) + if status in fail: + return int(status) + + if time.time() - start_ts > timeout_s: + raise TimeoutError(f"任务超时, task_id={task_id}, last_status={status}") + + time.sleep(poll_interval_s) + + def create_and_start_task(self, payload: JsonDict) -> int: + """ + 28 + 功能: + 创建任务并启动, 返回 task_id. + 参数: + payload: AddTask 请求体. + 返回: + int, task_id. + """ + resp = self.add_task(payload) + task_id = resp.get("task_id") or resp.get("result", {}).get("task_id") + if not isinstance(task_id, int): + raise ValidationError(f"AddTask 未返回 task_id, resp={resp}") + self.start_task(int(task_id)) + return int(task_id) + + # ---------- 任务生成 (Excel/CSV 模板) ---------- + def build_task_payload(self, params: Dict[str, Any], headers: List[str], + data_rows: List[List[Any]], chemical_db: Dict[str, Any]) -> JsonDict: + """ + 功能:3333333322222222222222222222222210. + 将结构化的实验数据转换为 AddTask API Payload + 参数: + params: 实验全局参数 (反应时间、温度等) + headers: 实验数据表头列表 + data_rows: 实验数据行 (每行为值列表) + chemical_db: 化学品信息字典 + 返回: + Dict: AddTask 请求体 + """ + # 1. 参数预处理 + try: + weighing_error_pct = float(str(params.get("称量误差(%)", 1)).replace('%', '')) + except: + weighing_error_pct = 1.0 + + try: + max_error_mg = float(str(params.get("最大称量误差(mg)", 1)).strip()) + except: + max_error_mg = 1.0 + + auto_magnet = str(params.get("自动加磁子", "是")).strip() == "是" + fixed_order = str(params.get("固定加料顺序", "否")).strip() == "是" + exp_count = len(data_rows) + + if exp_count not in [12, 24, 36, 48]: + # 这里的限制取决于硬件,暂时警告 + self._logger.warning(f"实验数量 {exp_count} 非标准 (12/24/36/48)") + + # 2. 全局列分析 (Global Mapping) - 不依赖 Pandas + # col_metadata 结构: {col_idx, type, max_vol, name, is_reagent_group} + col_metadata = [] + col_idx = 0 + + while col_idx < len(headers): + header = headers[col_idx] + + if "试剂" in header: + # 这一组是 (试剂名称, 试剂量) + # 扫描该列所有行确定类型 + is_liquid, is_solid, is_magnet_manual = False, False, False + max_vol = 0.0 + + for row in data_rows: + if col_idx >= len(row): continue + c_name = str(row[col_idx]).strip() + if not c_name or c_name == "0": continue + + if c_name == "加磁子": + is_magnet_manual = True + elif c_name in chemical_db: + state = chemical_db[c_name].get('physical_state', '').lower() + if 'liquid' in state: is_liquid = True + if 'solid' in state: is_solid = True + + # 计算最大体积用于排序 + if 'liquid' in state and (col_idx + 1 < len(row)): + amt_val, amt_unit = self._split_amount_unit(str(row[col_idx+1])) + vol_ml = amt_val if amt_unit in ['ml', 'mL'] else amt_val / 1000.0 + if vol_ml > max_vol: max_vol = vol_ml + + final_type = 'other' + if is_magnet_manual: final_type = 'magnet_manual' + elif is_liquid: final_type = 'liquid' + elif is_solid: final_type = 'solid' + + col_metadata.append({ + "col_idx": col_idx, + "type": final_type, + "max_vol": max_vol, + "is_reagent_group": True + }) + col_idx += 2 # 跳过 Amount 列 + + elif "加磁子" in header: + col_metadata.append({ + "col_idx": col_idx, + "type": "magnet_manual", + "max_vol": 0, + "is_reagent_group": False + }) + col_idx += 1 + else: + col_idx += 1 + + # 3. 确定执行顺序 + VIRTUAL_MAGNET_COL_IDX = -999 + ordered_cols = [] + + if not fixed_order: + # 排序策略: 固体 -> 自动磁子 -> 手动磁子 -> 液体(体积降序) -> 其他 + solids = [c for c in col_metadata if c['type'] == 'solid'] + manual_magnets = [c for c in col_metadata if c['type'] == 'magnet_manual'] + liquids = [c for c in col_metadata if c['type'] == 'liquid'] + liquids.sort(key=lambda x: x['max_vol'], reverse=True) + others = [c for c in col_metadata if c['type'] not in ['solid', 'liquid', 'magnet_manual']] + + ordered_cols.extend(solids) + if auto_magnet: + ordered_cols.append({"col_idx": VIRTUAL_MAGNET_COL_IDX, "type": "magnet_auto"}) + ordered_cols.extend(manual_magnets) + ordered_cols.extend(liquids) + ordered_cols.extend(others) + else: + # 固定顺序策略: 保持原序,首个液体前插磁子 + inserted_magnet = False + for c in col_metadata: + if auto_magnet and not inserted_magnet and c['type'] == 'liquid': + ordered_cols.append({"col_idx": VIRTUAL_MAGNET_COL_IDX, "type": "magnet_auto"}) + inserted_magnet = True + ordered_cols.append(c) + if auto_magnet and not inserted_magnet: + ordered_cols.append({"col_idx": VIRTUAL_MAGNET_COL_IDX, "type": "magnet_auto"}) + + # 4. 行号映射 + col_to_row_map = {} + curr_row = 0 + for item in ordered_cols: + col_to_row_map[item['col_idx']] = curr_row + curr_row += 1 + + # 固定操作行号 + ROW_IDX_REACTION = curr_row + 1 + ROW_IDX_INT_STD = curr_row + 2 + ROW_IDX_STIR_AFTER = curr_row + 3 + ROW_IDX_FILTER = curr_row + 4 + + # 5. 生成 Layout List + layout_list = [] + common_fields = { + "layout_code": "", "src_layout_code": "", "resource_type": "551000502", + "status": 0, "tray_QR_code": "", "QR_code": "" + } + + for exp_idx, row_vals in enumerate(data_rows): + unit_column = exp_idx + + # 遍历排好序的列定义 + for item in ordered_cols: + c_idx = item['col_idx'] + target_row = col_to_row_map[c_idx] + + # A. 自动加磁子 + if c_idx == VIRTUAL_MAGNET_COL_IDX: + has_explicit = False + for val in row_vals: + if str(val).strip() == "加磁子": + has_explicit = True + break + if not has_explicit: + self._add_unit_magnet(layout_list, common_fields, unit_column, target_row) + continue + + # B. 真实数据列 + if c_idx >= len(row_vals): continue + val_name = str(row_vals[c_idx]).strip() + + if not val_name or val_name == "0": continue + + if val_name == "加磁子": + self._add_unit_magnet(layout_list, common_fields, unit_column, target_row) + continue + + if val_name not in chemical_db: + raise ValidationError(f"实验 {exp_idx+1}: 未知化学品 '{val_name}'") + + chem_info = chemical_db[val_name] + + if item.get('is_reagent_group'): + # 获取 Amount + val_amt_str = str(row_vals[c_idx+1]) if (c_idx+1 < len(row_vals)) else "0" + amt_val, amt_unit = self._split_amount_unit(val_amt_str) + + if amt_val > 0: + self._add_reagent_unit( + layout_list, common_fields, unit_column, target_row, + val_name, chem_info, amt_val, amt_unit, weighing_error_pct,max_error_mg + ) + + # C. 后续固定操作 + # 反应 + self._add_reaction_unit(layout_list, common_fields, unit_column, ROW_IDX_REACTION, params) + + # 内标 + std_name = str(params.get("内标种类", "")).strip() + if std_name: + self._add_internal_std_unit( + layout_list, common_fields, unit_column, ROW_IDX_INT_STD, + std_name, chemical_db, params, weighing_error_pct,max_error_mg + ) + + # 搅拌 + stir_t = str(params.get("加入内标后搅拌时间(min)", "")).strip() + if stir_t: + self._add_stir_unit( + layout_list, common_fields, unit_column, ROW_IDX_STIR_AFTER, + float(stir_t), params + ) + + # 过滤 + dil_name = str(params.get("稀释液种类", "")).strip() + if dil_name: + self._add_filter_unit( + layout_list, common_fields, unit_column, ROW_IDX_FILTER, + dil_name, chemical_db, params + ) + + # 6. 组装 Payload + return { + "task_id": 0, + "task_name": str(params.get("实验名称", "AutoTask")), + "layout_list": layout_list, + "task_setup": { + "subtype": None, + "experiment_num": exp_count, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": False + } + + # ---------- 任务生成辅助函数:添加各类 Unit---------- + def _add_reagent_unit(self, layout_list: List[Dict], common_fields: Dict, col: int, row: int, + name: str, chem_info: Dict, amt_val: float, amt_unit: str, error_pct: float, max_error_mg: float): + """功能: 添加加粉或加液操作""" + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_column": col, "unit_row": row, "unit_id": f"unit-{uuid.uuid4().hex[:8]}" + }) + + state = chem_info.get('physical_state', 'unknown').lower() + mw = float(chem_info.get('molecular_weight', 0) or 0) + density = float(chem_info.get('density (g/mL)', 0) or 0) + + if 'solid' in state: + target_mg = 0.0 + if amt_unit.lower() == 'mmol': target_mg = amt_val * mw + elif amt_unit.lower() == 'g': target_mg = amt_val * 1000.0 + elif amt_unit.lower() == 'mg': target_mg = amt_val + + calc_offset = target_mg * (error_pct / 100.0) + final_offset = max(0.1, min(calc_offset, max_error_mg)) + + unit_dict.update({ + "unit_type": "exp_add_powder", + "process_json": { + "offset": round(final_offset, 1), + "custom": {"unit": "mg", "unitOptions": ["mg", "g"]}, + "substance": name, + "chemical_id": chem_info['chemical_id'], + "add_weight": round(target_mg, 1) + } + }) + + elif 'liquid' in state: + target_vol_ml = 0.0 + if amt_unit.lower() == 'mmol': + mass_mg = amt_val * mw + if density > 0: target_vol_ml = (mass_mg / 1000.0) / density + elif amt_unit.lower() == 'ml': target_vol_ml = amt_val + elif amt_unit.lower() == 'ul': target_vol_ml = amt_val / 1000.0 + + unit_dict.update({ + "unit_type": "exp_pipetting", + "process_json": { + "custom": {"unit": "mL","unitOptions": ["mL","µL","L"]}, + "substance": name, + "chemical_id": chem_info['chemical_id'], + "add_volume": round(target_vol_ml,3) + } + }) + layout_list.append(unit_dict) + + def _add_unit_magnet(self, layout_list, common_fields, col, row): + """功能: 添加加磁子操作""" + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_type": "exp_add_magnet", + "unit_column": col, "unit_row": row, "unit_id": f"unit-{uuid.uuid4().hex[:8]}", + "process_json": {"custom": {"unit": ""}} + }) + layout_list.append(unit_dict) + + def _add_reaction_unit(self, layout_list: List[JsonDict], common_fields: JsonDict, col: int, row: int, params: JsonDict) -> None: + """ + 功能: + 添加反应操作单元 (Unit), 处理温度与加热状态. + 参数: + layout_list: List[JsonDict], 任务布局列表. + common_fields: JsonDict, 通用字段模板. + col: int, 单元所在列号. + row: int, 单元所在行号. + params: JsonDict, 实验参数字典. + 返回: + 无. + """ + rxn_temp_raw = params.get("反应温度(°C)") + + # Determine target temperature from params + tgt_temp_raw = None + for key in params.keys(): + if "搅拌后" in str(key) and "温度" in str(key): + tgt_temp_raw = params[key] + break + + rxn_time_h = float(params.get("反应时间(h)", 0)) + rxn_rpm = int(params.get("转速(rpm)", 0)) + is_wait = str(params.get("等待目标温度", "否")) == "是" + + process_data = { + "rotation_speed": rxn_rpm, + "reaction_duration": int(rxn_time_h * 3600), + "is_wait": is_wait, + "custom": {"unit": ""} + } + + # 1. Reaction Temperature logic: Replace pd.isna with None/Empty check + if rxn_temp_raw is None or str(rxn_temp_raw).strip() == "": + process_data["temperature"] = 25 + else: + process_data["temperature"] = float(rxn_temp_raw) + + # 2. Target Temperature logic: Replace pd.isna with None/Empty check + if tgt_temp_raw is None or str(tgt_temp_raw).strip() == "": + process_data["is_heating"] = False + else: + try: + target_t = float(tgt_temp_raw) + process_data["is_heating"] = True + process_data["target_temperature"] = target_t + except ValueError: + process_data["is_heating"] = False + + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_type": "exp_magnetic_stirrer", + "unit_column": col, + "unit_row": row, + "unit_id": f"unit-{uuid.uuid4().hex[:8]}", + "process_json": process_data + }) + layout_list.append(unit_dict) + + def _add_internal_std_unit(self, layout_list: List[JsonDict], common_fields: JsonDict, col: int, row: int, name: str, db: Dict[str, Any], params: JsonDict, error_pct: float, max_error_mg: float) -> None: + """ + 功能: + 添加内标加料单元. + 参数: + layout_list: List[JsonDict], 任务布局列表. + common_fields: JsonDict, 通用字段模板. + col/row: int, 坐标. + name: str, 内标物质名称. + db: Dict, 化学品数据库. + params: JsonDict, 参数. + error_pct: float, 允许误差百分比. + 返回: + 无. + """ + if name not in db: + return + + chem_info = db[name] + state = chem_info.get('physical_state', 'unknown').lower() + chem_id = chem_info['chemical_id'] + + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_column": col, + "unit_row": row, + "unit_id": f"unit-{uuid.uuid4().hex[:8]}" + }) + + if 'solid' in state: + target_mg = float(params.get("内标用量(μL/mg)", 10.0)) + calc_offset = target_mg * (error_pct / 100.0) + final_offset = max(0.1, min(calc_offset, min(calc_offset, max_error_mg))) + + unit_dict.update({ + "unit_type": "exp_add_powder", + "process_json": { + "offset": round(final_offset, 1), + "custom": {"unit": "mg", "unitOptions": ["mg", "g"]}, + "substance": name, + "chemical_id": chem_id, + "add_weight": round(target_mg, 1) + } + }) + elif 'liquid' in state: + target_vol_ml = 0.1 + + # Replacement for pd.notna: Check if key exists and value is not empty + val_ul = params.get("内标用量(μL/mg)") + + if val_ul is not None and str(val_ul).strip() != "": + target_vol_ml = float(val_ul) / 1000.0 + + unit_dict.update({ + "unit_type": "exp_pipetting", + "process_json": { + "custom": {"unit": "mL","unitOptions": ["mL","µL","L"]}, + "substance": name, + "chemical_id": chem_id, + "add_volume": round(target_vol_ml,3) + } + }) + layout_list.append(unit_dict) + + def _add_stir_unit(self, layout_list, common_fields, col, row, time_min, params): + """功能: 添加搅拌""" + rxn_rpm = int(params.get("转速(rpm)", 600)) + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_type": "exp_magnetic_stirrer", + "unit_column": col, "unit_row": row, "unit_id": f"unit-{uuid.uuid4().hex[:8]}", + "process_json": { + "temperature": 25, "rotation_speed": rxn_rpm, "reaction_duration": int(time_min * 60), + "is_wait": False, "is_heating": False, "target_temperature": 25, "custom": {"unit": ""} + } + }) + layout_list.append(unit_dict) + + def _add_filter_unit(self, layout_list, common_fields, col, row, diluent_name, db, params): + """功能: 添加过滤""" + if diluent_name not in db: return + chem_id = db[diluent_name]['chemical_id'] + dilution_vol_ul = float(params.get("稀释量(μL)", 0)) + sample_vol_ul = float(params.get("取样量(μL)", 0)) + + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_type": "exp_filtering_sample", + "unit_column": col, "unit_row": row, "unit_id": f"unit-{uuid.uuid4().hex[:8]}", + "process_json": { + "single_press_num": 6, "substance": diluent_name, "chemical_id": chem_id, + "add_volume": dilution_vol_ul/1000, "sampling_volume": sample_vol_ul/1000 + } + }) + layout_list.append(unit_dict) + + def _parse_amount_string(self, amt_str: Any) -> Tuple[float, str]: + """ + 功能: + 解析 '100mg', '5 mmol' 等字符串, 分离数值与单位. + 参数: + amt_str: Any, 输入的金额字符串或数值. + 返回: + Tuple[float, str], (数值, 单位). + """ + # Replacement for pd.isna: check None or empty string + if amt_str is None: + return 0, "" + + text = str(amt_str).strip().lower() + if text == "" or text == "0": + return 0, "" + + # Regex matching number + unit + match = re.match(r"([0-9.]+)\s*([a-z%]+)", text) + if match: + return float(match.group(1)), match.group(2) + + try: + return float(text), "unknown" + except Exception: + return 0, "error" + + # ---------- 消息通知与故障恢复 ---------- + def notice(self, types: Optional[List[int]] = None) -> JsonDict: + return self._call_with_relogin(self._client.notice, types) + + def fault_recovery( + self, + *, + ids: Optional[List[int]] = None, + recovery_type: int = 0, + resume_task: int = 1, + ) -> JsonDict: + return self._call_with_relogin( + self._client.fault_recovery, + ids=ids, + recovery_type=recovery_type, + resume_task=resume_task, + ) + + # ---------- 方法模块 ---------- 未完成 + def create_method(self, payload: JsonDict) -> JsonDict: + return self._call_with_relogin(self._client.create_method, payload) + + def update_method(self, task_template_id: int, payload: JsonDict) -> JsonDict: + return self._call_with_relogin(self._client.update_method, task_template_id, payload) + + def delete_method(self, task_template_id: int) -> JsonDict: + return self._call_with_relogin(self._client.delete_method, task_template_id) + + def get_method_detail(self, task_template_id: int) -> JsonDict: + return self._call_with_relogin(self._client.get_method_detail, task_template_id) + + def get_method_list(self, *, limit: int = 20, offset: int = 0, sort: str = "desc") -> JsonDict: + return self._call_with_relogin(self._client.get_method_list, limit=limit, offset=offset, sort=sort) + + def get_latest_method_detail(self) -> JsonDict: + """ + 功能: + 获取最近一个方法详情, 通过 list(limit=1) 再 detail 的方式实现。 + 参数: + 无. + 返回: + Dict, 方法详情. + """ + lst = self.get_method_list(limit=1, offset=0, sort="desc") + data = lst.get("result") or lst.get("data") or lst + items = data.get("list") if isinstance(data, dict) else None + if not items: + raise ValidationError(f"方法列表为空, resp={lst}") + tid = items[0].get("task_template_id") + if not isinstance(tid, int): + raise ValidationError(f"无法解析 task_template_id, item={items[0]}") + return self.get_method_detail(int(tid)) + +if __name__ == "__main__": + + settings = Settings.from_env() + configure_logging(settings.log_level) + logger = logging.getLogger("station_controller.main") + + controller = SynthesisStationController() + + # #设备输初始化 + # controller.device_init() + + #获取资源列表 + resource_info = controller.get_resource_info() + out_path = Path("resource_info.json") + out_path.write_text(json.dumps(resource_info, ensure_ascii=False, indent=2), encoding="utf-8") + + # #获取工站内设备所有信息 + # device_info = controller.list_device_status() + # print(device_info) + + # #获取工站化合物库信息: + # chemical_info = controller.get_all_chemical_list() + # print(chemical_info) + # output_csv = controller.export_chemical_list_to_csv(chemical_info, Path("station_chemical_list.csv")) + + # #从csv文件中新增化合物 + # controller.sync_chemicals_from_csv(Path("add_chemical_list.csv"), overwrite=False) + + # 删除特定ID的化合物 + # controller.delete_chemical(363) + + # #获取手套箱内气体氛围的情况 + # device_info = controller.get_glovebox_env() + # print(device_info) + + # #批量下料测试 + # out_resp = controller.batch_out_tray(["N-4", "W-2-1","W-2-5","W-3-2"]) + + # # 批量上料测试 + # resource_req_list = controller.build_batch_in_tray_payload_from_sheet("batch_in_tray.xlsx") + # out_path = Path("resource_req_list.json") + # out_path.write_text(json.dumps(resource_req_list, ensure_ascii=False, indent=2), encoding="utf-8") + # result = controller.batch_in_tray(resource_req_list) + + #————————————————————————化合物库对齐———————————————————————— + + # #检查化学品列表的合理性 + # controller.check_chemical_list_file("chemical_list.xlsx") + + # # 对齐化合物库和站内化学品列表 + # controller.align_chemicals_from_file("chemical_list.xlsx") + + #————————————————————————创建任务———————————————————————— + + # #获取所有任务信息 + # result = controller.get_all_tasks() + # out_path = Path("task_list.json") + # out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") + + # #从表格中创建任务 + # result = controller.create_task_from_template("reeaction_template.xlsx") + # result2 = controller.add_task(result) + # print(result2) + # # controller.delete_task(571) + + # out_path = Path("reaction_template.json") + # out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8") + + + diff --git a/unilabos/devices/workstation/eit_synthesis_station/driver/__init.py b/unilabos/devices/workstation/eit_synthesis_station/driver/__init.py new file mode 100644 index 00000000..b77438f2 --- /dev/null +++ b/unilabos/devices/workstation/eit_synthesis_station/driver/__init.py @@ -0,0 +1,27 @@ +""" +功能: + core 层导出. +""" +from .api_client import ApiClient +from .exceptions import ( + DriverError, + ConfigError, + RequestError, + ResponseError, + ApiError, + AuthenticationError, + AuthorizationExpiredError, + ValidationError, +) + +__all__ = [ + "ApiClient", + "DriverError", + "ConfigError", + "RequestError", + "ResponseError", + "ApiError", + "AuthenticationError", + "AuthorizationExpiredError", + "ValidationError", +] diff --git a/unilabos/devices/workstation/eit_synthesis_station/driver/api_client.py b/unilabos/devices/workstation/eit_synthesis_station/driver/api_client.py new file mode 100644 index 00000000..2feb5b41 --- /dev/null +++ b/unilabos/devices/workstation/eit_synthesis_station/driver/api_client.py @@ -0,0 +1,615 @@ +import json +import logging +from typing import Any, Dict, List, Optional, Union + +import requests + +from config.setting import Settings, configure_logging +from .exceptions import ( + ApiError, + AuthenticationError, + AuthorizationExpiredError, + ConfigError, + RequestError, + ResponseError, + ValidationError, +) + +JsonDict = Dict[str, Any] + + +class ApiClient: + """ + 功能: + 【底层驱动】封装 31 个基础 API 调用, 只做 HTTP 与错误处理, 不做业务流程. + """ + + def __init__(self, settings: Settings): + if not settings.base_url: + raise ConfigError("base_url 不能为空.") + + self._settings = settings + self._session = requests.Session() + self._logger = logging.getLogger(self.__class__.__name__) + + self._token_type: Optional[str] = None + self._access_token: Optional[str] = None + + @property + def access_token(self) -> Optional[str]: + return self._access_token + + def set_token(self, token_type: str, access_token: str) -> None: + """ + 功能: + 设置鉴权 token, 后续请求会自动带 Authorization 头. + 参数: + token_type: 例如 "Bearer". + access_token: token 字符串. + 返回: + 无. + """ + self._token_type = token_type + self._access_token = access_token + + def clear_token(self) -> None: + """ + 功能: + 清理本地缓存的 token, 用于主动退出或异常恢复 + 参数: + 无 + 返回: + None + """ + self._token_type = None + self._access_token = None + + def _url(self, path: str) -> str: + """ + 功能: + 拼接基础地址与路径, 生成完整请求 URL + 参数: + path: str, 目标接口路径, 允许带或不带前导斜杠 + 返回: + str, 完整的可访问 URL + """ + base = self._settings.base_url.rstrip("/") + p = path if path.startswith("/") else f"/{path}" + return f"{base}{p}" + + def _mask_sensitive(self, obj: Any) -> Any: + """ + 功能: + 遍历对象并掩码敏感字段, 用于日志输出时避免泄露 + 参数: + obj: Any, 待处理的字典或列表对象 + 返回: + Any, 已对敏感字段替换为 *** 的同结构对象 + """ + if isinstance(obj, dict): + masked = {} + for k, v in obj.items(): + if str(k).lower() in ("password", "access_token", "authorization"): + masked[k] = "***" + else: + masked[k] = self._mask_sensitive(v) + return masked + if isinstance(obj, list): + return [self._mask_sensitive(x) for x in obj] + return obj + + def _request( + self, + method: str, + path: str, + *, + json_body: Optional[JsonDict] = None, + params: Optional[JsonDict] = None, + timeout_s: Optional[float] = None, + ) -> JsonDict: + """ + 功能: + 统一封装 HTTP 请求, 处理认证、超时与业务异常 + 参数: + method: str, HTTP 方法名称, 例如 GET 或 POST + path: str, 接口路径, 允许带或不带前导斜杠 + json_body: Optional[JsonDict], 请求 JSON 体, 默认为 None + params: Optional[JsonDict], 查询参数, 默认为 None + timeout_s: Optional[float], 覆盖默认超时时间, 默认为 None 表示使用配置 + 返回: + JsonDict, 成功时的响应数据, 若响应为非字典则包装为 {"result": data} + """ + url = self._url(path) + timeout = timeout_s if timeout_s is not None else self._settings.timeout_s + + headers: Dict[str, str] = { + "Content-Type": "application/json", + } + if self._access_token: + # 附加授权头, 兼容缺省的 Bearer 类型 + token_type = self._token_type or "Bearer" + headers["Authorization"] = f"{token_type} {self._access_token}" + + self._logger.debug( + "HTTP request, method=%s, url=%s, params=%s, json=%s", + method, + url, + self._mask_sensitive(params or {}), + self._mask_sensitive(json_body or {}), + ) + + try: + resp = self._session.request( + method=method.upper(), + url=url, + headers=headers, + params=params, + json=json_body, + timeout=timeout, + verify=self._settings.verify_ssl, + ) + except requests.Timeout as e: + # 超时统一转为业务异常, 便于上层捕获 + raise RequestError(f"请求超时, url={url}") from e + except requests.RequestException as e: + # 其他请求异常统一包装 + raise RequestError(f"请求失败, url={url}, err={e}") from e + + if resp.status_code == 401: + # 登录失效明确提示重新登录 + raise AuthorizationExpiredError("登录失效(401), 请重新登录.") #失效登陆反馈 + if resp.status_code >= 400: + # 其他 HTTP 错误统一抛出请求异常 + raise RequestError( + f"HTTP错误, status={resp.status_code}, url={url}, body={resp.text}", + status_code=resp.status_code, + ) + + try: + data = resp.json() + except json.JSONDecodeError as e: + # 返回体非 JSON 时抛出响应异常 + raise ResponseError(f"响应非JSON, url={url}, body={resp.text}") from e + + self._logger.debug("HTTP response, url=%s, json=%s", url, self._mask_sensitive(data)) + + if isinstance(data, dict) and "code" in data: + code = data.get("code") + if code != 200: + raise ApiError(code=code, msg=str(data.get("msg", "")), payload=data) + + return data if isinstance(data, dict) else {"result": data} + + # 1. 登录 + def login(self, username: str, password: str) -> JsonDict: + """ + 功能: + 登录获取 token. + 参数: + username: 用户名. + password: 密码. + 返回: + Dict, 包含 access_token, token_type. + """ + if not username or not password: + raise ValidationError("username 与 password 不能为空.") + data = self._request("POST", "/api/Token", json_body={"username": username, "password": password}) + if "access_token" not in data: + raise AuthenticationError(f"登录失败, 响应缺少 access_token, resp={data}") + return data + + # 2. 设备初始化 + def device_init(self, device_id: Optional[List[str]] = None) -> JsonDict: + """ + 功能: + 设备初始化(复位, 状态检测等), 支持全站或指定设备. + 参数: + device_id: 设备 id 列表, None 表示全站初始化. + 返回: + Dict. + """ + body: JsonDict = {} + if device_id: + body["device_id"] = device_id + print(body) + return self._request("POST", "/api/DeviceInit", json_body=body) + + # 3. 获取资源 + def get_resource_info(self, filters: Optional[JsonDict] = None) -> JsonDict: + """ + 功能: + 获取资源详情, 参数为空则获取单站所有资源. + 参数: + filters: 可选过滤字段, 不确定字段时可传 {}. + 返回: + Dict. + """ + return self._request("POST", "/api/GetResourceInfo", json_body=filters or {}) + + # 4. 进料 + def in_tray(self, tray_qr_code: str, resource_list: List[JsonDict]) -> JsonDict: + """ + 功能: + 进料, 单盘进料. + 参数: + tray_qr_code: 托盘二维码. + resource_list: 资源列表. + 返回: + Dict. + """ + body = {"tray_QR_code": tray_qr_code, "resource_list": resource_list} + return self._request("POST", "/api/InTray", json_body=body) + + # 5. 批量进料 + def batch_in_tray(self, resource_req_list: List[JsonDict]) -> JsonDict: + """ + 功能: + 批量进料, 支持多托盘. + 参数: + resource_req_list: 每个托盘的进料请求列表. + 返回: + Dict. + """ + return self._request("POST", "/api/BatchInTray", json_body={"resource_req_list": resource_req_list}) + + # 6. 下料 + def out_tray(self, layout_list: List[JsonDict]) -> JsonDict: + """ + 功能: + 出料, 目前只支持整盘出料. + 参数: + layout_list: 资源列表, layout_code 支持 "All" 表示清空工站. + 返回: + Dict. + """ + return self._request("POST", "/api/OutTray", json_body={"layout_list": layout_list}) + + # 7. 批量下料 + def batch_out_tray(self, layout_list: List[JsonDict], move_type: str) -> JsonDict: + """ + 功能: + 批量下料, 对应 BatchOutTray. + 参数: + layout_list: 资源列表. + move_type: 下料方式, 例如 "main_out". + 返回: + Dict. + """ + body = {"layout_list": layout_list, "move_type": move_type} + return self._request("POST", "/api/BatchOutTray", json_body=body) + + # 8. 获取化学品 + def get_chemical_list( + self, + *, + query_key: Optional[str] = None, + sort: str = "desc", + offset: int = 0, + limit: int = 20, + ) -> JsonDict: + """ + 功能: + 获取化学品库列表, 对应 getChemicalList. + 参数: + query_key: 查询字符串. + sort: asc 或 desc. + offset: 数据起点. + limit: 数据限制. + 返回: + Dict. + """ + params: JsonDict = {"sort": sort, "offset": offset, "limit": limit} + if query_key: + params["query_key"] = query_key + return self._request("GET", "/api/v1/knowledge/getChemicalList", params=params) + + # 9. 新增化学品 + def add_chemical(self, payload: JsonDict) -> JsonDict: + """ + 功能: + 新增单个化学品, 对应 addChemical. + 参数: + payload: 化学品字段集合. + 返回: + Dict. + """ + return self._request("POST", "/api/v1/knowledge/addChemical", json_body=payload) + + # 10. 编辑化学品 + def update_chemical(self, payload: JsonDict) -> JsonDict: + """ + 功能: + 修改化学品信息, 对应 updateChemical. + 参数: + payload: 至少包含 fid, 其余字段参考新增化学品. + 返回: + Dict. + """ + return self._request("POST", "/api/v1/knowledge/updateChemical", json_body=payload) + + # 11. 删除化学品 + def delete_chemical(self, chemical_id: int) -> JsonDict: + """ + 功能: + 根据 id 删除化学品, 对应 deleteChemical. + 参数: + chemical_id: 化学品 id. + 返回: + Dict. + """ + return self._request("POST", "/api/v1/knowledge/deleteChemical", params={"chemical_id": chemical_id}) + + # 12. 新建方法 + def create_method(self, payload: JsonDict) -> JsonDict: + """ + 功能: + 新建方法, 对应 task_templates POST. + 参数: + payload: 方法字段集合, 例如 task_template_name, unit_save_json 等. + 返回: + Dict. + """ + return self._request("POST", "/api/v1/task_templates", json_body=payload) + + # 13. 编辑方法 + def update_method(self, task_template_id: int, payload: JsonDict) -> JsonDict: + """ + 功能: + 编辑某个方法, 对应 task_templates PUT. + 参数: + task_template_id: 方法 id. + payload: 方法字段集合, 参数同新建方法. + 返回: + Dict. + """ + return self._request("PUT", f"/api/v1/task_templates/{task_template_id}", json_body=payload) + + # 14. 删除方法 + def delete_method(self, task_template_id: int) -> JsonDict: + """ + 功能: + 删除某个方法, 对应 delete_template. + 参数: + task_template_id: 方法 id. + 返回: + Dict. + """ + return self._request( + "POST", + "/api/v1/task_templates/delete_template", + json_body={"task_template_id": task_template_id}, + ) + + # 15. 获取单个方法详情 + def get_method_detail(self, task_template_id: int) -> JsonDict: + """ + 功能: + 获取单个方法详情, 对应 task_templates GET. + 参数: + task_template_id: 方法 id. + 返回: + Dict. + """ + return self._request("GET", f"/api/v1/task_templates/{task_template_id}") + + # 16. 获取方法列表 + def get_method_list(self, *, limit: int = 20, offset: int = 0, sort: str = "desc") -> JsonDict: + """ + 功能: + 获取方法列表. + 参数: + limit: 每页数量. + offset: 偏移量. + sort: asc 或 desc. + 返回: + Dict. + """ + return self._request("GET", "/api/v1/task_templates", params={"limit": limit, "offset": offset, "sort": sort}) + + + # 17. 创建任务 + def add_task(self, payload: JsonDict) -> JsonDict: + """ + 功能: + 创建或更新任务, 对应 AddTask. + 参数: + payload: AddTask 完整请求体. + 返回: + Dict. + """ + return self._request("POST", "/api/AddTask", json_body=payload) + + # 18. 启动任务 + def start_task(self, task_id: int) -> JsonDict: + """ + 功能: + 启动任务. + 参数: + task_id: 任务 id. + 返回: + Dict. + """ + return self._request("POST", "/api/StartTask", json_body={"task_id": task_id}) + + # 19. 暂停任务 + def stop_task(self, task_id: int) -> JsonDict: + """ + 功能: + 暂停任务, 对应 StopTask. + 参数: + task_id: 任务 id. + 返回: + Dict. + """ + return self._request("POST", "/api/StopTask", json_body={"task_id": task_id}) + + # 20. 取消任务 + def cancel_task(self, task_id: int) -> JsonDict: + """ + 功能: + 取消任务, 对应 CancelTask. + 参数: + task_id: 任务 id. + 返回: + Dict. + """ + return self._request("POST", "/api/CancelTask", json_body={"task_id": task_id}) + + # 21. 删除任务 + def delete_task(self, task_id: int) -> JsonDict: + """ + 功能: + 删除任务, 对应 DeleteTask. + 参数: + task_id: 任务 id. + 返回: + Dict. + """ + return self._request("POST", "/api/DeleteTask", json_body={"task_id": task_id}) + + # 22. 获取所有任务列表 + def get_task_list(self, payload: Optional[JsonDict] = None) -> JsonDict: + """ + 功能: + 获取任务列表, 对应 GetTaskList. + 参数: + payload: 查询参数, 不确定时可传 {}. + 返回: + Dict. + """ + return self._request("POST", "/api/GetTaskList", json_body=payload or {}) + + # 23. 获取单个任务详情 + def get_task_info(self, task_id: int) -> JsonDict: + """ + 功能: + 获取任务详情, 对应 GetTaskInfo. + 参数: + task_id: 任务 id. + 返回: + Dict. + """ + return self._request("POST", "/api/GetTaskInfo", json_body={"task_id": task_id}) + + # 24. 获取消息通知 + def notice(self, types: Optional[List[int]] = None) -> JsonDict: + """ + 功能: + 获取消息通知, 对应 Notice. + 参数: + types: 可选, 例如 [1, 2] 表示故障与告警, None 表示全部. + 返回: + Dict. + """ + body: JsonDict = {} + if types is not None: + body["type"] = types + return self._request("POST", "/api/Notice", json_body=body) + + # 25. 故障恢复 + def fault_recovery( + self, + *, + ids: Optional[List[int]] = None, + recovery_type: int = 0, + resume_task: int = 1, + ) -> JsonDict: + """ + 功能: + 故障恢复, 取消告警并可恢复任务运行. + 参数: + ids: 告警 id 列表, None 表示清除本机所有通知告警. + recovery_type: 恢复类型, 0..5. + resume_task: 0 仅清除不恢复, 1 清除并恢复, 默认 1. + 返回: + Dict. + """ + body: JsonDict = {"type": recovery_type, "resume_task": resume_task} + if ids is not None: + body["id"] = ids + return self._request("POST", "/api/Notice", json_body=body) + + # 26. 工站设备状态 + def station_state(self) -> JsonDict: + """ + 功能: + 获取工站设备状态. + 参数: + 无. + 返回: + Dict. + """ + return self._request("GET", "/api/station/state") + + # 27. 获取设备模块列 + def list_device_info(self) -> JsonDict: + """ + 功能: + 获取工站设备模块列表. + 参数: + 无. + 返回: + Dict. + """ + return self._request("POST", "/api/ListDeviceInfo", json_body={}) + + # 28. 获取所有设备信息 + def get_all_device_info(self) -> JsonDict: + """ + 功能: + 获取所有设备信息。 + 参数: + 无。 + 返回: + Dict. + """ + return self._request("POST", "/api/getAllDeviceInfo", json_body={}) + + # 29.清空站内所有资源 (ClearTrayShelf) + def clear_tray_shelf(self) -> JsonDict: + """ + 功能: + 清空站内所有资源(需手动移除所有资源)。 + 参数: + 无。 + 返回: + Dict. + """ + return self._request("POST", "/api/ClearTrayShelf", json_body={}) + + # 30.打开/关闭过渡舱外门 (OpenCloseDoor) + def open_close_door(self, station: str, op: str, door_num: int) -> JsonDict: + """ + 功能: + 打开/关闭过渡舱外门。 + 参数: + station: 站点编码, 例如 "FSY". + op: "open" 或 "close". 注意关门会自动置换气体! + door_num: 门编号, 例如 0. + 返回: + Dict. + """ + body = {"op": op, "station": station, "door_num": door_num} + return self._request( + "POST", + "/api/OpenCloseDoor", + json_body=body, + params={"station": station}, + ) + + # 31. 批量查询设备运行状态 + def batch_list_device_runtimes(self, device_code_list: List[str]) -> JsonDict: + """ + 功能: + 批量查询设备运行状态, 对应 BatchListDeviceRuntimes + 参数: + device_code_list: List[str], 设备代码列表, 例如 ["352", "304", "306"] + 返回: + Dict[str, Any], 接口响应 + """ + if not device_code_list: + raise ValidationError("device_code_list 不能为空") + body = {"device_code_list": device_code_list} + return self._request("POST", "/api/BatchListDeviceRuntimes", json_body=body) + + diff --git a/unilabos/devices/workstation/eit_synthesis_station/driver/exceptions.py b/unilabos/devices/workstation/eit_synthesis_station/driver/exceptions.py new file mode 100644 index 00000000..eca0ae6c --- /dev/null +++ b/unilabos/devices/workstation/eit_synthesis_station/driver/exceptions.py @@ -0,0 +1,71 @@ +from typing import Any, Optional + + +class DriverError(Exception): + """ + 功能: + driver 统一异常基类. + """ + + +class ConfigError(DriverError): + """ + 功能: + 配置错误, 例如 base_url 为空. + """ + + +class ValidationError(DriverError): + """ + 功能: + 参数校验错误, 例如缺少必填字段. + """ + + +class RequestError(DriverError): + """ + 功能: + 网络请求错误, 例如连接失败, 超时, HTTP 非 200. + """ + + def __init__(self, message: str, *, status_code: Optional[int] = None): + super().__init__(message) + self.status_code = status_code + + +class ResponseError(DriverError): + """ + 功能: + 响应解析错误, 例如非 JSON, 或结构不符合预期. + """ + + +class ApiError(DriverError): + """ + 功能: + 业务错误, 即 HTTP 成功但 JSON 中 code != 200 或 msg 表示失败. + 参数: + code: 设备返回的业务 code. + msg: 设备返回的 msg. + payload: 原始响应 JSON, 便于排查. + """ + + def __init__(self, code: Any, msg: str, payload: Optional[dict] = None): + super().__init__(f"API error, code={code}, msg={msg}") + self.code = code + self.msg = msg + self.payload = payload or {} + + +class AuthenticationError(DriverError): + """ + 功能: + 登录失败或 token 无效. + """ + + +class AuthorizationExpiredError(AuthenticationError): + """ + 功能: + 登录失效(401), 需要重新登录. + """ diff --git a/unilabos/devices/workstation/eit_synthesis_station/reaction_template.xlsx b/unilabos/devices/workstation/eit_synthesis_station/reaction_template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0e8c775a9c6a2bc29d59c710541400ec23c4b42e GIT binary patch literal 11144 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 w=ed0GTr2=2.31.0 +pandas>=2.0.0 +openpyxl>=3.1.0 diff --git a/unilabos/devices/workstation/eit_synthesis_station/station_manager.py b/unilabos/devices/workstation/eit_synthesis_station/station_manager.py new file mode 100644 index 00000000..4f547bcf --- /dev/null +++ b/unilabos/devices/workstation/eit_synthesis_station/station_manager.py @@ -0,0 +1,738 @@ +import csv +import json +import re +import logging +import pandas as pd +import openpyxl +from openpyxl import Workbook,load_workbook +from openpyxl.worksheet.datavalidation import DataValidation +from openpyxl.styles import Font, Alignment +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple + +# 引入底层的控制器 (假设在同一目录下) +from controller.station_controller import SynthesisStationController +from config.setting import Settings, configure_logging +from config.constants import ResourceCode, TRAY_CODE_DISPLAY_NAME, TraySpec + +from driver.exceptions import ValidationError + +logger = logging.getLogger("StationManager") + +JsonDict = Dict[str, Any] + +class StationManager(SynthesisStationController): + """ + 功能: + 上层面向用户的管理器,继承自 SynthesisStationController。 + 负责处理 CSV/Excel 文件读取、生成模板,将文件内容转换为中间格式(List/Dict), + 然后调用父类方法执行具体的业务逻辑。 + """ + + def __init__(self, settings: Optional[Settings] = None): + settings = settings or Settings.from_env() + configure_logging(settings.log_level) + super().__init__(settings) + + # ---------- 1. 化合物库文件处理 ---------- + def export_chemical_list_to_file(self, output_path: str) -> None: + """ + 功能: + 获取所有化学品并导出到 CSV 文件 + 参数: + output_path: 输出路径 + 返回: + None + """ + path = Path(output_path) + chemical_info = self.get_all_chemical_list() + chemical_list = chemical_info.get("chemical_list", []) + + if not chemical_list: + logger.warning("化学品列表为空,未写入文件") + return + + fieldnames = [ + "fid", "name", "sssi", "cas", "element", "state", + "concentration_str", "chemical_properties", "preparation_method" + ] + + # 确保目录存在 + path.parent.mkdir(parents=True, exist_ok=True) + + with path.open("w", newline="", encoding="utf-8-sig") as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=fieldnames, extrasaction='ignore') + writer.writeheader() + for item in chemical_list: + writer.writerow(item) + + logger.info(f"化学品列表已导出至: {path.resolve()}") + + def sync_chemicals_from_file(self, file_path: str, overwrite: bool = False) -> None: + """ + 功能: + 读取 CSV 文件并通过父类同步化学品到工站 + 参数: + file_path: CSV 文件路径 + overwrite: 是否覆盖更新 + 返回: + None + """ + path = Path(file_path) + if not path.exists(): + # 生成模板 + header = ["name", "cas", "element", "state", "concentration_str", "chemical_properties", "preparation_method"] + with path.open("w", newline="", encoding="utf-8-sig") as f: + csv.writer(f).writerow(header) + logger.warning(f"文件不存在,已生成模板: {path}") + return + + # 读取并清洗数据 + items: List[JsonDict] = [] + with path.open("r", newline="", encoding="utf-8-sig") as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + name = (row.get("name") or "").strip() + state = (row.get("state") or "").strip() + if name and state: + # 过滤空值键 + clean_item = {k: v.strip() for k, v in row.items() if v and str(v).strip()} + items.append(clean_item) + + # 调用父类逻辑处理 + self.sync_chemicals_from_data(items, overwrite=overwrite) + + def check_chemical_library_by_file(self, file_path: str) -> Dict[str, List[str]]: + """ + 功能: + 读取化学品库文件并调用底层校验逻辑,输出校验结果 + 参数: + file_path: str, 化学品库文件路径,支持 Excel/CSV + 返回: + Dict[str, List[str]], 包含 errors 与 warnings + """ + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"未找到化学品库文件: {path}") + + # 读取文件后交给控制层做校验 + df = pd.read_excel(path) if path.suffix.lower() in [".xlsx", ".xls"] else pd.read_csv(path) + df = df.fillna("") + rows = df.to_dict(orient="records") + headers = [str(col).strip() for col in df.columns] + + result = self.check_chemical_library_data(rows, headers) + + for msg in result.get("warnings", []): + logger.warning(msg) + + if len(result.get("errors", [])) > 0: + for msg in result["errors"]: + logger.error(msg) + raise ValidationError("化学品库完整性检查未通过,请修复错误后重试") + + return result + + def deduplicate_chemical_library_by_file(self, file_path: str, output_path: Optional[str] = None) -> List[JsonDict]: + """ + 功能: + 读取化学品库文件,按 substance 自动去重并回写 + 参数: + file_path: str, 输入文件路径,支持 Excel/CSV + output_path: Optional[str], 输出文件路径,默认覆盖原文件 + 返回: + List[Dict[str, Any]], 去重后的数据 + """ + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"未找到化学品库文件: {path}") + + df = pd.read_excel(path) if path.suffix.lower() in [".xlsx", ".xls"] else pd.read_csv(path) + df = df.fillna("") + headers = [str(c).strip() for c in df.columns] + rows = df.to_dict(orient="records") + + dedup_rows = self.deduplicate_chemical_library_data(rows, headers) + + target_path = Path(output_path) if output_path else path + out_df = pd.DataFrame(dedup_rows) + if target_path.suffix.lower() == ".csv": + out_df.to_csv(target_path, index=False, encoding="utf-8-sig") + else: + out_df.to_excel(target_path, index=False) + self._beautify_excel_database(target_path) # 保存后再美化 + + logger.info("化合物库去重完成,输出文件: %s", target_path.resolve()) + return dedup_rows + + def _beautify_excel_database(self, file_path: Path) -> None: + """ + 功能: + 美化去重后的 Excel: 表头加粗、全居中、列宽自适应、按内容选择中英文字体 + 参数: + file_path: Path, 目标 Excel 路径 + 返回: + None + """ + wb = load_workbook(file_path) + ws = wb.active + MAX_WIDTH = 60 # 列宽上限 + + align_center = Alignment(horizontal="center", vertical="center") + + def _is_chinese(text: str) -> bool: + return re.search(r"[\u4e00-\u9fff]", text) is not None + + # 遍历列计算列宽并设置字体/对齐 + for col_cells in ws.iter_cols(): + max_len = 0 + for idx, cell in enumerate(col_cells): + val_str = "" if cell.value is None else str(cell.value) + max_len = max(max_len, len(val_str)) + + # 按内容切换字体,表头加粗 + if idx == 0: + cell.font = Font(name="宋体", bold=True) if _is_chinese(val_str) else Font(name="Arial", bold=True) + else: + cell.font = Font(name="宋体") if _is_chinese(val_str) else Font(name="Arial") + + cell.alignment = align_center + + # 列宽留一点边距,最小 10,最大 40 + col_width = max(10, max_len + 2) + col_width = min(col_width, MAX_WIDTH) + ws.column_dimensions[col_cells[0].column_letter].width = col_width + + wb.save(file_path) + + def align_chemicals_with_file(self, file_path: str, auto_delete: bool = True) -> None: + """ + 功能: + 读取 Excel/CSV 文件,调用父类对齐逻辑,并将结果(fid)写回文件 + 参数: + file_path: 文件路径 + auto_delete: 是否删除不在文件中的工站化学品 + 返回: + None + """ + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"未找到化学品对齐文件: {path}") + + # 读取文件内容为 List[Dict] + df = pd.read_excel(path) if path.suffix in ['.xlsx', '.xls'] else pd.read_csv(path) + # 将 NaN 替换为空字符串 + df = df.fillna("") + rows = df.to_dict(orient='records') + header = df.columns.tolist() + + # 调用父类进行对齐,父类会修改 rows 中的数据(如回填 chemical_id) + updated_rows = self.align_chemicals_from_data(rows, auto_delete=auto_delete) + + # 写回文件 + new_df = pd.DataFrame(updated_rows) + # 保持原有列顺序,如果增加了新列(如 chemical_id 之前没有),这会包含它 + if path.suffix == '.csv': + new_df.to_csv(path, index=False, encoding="utf-8-sig") + else: + new_df.to_excel(path, index=False) + self._beautify_excel_database(path) # 保存后再美化 + + logger.info(f"化学品对齐完成并回写文件: {path}") + + # ---------- 2. 上料文件处理 ---------- + def batch_in_tray_by_file(self, file_path: str) -> JsonDict: + """ + 功能: + 读取上料表格,转换为中间格式,调用父类生成 Payload 并执行上料 + 参数: + file_path: 文件路径 + 返回: + Dict: API 响应 + """ + path = Path(file_path) + if not path.exists(): + logger.warning(f"未找到{file_path}.自动生成模板文件") + self._generate_batch_in_tray_template(path.with_suffix(".xlsx")) + return {} + + rows: List[Tuple[str, str, str]] = [] + + # 读取文件 + if path.suffix == '.xlsx': + wb = openpyxl.load_workbook(path) + ws = wb.active + for row in ws.iter_rows(min_row=2, values_only=True): + # 确保取前三列,且处理 None + pos = str(row[0]) if row[0] is not None else "" + t_type = str(row[1]) if len(row) > 1 and row[1] is not None else "" + content = str(row[2]) if len(row) > 2 and row[2] is not None else "" + rows.append((pos, t_type, content)) + else: + df = pd.read_csv(path) + df = df.fillna("") + for _, row in df.iterrows(): + rows.append((str(row[0]), str(row[1]), str(row[2]))) + + # 调用父类生成 Payload + payload = self.build_batch_in_tray_payload(rows) + + if not payload: + logger.warning("生成的上料数据为空") + return {} + + # 执行上料 + return payload + # return self.batch_in_tray(payload) + + def _generate_batch_in_tray_template(self, file_path: Path) -> None: + """ + 功能: + 生成批量上料Excel模板, 配置上料点位下拉、托盘类型下拉与内容示例 + 参数: + file_path: Path, 模板输出路径 + 返回: + None + """ + wb = Workbook() + ws = wb.active + ws.title = "batch_in_tray" + ws.append(["position", "tray_type", "content"]) + ws.column_dimensions["B"].width = 60 + ws.column_dimensions["C"].width = 80 + + # 位置下拉,包含 TB 列与 W-1-1~W-1-8 货位 + positions_tb = [f"TB-{row}-{col}" for row in (1, 2) for col in range(1, 5)] + positions_w = [f"W-1-{index}" for index in range(1, 9)] + positions = positions_tb + positions_w + dv_pos = DataValidation(type="list", formula1=f"\"{','.join(positions)}\"") + ws.add_data_validation(dv_pos) + dv_pos.add("A2:A101") + + # 托盘下拉,耗材显示数量范围,带物质显示点位范围 + consumable_trays = { + int(ResourceCode.TIP_TRAY_50UL), + int(ResourceCode.TIP_TRAY_1ML), + int(ResourceCode.TIP_TRAY_5ML), + int(ResourceCode.REACTION_SEAL_CAP_TRAY), + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY), + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY), + int(ResourceCode.REACTION_TUBE_TRAY_2ML), + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML), + } + tray_display: List[str] = [] + for code, name in TRAY_CODE_DISPLAY_NAME.items(): + base_text = f"{name}({code})" + try: + enum_name = ResourceCode(code).name + spec = getattr(TraySpec, enum_name, None) + except Exception: + spec = None + + if spec is None: + tray_display.append(base_text) + continue + + col_count, row_count = spec + if col_count <= 0 or row_count <= 0: + tray_display.append(base_text) + continue + + if code in consumable_trays: + capacity = col_count * row_count + tray_display.append(f"{base_text} [1-{capacity}]") + else: + end_row_char = chr(ord("A") + row_count - 1) + tray_display.append(f"{base_text} [A1-{end_row_char}{col_count}]") + + # 用隐藏sheet作为数据源,避免下拉字符串过长 + tray_sheet = wb.create_sheet("validation_meta") + for idx, option in enumerate(tray_display, start=1): + tray_sheet.cell(row=idx, column=1).value = option + tray_sheet.sheet_state = "hidden" + + dv_tray = DataValidation( + type="list", + formula1=f"=validation_meta!$A$1:$A${len(tray_display)}", + showInputMessage=True, + ) + ws.add_data_validation(dv_tray) + dv_tray.add("B2:B101") + + ws["C1"] = "content(耗材填数量; 物质填: A1|名称|2mL; B2|名称|5mg)" + wb.save(file_path) + logger.info(f"已生成上料模板: {file_path}") + + # ---------- 3. 任务生成文件处理 ---------- + def create_task_by_file(self, template_path: str, chemical_db_path: str) -> JsonDict: + """ + 功能: + 读取任务模板和化学品库,解析为中间数据,调用父类生成任务 Payload 并提交 + 参数: + template_path: 实验模板路径 + chemical_db_path: 化学品库路径 + 返回: + Dict: 任务创建结果 + """ + t_path = Path(template_path) + c_path = Path(chemical_db_path) + + # 1. 检查并生成模板 + if not t_path.exists(): + self._generate_reaction_template(t_path) + raise FileNotFoundError(f"已生成模板 {t_path},请填写后重试") + + if not c_path.exists(): + raise FileNotFoundError(f"未找到化学品库文件: {c_path}") + + # 2. 读取化学品库 -> Dict + chem_df = pd.read_excel(c_path) if c_path.suffix.lower() in [".xlsx", ".xls"] else pd.read_csv(c_path) + chem_df.columns = [str(c).strip().lower() for c in chem_df.columns] + + def _pick(row, *keys, default=None): + for k in keys: + if k in row and pd.notna(row[k]): + return row[k] + return default + + chemical_db: Dict[str, Dict[str, Any]] = {} + for _, r in chem_df.iterrows(): + row = {k: r.get(k) for k in chem_df.columns} + name = str(_pick(row, "substance", "name", "chemical_name", default="") or "").strip() + if not name: + continue + + chemical_db[name] = { + "chemical_id": _pick(row, "chemical_id"), + "molecular_weight": _pick(row, "molecular_weight", "mw"), + "physical_state": str(_pick(row, "physical_state", "state", default="") or "").strip().lower(), + # 统一把各种写法都接住(你原先 lower 后再用 'density (g/mL)' 是取不到的) + "density (g/mL)": _pick( + row, + "density (g/ml)", + "density(g/ml)", + "density_g_ml", + "density", + default=None, + ), + "fid": _pick(row, "fid"), + } + + # 3. 读取任务模板 -> params(Dict), headers(List), data_rows(List[List]) + wb = load_workbook(t_path, data_only=True) + ws = wb.active + + # 3.1 找到表头行/实验编号列(模板里一般是:row=1, col=3) + header_row = None + exp_no_col = None + for r in range(1, min(ws.max_row, 50) + 1): + for c in range(1, min(ws.max_column, 50) + 1): + v = ws.cell(r, c).value + if isinstance(v, str) and "实验编号" in v: + header_row, exp_no_col = r, c + break + if header_row is not None: + break + if header_row is None or exp_no_col is None: + raise ValueError("模板中未找到'实验编号'表头") + + # 3.2 提取全局参数(左侧 A/B) + # - 实验名称:A1是标签,用户通常填在 B1 + params: Dict[str, Any] = {} + exp_name = ws.cell(1, 2).value # B1 + if exp_name is not None and str(exp_name).strip() != "": + params["实验名称"] = str(exp_name).strip() + + # 扫描 A/B(从第2行开始,遇到“注:”不停止也可以;这里仅跳过“注:”本行) + for r in range(2, ws.max_row + 1): + key = ws.cell(r, 1).value + val = ws.cell(r, 2).value + + if key is None: + continue + key_str = str(key).strip() + if not key_str: + continue + + # 跳过注释行(不写入 params;否则会污染) + if key_str.startswith("注:") or key_str.startswith("注:"): + continue + + # 分类标题行通常是合并单元格,B 为空;这类不要写入 params + if val is None or (isinstance(val, str) and val.strip() == ""): + continue + + params[key_str] = val + + # 3.3 生成 headers(从 “实验编号”列开始往右:C..M) + # 同时把 “试剂_1” -> “试剂名称_1”,让 build_task_payload 能识别 + raw_headers: List[Any] = [] + for c in range(exp_no_col, ws.max_column + 1): + raw_headers.append(ws.cell(header_row, c).value) + + headers: List[str] = [] + reagent_idx = 0 + for h in raw_headers: + s = "" if h is None else str(h).strip() + + # 规范化:试剂_1/试剂1 -> 试剂名称_1 + if s.startswith("试剂") and "量" not in s and s != "试剂名称": + reagent_idx += 1 + headers.append(f"试剂名称_{reagent_idx}") + continue + + # 规范化:试剂量 -> 试剂量_1/2/... + if "试剂量" in s: + # 若前面还没遇到试剂列,给个兜底编号 + idx = reagent_idx if reagent_idx > 0 else (len([x for x in headers if "试剂量" in x]) + 1) + headers.append(f"试剂量_{idx}") + continue + + headers.append(s) + + # 3.4 生成 data_rows:从表头下一行开始,按实验编号列读取到最后一列(C..M) + data_rows: List[List[Any]] = [] + for r in range(header_row + 1, ws.max_row + 1): + exp_no = ws.cell(r, exp_no_col).value + + # 实验编号为空:认为实验区结束(模板一般后面都是空) + if exp_no is None or (isinstance(exp_no, str) and exp_no.strip() == ""): + # 只有在已经读到至少一行实验后才 break,避免中间空行误判 + if data_rows: + break + else: + continue + + row_vals: List[Any] = [] + for c in range(exp_no_col, ws.max_column + 1): + v = ws.cell(r, c).value + # 这里不要强制 str 化,build_task_payload 内部会 str();但 None 要变成 "" + row_vals.append("" if v is None else v) + + data_rows.append(row_vals) + + # 4. 调用父类纯逻辑生成 Payload + task_payload = self.build_task_payload(params, headers, data_rows, chemical_db) + + # 5. 提交任务信息到工站 + resp = self.add_task(task_payload) + + self._assert_success(resp,"创建任务") + + # 6. 提交任务信息到工站 + task_id = resp.get("task_id") + + return task_id + + def _generate_reaction_template(self, path: Path) -> None: + """ + 生成与 reeaction_template.xlsx 一致的反应模板 + 结构:左侧为参数配置区,右侧为实验试剂填报区 + """ + wb = Workbook() + ws = wb.active + ws.title = "Sheet1" + + # 模板默认字体:等线 11 + base_font = Font(name="等线", charset=134, family=2, scheme="minor", sz=11) + title_font = Font(name="等线", charset=134, family=2, scheme="minor", sz=11, bold=True) + center = Alignment(horizontal="center", vertical="center") + + # --- 1. 定义左侧参数配置数据 (行2开始, A列和B列) --- + left_params = [ + ("反应设定", ""), + ("实验名称", "Auto_task"), + ("反应器类型", "heat"), + ("反应时间(h)", 8), + ("反应温度(°C)", 40), + ("转速(rpm)", 500), + ("搅拌后⽬标温度(°C)", 30), + ("等待目标温度", "否"), + ("称量设定", ""), + ("称量误差(%)", 3), + ("最大称量误差(mg)", 1), + ("加料设定", ""), + ("固定加料顺序", "否"), + ("自动加磁子", "是"), + ("内标设定", ""), + ("内标种类", "1,3,5-三异丙基苯(内标,1mol/L in MeCN)"), + ("内标用量(μL/mg)", 100), + ("稀释液种类", "乙腈"), + ("稀释量(μL)", 500), + ("取样量(μL)", 2), + ("加入内标后搅拌时间(min)", 5), + ("", ""), # 空行 + ] + + # --- 2. 设置第一行表头 (Row 1) --- + ws.cell(row=1, column=3, value="实验编号").font = base_font + + reagent_count = 5 + current_col = 4 + for i in range(1, reagent_count + 1): + ws.cell(row=1, column=current_col, value=f"试剂").font = base_font + ws.cell(row=1, column=current_col + 1, value="试剂量").font = base_font + current_col += 2 + + # --- 3. 填充左侧参数区 (Row 2 ~ Row 22) --- + for idx, (param_name, default_val) in enumerate(left_params): + row_idx = idx + 1 # 从第2行开始 + + # 分类标题:模板是 A:B 合并,只写 A 列,且加粗 + if param_name and default_val == "": + ws.cell(row=row_idx, column=1, value=param_name).font = title_font + ws.merge_cells(start_row=row_idx, start_column=1, end_row=row_idx, end_column=2) + continue + + # 空行:保持空 + if param_name == "" and default_val == "": + continue + + # 普通参数行 + ws.cell(row=row_idx, column=1, value=param_name).font = base_font + ws.cell(row=row_idx, column=2, value=default_val).font = base_font + + # --- 4. 填充右侧实验编号 (Row 2 ~ Row 25) --- + for i in range(1, 25): # 1~24 + row_idx = i + 1 + ws.cell(row=row_idx, column=3, value=i).font = base_font + + # --- 5. 底部注释 (Row 23) --- + note_row = len(left_params) + 2 # 23 + note_text = "注:试剂量支持单位:(mmol,g,mg,μL,mL)" + ws.cell(row=note_row, column=1, value=note_text).font = base_font + ws.merge_cells(start_row=note_row, start_column=1, end_row=note_row, end_column=2) + ws.cell(row=note_row, column=1).alignment = center # 合并后的单元格居中 + + # --- 6. 字体铺满 (A1:M25) --- + for r in range(1, 26): + for c in range(1, 14): # A..M + cell = ws.cell(r, c) + # 标题行的粗体不要覆盖 + if cell.font and cell.font.bold: + continue + cell.font = base_font + + # --- 7. 对齐 --- + # C~L 整块都居中(含空白) + for r in range(1, 26): + for c in range(3, 13): # C..L + ws.cell(r, c).alignment = center + + # A 列:1~21 + 23 行居中(22/24/25 行保持默认) + for r in list(range(1, 22)) + [23]: + ws.cell(r, 1).alignment = center + + # B 列:只有有值的参数行居中(标题行/空白行/合并后的 B 不处理) + for r in [2, 3, 4, 5, 6, 7, 8, 10, 11, 13, 14, 16, 17, 18, 19, 20, 21]: + ws.cell(r, 2).alignment = center + + # M 列:只有表头 M1 居中 + ws.cell(1, 13).alignment = center + + # 表头 A1/C1 也居中(模板如此) + ws.cell(1, 1).alignment = center + ws.cell(1, 3).alignment = center + + # --- 8. 列宽: --- + widths_map = { + "A": 26.0, + "B": 38.0, + "C": 15.0, + "D": 14.0, + "E": 14.0, + "F": 14.0, + "G": 14.0, + "H": 14.0, + "I": 14.0, + "J": 14.0, + "K": 14.0, + "L": 14.0, + "M": 14.0, + } + for col_letter, w in widths_map.items(): + ws.column_dimensions[col_letter].width = w + + wb.save(path) + logger.info(f"已生成任务模板: {path}") + + # ---------- 4. 任务物料和站内物料对比 ---------- + + +if __name__ == "__main__": + + # 测试代码 + try: + settings = Settings.from_env() + manager = StationManager(settings) + + #---------------提交任务流程------------------- + + # 0. 设定文件名称 + + # 提交任务文件 + task_tpl = Path("reaction_template_5.xlsx") + + # 化合物库文件 + chem_db = Path("chemical_list.xlsx") + + # 进料文件 + template_in = Path("batch_in_tray.xlsx") + + # 3. 本地化学品库去重整理 + # manager.deduplicate_chemical_library_by_file(chem_db) + + # 4. 本地化学品库数据完整性检验 + # manager.check_chemical_library_by_file(chem_db) + + # 5. 工站化学品库和本地化学品库数据对齐 + # manager.align_chemicals_with_file(chem_db) + + # 6. 上传任务到工站 + # task_id = manager.create_task_by_file(str(task_tpl), str(chem_db)) + + # 4. 对比站内资源和任务文件json列出缺乏 + # manager.check_resource_for_task(str(task_tpl), str(chem_db)) + + # 5. 上料 + # manager.batch_in_tray_by_file(str(template_in)) + + # # 7 . 开始任务 + # resp = manager.start_task(task_id) + + #---------------工站状态查询------------------- + + # 1. 查询站内所有物料信息 + # resource_info = manager.get_resource_info() + + # 2. 查询站内所有设设备状态 + devices_info = manager.list_device_status() + print(devices_info) + + # 3. 查询工站运行状态 + station_info = manager.station_state() + + print(station_info) + # 4. 查询手套箱状态 + glovebox_info = manager.get_glovebox_env() + print(glovebox_info) + + #---------------其他可执行动作------------------- + + # 1. 登录 + # manager.login() + + # 2. 设备初始化 + # manager.device_init() + + + except Exception as e: + logger.error(f"测试失败: {e}", exc_info=True) + + #————————————————额外功能———————————————————— + + # # 获取站内所有化学品信息,导出到csv文件 + # manager.export_chemical_list_to_file("chemicals_list_export.csv") + + # # 通过csv进行化学品录入 + # manager.sync_chemicals_from_file("add_chemical_list.csv") From 7b6d8054d2a78404cd6149bcaa56ba45f14d7920 Mon Sep 17 00:00:00 2001 From: noiosoooo9999 Date: Mon, 12 Jan 2026 14:06:32 +0800 Subject: [PATCH 02/25] cloud --- mock_server.py | 45 + .../workstation/eit_synthesis_station.zip | Bin 0 -> 461581 bytes .../eit_synthesis_station/chemical_list.csv | 2085 +++++++++++++++++ .../eit_synthesis_station/chemical_list.xlsx | Bin 210502 -> 183607 bytes .../eit_synthesis_station/config/setting.py | 3 +- .../controller/station_controller.py | 8 +- .../driver/api_client.py | 4 +- .../eit_synthesis_station/eit_station.yaml | 208 ++ .../eit_synthesis_station/station_manager.py | 8 +- .../eit_synthesis_station/test.json | 20 + unilabos/registry/devices/eit_station.yaml | 208 ++ 11 files changed, 2578 insertions(+), 11 deletions(-) create mode 100644 mock_server.py create mode 100644 unilabos/devices/workstation/eit_synthesis_station.zip create mode 100644 unilabos/devices/workstation/eit_synthesis_station/chemical_list.csv create mode 100644 unilabos/devices/workstation/eit_synthesis_station/eit_station.yaml create mode 100644 unilabos/devices/workstation/eit_synthesis_station/test.json create mode 100644 unilabos/registry/devices/eit_station.yaml diff --git a/mock_server.py b/mock_server.py new file mode 100644 index 00000000..f3dca9f4 --- /dev/null +++ b/mock_server.py @@ -0,0 +1,45 @@ +from flask import Flask, request, jsonify + +app = Flask(__name__) + +# 通用的成功响应模板 +def success_response(data=None): + return jsonify({ + "code": 200, + "msg": "success", + "data": data or {}, + "access_token": "fake_token_123", # 专门给登录用 + "token_type": "Bearer" + }) + +# 1. 模拟登录接口 +@app.route('/api/Token', methods=['POST']) +def login(): + print(f"[Mock] 收到登录请求: {request.json}") + return success_response() + +# 2. 模拟创建任务接口 +@app.route('/api/AddTask', methods=['POST']) +def add_task(): + print(f"[Mock] 收到创建任务请求. 任务名称: {request.json.get('task_name')}") + # 这里可以打印一下,帮你检查发过来的数据对不对 + return success_response({"task_id": 999}) + +# 3. 模拟获取资源/化学品列表 (防止其他地方报错) +@app.route('/api/v1/knowledge/getChemicalList', methods=['GET']) +def get_chem_list(): + return success_response({ + "chemical_list": [], + "chemical_sums": 0 + }) + +# 4. “万能”接口:只要是你没定义的接口,统统返回成功 +@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE']) +@app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE']) +def catch_all(path): + print(f"[Mock] 调用了通用接口: /{path}") + return success_response() + +if __name__ == '__main__': + print("⚡ 模拟服务器已启动,地址: http://127.0.0.1:4669") + app.run(port=4669) \ No newline at end of file diff --git a/unilabos/devices/workstation/eit_synthesis_station.zip b/unilabos/devices/workstation/eit_synthesis_station.zip new file mode 100644 index 0000000000000000000000000000000000000000..21ff72ef8b725efe28e2527670e9255405ba6ccf GIT binary patch literal 461581 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