from datetime import datetime
import json
import time
from typing import Optional, Dict, Any, List
from typing_extensions import TypedDict
import requests
from unilabos.devices.workstation.bioyond_studio.config import API_CONFIG

from unilabos.devices.workstation.bioyond_studio.bioyond_rpc import BioyondException
from unilabos.devices.workstation.bioyond_studio.station import BioyondWorkstation
from unilabos.ros.nodes.base_device_node import ROS2DeviceNode, BaseROS2DeviceNode
import json
import sys
from pathlib import Path
import importlib

class ComputeExperimentDesignReturn(TypedDict):
    solutions: list
    titration: dict
    solvents: dict
    feeding_order: list
    return_info: str


class BioyondDispensingStation(BioyondWorkstation):
    def __init__(
        self,
        config,
            # 桌子
        deck,
        *args,
        **kwargs,
        ):
        super().__init__(config, deck, *args, **kwargs)
        # self.config = config
        # self.api_key = config["api_key"]
        # self.host = config["api_host"]
        #
        # # 使用简单的Logger替代原来的logger
        # self._logger = SimpleLogger()
        # self.is_running = False

        # 用于跟踪任务完成状态的字典: {orderCode: {status, order_id, timestamp}}
        self.order_completion_status = {}

    def _post_project_api(self, endpoint: str, data: Any) -> Dict[str, Any]:
        """项目接口通用POST调用

        参数:
            endpoint: 接口路径（例如 /api/lims/order/brief-step-paramerers）
            data: 请求体中的 data 字段内容

        返回:
            dict: 服务端响应，失败时返回 {code:0,message,...}
        """
        request_data = {
            "apiKey": API_CONFIG["api_key"],
            "requestTime": self.hardware_interface.get_current_time_iso8601(),
            "data": data
        }
        try:
            response = requests.post(
                f"{self.hardware_interface.host}{endpoint}",
                json=request_data,
                headers={"Content-Type": "application/json"},
                timeout=30
            )
            result = response.json()
            return result if isinstance(result, dict) else {"code": 0, "message": "非JSON响应"}
        except json.JSONDecodeError:
            return {"code": 0, "message": "非JSON响应"}
        except requests.exceptions.Timeout:
            return {"code": 0, "message": "请求超时"}
        except requests.exceptions.RequestException as e:
            return {"code": 0, "message": str(e)}

    def _delete_project_api(self, endpoint: str, data: Any) -> Dict[str, Any]:
        """项目接口通用DELETE调用

        参数:
            endpoint: 接口路径（例如 /api/lims/order/workflows）
            data: 请求体中的 data 字段内容

        返回:
            dict: 服务端响应，失败时返回 {code:0,message,...}
        """
        request_data = {
            "apiKey": API_CONFIG["api_key"],
            "requestTime": self.hardware_interface.get_current_time_iso8601(),
            "data": data
        }
        try:
            response = requests.delete(
                f"{self.hardware_interface.host}{endpoint}",
                json=request_data,
                headers={"Content-Type": "application/json"},
                timeout=30
            )
            result = response.json()
            return result if isinstance(result, dict) else {"code": 0, "message": "非JSON响应"}
        except json.JSONDecodeError:
            return {"code": 0, "message": "非JSON响应"}
        except requests.exceptions.Timeout:
            return {"code": 0, "message": "请求超时"}
        except requests.exceptions.RequestException as e:
            return {"code": 0, "message": str(e)}

    def compute_experiment_design(
        self,
        ratio: dict,
        wt_percent: str = "0.25",
        m_tot: str = "70",
        titration_percent: str = "0.03",
    ) -> ComputeExperimentDesignReturn:
        try:
            if isinstance(ratio, str):
                try:
                    ratio = json.loads(ratio)
                except Exception:
                    ratio = {}
            root = str(Path(__file__).resolve().parents[3])
            if root not in sys.path:
                sys.path.append(root)
            try:
                mod = importlib.import_module("tem.compute")
            except Exception as e:
                raise BioyondException(f"无法导入计算模块: {e}")
            try:
                wp = float(wt_percent) if isinstance(wt_percent, str) else wt_percent
                mt = float(m_tot) if isinstance(m_tot, str) else m_tot
                tp = float(titration_percent) if isinstance(titration_percent, str) else titration_percent
            except Exception as e:
                raise BioyondException(f"参数解析失败: {e}")
            res = mod.generate_experiment_design(ratio=ratio, wt_percent=wp, m_tot=mt, titration_percent=tp)
            out = {
                "solutions": res.get("solutions", []),
                "titration": res.get("titration", {}),
                "solvents": res.get("solvents", {}),
                "feeding_order": res.get("feeding_order", []),
                "return_info": json.dumps(res, ensure_ascii=False)
            }
            return out
        except BioyondException:
            raise
        except Exception as e:
            raise BioyondException(str(e))

    # 90%10%小瓶投料任务创建方法
    def create_90_10_vial_feeding_task(self,
                                       order_name: str = None,
                                       speed: str = None,
                                       temperature: str = None,
                                       delay_time: str = None,
                                       percent_90_1_assign_material_name: str = None,
                                       percent_90_1_target_weigh: str = None,
                                       percent_90_2_assign_material_name: str = None,
                                       percent_90_2_target_weigh: str = None,
                                       percent_90_3_assign_material_name: str = None,
                                       percent_90_3_target_weigh: str = None,
                                       percent_10_1_assign_material_name: str = None,
                                       percent_10_1_target_weigh: str = None,
                                       percent_10_1_volume: str = None,
                                       percent_10_1_liquid_material_name: str = None,
                                       percent_10_2_assign_material_name: str = None,
                                       percent_10_2_target_weigh: str = None,
                                       percent_10_2_volume: str = None,
                                       percent_10_2_liquid_material_name: str = None,
                                       percent_10_3_assign_material_name: str = None,
                                       percent_10_3_target_weigh: str = None,
                                       percent_10_3_volume: str = None,
                                       percent_10_3_liquid_material_name: str = None,
                                       hold_m_name: str = None) -> dict:
        """
        创建90%10%小瓶投料任务

        参数说明:
        - order_name: 任务名称，如果为None则使用默认名称
        - speed: 搅拌速度，如果为None则使用默认值400
        - temperature: 温度，如果为None则使用默认值40
        - delay_time: 延迟时间，如果为None则使用默认值600
        - percent_90_1_assign_material_name: 90%_1物料名称
        - percent_90_1_target_weigh: 90%_1目标重量
        - percent_90_2_assign_material_name: 90%_2物料名称
        - percent_90_2_target_weigh: 90%_2目标重量
        - percent_90_3_assign_material_name: 90%_3物料名称
        - percent_90_3_target_weigh: 90%_3目标重量
        - percent_10_1_assign_material_name: 10%_1固体物料名称
        - percent_10_1_target_weigh: 10%_1固体目标重量
        - percent_10_1_volume: 10%_1液体体积
        - percent_10_1_liquid_material_name: 10%_1液体物料名称
        - percent_10_2_assign_material_name: 10%_2固体物料名称
        - percent_10_2_target_weigh: 10%_2固体目标重量
        - percent_10_2_volume: 10%_2液体体积
        - percent_10_2_liquid_material_name: 10%_2液体物料名称
        - percent_10_3_assign_material_name: 10%_3固体物料名称
        - percent_10_3_target_weigh: 10%_3固体目标重量
        - percent_10_3_volume: 10%_3液体体积
        - percent_10_3_liquid_material_name: 10%_3液体物料名称
        - hold_m_name: 库位名称，如"C01"，用于查找对应的holdMId

        返回: 任务创建结果

        异常:
        - BioyondException: 各种错误情况下的统一异常
        """
        try:
            # 1. 参数验证
            if not hold_m_name:
                raise BioyondException("hold_m_name 是必填参数")

            # 检查90%物料参数的完整性
            # 90%_1物料：如果有物料名称或目标重量，就必须有全部参数
            if percent_90_1_assign_material_name or percent_90_1_target_weigh:
                if not percent_90_1_assign_material_name:
                    raise BioyondException("90%_1物料：如果提供了目标重量，必须同时提供物料名称")
                if not percent_90_1_target_weigh:
                    raise BioyondException("90%_1物料：如果提供了物料名称，必须同时提供目标重量")

            # 90%_2物料：如果有物料名称或目标重量，就必须有全部参数
            if percent_90_2_assign_material_name or percent_90_2_target_weigh:
                if not percent_90_2_assign_material_name:
                    raise BioyondException("90%_2物料：如果提供了目标重量，必须同时提供物料名称")
                if not percent_90_2_target_weigh:
                    raise BioyondException("90%_2物料：如果提供了物料名称，必须同时提供目标重量")

            # 90%_3物料：如果有物料名称或目标重量，就必须有全部参数
            if percent_90_3_assign_material_name or percent_90_3_target_weigh:
                if not percent_90_3_assign_material_name:
                    raise BioyondException("90%_3物料：如果提供了目标重量，必须同时提供物料名称")
                if not percent_90_3_target_weigh:
                    raise BioyondException("90%_3物料：如果提供了物料名称，必须同时提供目标重量")

            # 检查10%物料参数的完整性
            # 10%_1物料：如果有物料名称、目标重量、体积或液体物料名称中的任何一个，就必须有全部参数
            if any([percent_10_1_assign_material_name, percent_10_1_target_weigh, percent_10_1_volume, percent_10_1_liquid_material_name]):
                if not percent_10_1_assign_material_name:
                    raise BioyondException("10%_1物料：如果提供了其他参数，必须同时提供固体物料名称")
                if not percent_10_1_target_weigh:
                    raise BioyondException("10%_1物料：如果提供了其他参数，必须同时提供固体目标重量")
                if not percent_10_1_volume:
                    raise BioyondException("10%_1物料：如果提供了其他参数，必须同时提供液体体积")
                if not percent_10_1_liquid_material_name:
                    raise BioyondException("10%_1物料：如果提供了其他参数，必须同时提供液体物料名称")

            # 10%_2物料：如果有物料名称、目标重量、体积或液体物料名称中的任何一个，就必须有全部参数
            if any([percent_10_2_assign_material_name, percent_10_2_target_weigh, percent_10_2_volume, percent_10_2_liquid_material_name]):
                if not percent_10_2_assign_material_name:
                    raise BioyondException("10%_2物料：如果提供了其他参数，必须同时提供固体物料名称")
                if not percent_10_2_target_weigh:
                    raise BioyondException("10%_2物料：如果提供了其他参数，必须同时提供固体目标重量")
                if not percent_10_2_volume:
                    raise BioyondException("10%_2物料：如果提供了其他参数，必须同时提供液体体积")
                if not percent_10_2_liquid_material_name:
                    raise BioyondException("10%_2物料：如果提供了其他参数，必须同时提供液体物料名称")

            # 10%_3物料：如果有物料名称、目标重量、体积或液体物料名称中的任何一个，就必须有全部参数
            if any([percent_10_3_assign_material_name, percent_10_3_target_weigh, percent_10_3_volume, percent_10_3_liquid_material_name]):
                if not percent_10_3_assign_material_name:
                    raise BioyondException("10%_3物料：如果提供了其他参数，必须同时提供固体物料名称")
                if not percent_10_3_target_weigh:
                    raise BioyondException("10%_3物料：如果提供了其他参数，必须同时提供固体目标重量")
                if not percent_10_3_volume:
                    raise BioyondException("10%_3物料：如果提供了其他参数，必须同时提供液体体积")
                if not percent_10_3_liquid_material_name:
                    raise BioyondException("10%_3物料：如果提供了其他参数，必须同时提供液体物料名称")

            # 2. 生成任务编码和设置默认值
            order_code = "task_vial_" + str(int(datetime.now().timestamp()))
            if order_name is None:
                order_name = "90%10%小瓶投料任务"
            if speed is None:
                speed = "400"
            if temperature is None:
                temperature = "40"
            if delay_time is None:
                delay_time = "600"

            # 3. 工作流ID
            workflow_id = "3a19310d-16b9-9d81-b109-0748e953694b"

            # 4. 查询工作流对应的holdMID
            material_info = self.hardware_interface.material_id_query(workflow_id)
            if not material_info:
                raise BioyondException(f"无法查询工作流 {workflow_id} 的物料信息")

            # 获取locations列表
            locations = material_info.get("locations", []) if isinstance(material_info, dict) else []
            if not locations:
                raise BioyondException(f"工作流 {workflow_id} 没有找到库位信息")

            # 查找指定名称的库位
            hold_mid = None
            for location in locations:
                if location.get("holdMName") == hold_m_name:
                    hold_mid = location.get("holdMId")
                    break

            if not hold_mid:
                raise BioyondException(f"未找到库位名称为 {hold_m_name} 的库位，请检查名称是否正确")

            extend_properties = f"{{\"{ hold_mid }\": {{}}}}"
            self.hardware_interface._logger.info(f"找到库位 {hold_m_name} 对应的holdMId: {hold_mid}")

            # 5. 构建任务参数
            order_data = {
                "orderCode": order_code,
                "orderName": order_name,
                "workflowId": workflow_id,
                "borderNumber": 1,
                "paramValues": {},
                "ExtendProperties": extend_properties
            }

            # 添加搅拌参数
            order_data["paramValues"]["e8264e47-c319-d9d9-8676-4dd5cb382b11"] = [
                {"m": 0, "n": 3, "Key": "speed", "Value": speed},
                {"m": 0, "n": 3, "Key": "temperature", "Value": temperature}
            ]

            # 添加延迟时间参数
            order_data["paramValues"]["dc5dba79-5e4b-8eae-cbc5-e93482e43b1f"] = [
                {"m": 0, "n": 4, "Key": "DelayTime", "Value": delay_time}
            ]

            # 添加90%_1参数
            if percent_90_1_assign_material_name is not None and percent_90_1_target_weigh is not None:
                order_data["paramValues"]["e7d3c0a3-25c2-c42d-c84b-860c4a5ef844"] = [
                    {"m": 15, "n": 1, "Key": "targetWeigh", "Value": percent_90_1_target_weigh},
                    {"m": 15, "n": 1, "Key": "assignMaterialName", "Value": percent_90_1_assign_material_name}
                ]

            # 添加90%_2参数
            if percent_90_2_assign_material_name is not None and percent_90_2_target_weigh is not None:
                order_data["paramValues"]["50b912c4-6c81-0734-1c8b-532428b2a4a5"] = [
                    {"m": 18, "n": 1, "Key": "targetWeigh", "Value": percent_90_2_target_weigh},
                    {"m": 18, "n": 1, "Key": "assignMaterialName", "Value": percent_90_2_assign_material_name}
                ]

            # 添加90%_3参数
            if percent_90_3_assign_material_name is not None and percent_90_3_target_weigh is not None:
                order_data["paramValues"]["9c3674b3-c7cb-946e-fa03-fa2861d8aec4"] = [
                    {"m": 21, "n": 1, "Key": "targetWeigh", "Value": percent_90_3_target_weigh},
                    {"m": 21, "n": 1, "Key": "assignMaterialName", "Value": percent_90_3_assign_material_name}
                ]

            # 添加10%_1固体参数
            if percent_10_1_assign_material_name is not None and percent_10_1_target_weigh is not None:
                order_data["paramValues"]["73a0bfd8-1967-45e9-4bab-c07ccd1a2727"] = [
                    {"m": 3, "n": 1, "Key": "targetWeigh", "Value": percent_10_1_target_weigh},
                    {"m": 3, "n": 1, "Key": "assignMaterialName", "Value": percent_10_1_assign_material_name}
                ]

            # 添加10%_1液体参数
            if percent_10_1_liquid_material_name is not None and percent_10_1_volume is not None:
                order_data["paramValues"]["39634d40-c623-473a-8e5f-bc301aca2522"] = [
                    {"m": 3, "n": 3, "Key": "volume", "Value": percent_10_1_volume},
                    {"m": 3, "n": 3, "Key": "assignMaterialName", "Value": percent_10_1_liquid_material_name}
                ]

            # 添加10%_2固体参数
            if percent_10_2_assign_material_name is not None and percent_10_2_target_weigh is not None:
                order_data["paramValues"]["2d9c16fa-2a19-cd47-a67b-3cadff9e3e3d"] = [
                    {"m": 7, "n": 1, "Key": "targetWeigh", "Value": percent_10_2_target_weigh},
                    {"m": 7, "n": 1, "Key": "assignMaterialName", "Value": percent_10_2_assign_material_name}
                ]

            # 添加10%_2液体参数
            if percent_10_2_liquid_material_name is not None and percent_10_2_volume is not None:
                order_data["paramValues"]["e60541bb-ed68-e839-7305-2b4abe38a13d"] = [
                    {"m": 7, "n": 3, "Key": "volume", "Value": percent_10_2_volume},
                    {"m": 7, "n": 3, "Key": "assignMaterialName", "Value": percent_10_2_liquid_material_name}
                ]

            # 添加10%_3固体参数
            if percent_10_3_assign_material_name is not None and percent_10_3_target_weigh is not None:
                order_data["paramValues"]["27494733-0f71-a916-7cd2-1929a0125f17"] = [
                    {"m": 11, "n": 1, "Key": "targetWeigh", "Value": percent_10_3_target_weigh},
                    {"m": 11, "n": 1, "Key": "assignMaterialName", "Value": percent_10_3_assign_material_name}
                ]

            # 添加10%_3液体参数
            if percent_10_3_liquid_material_name is not None and percent_10_3_volume is not None:
                order_data["paramValues"]["c8798c29-786f-6858-7d7f-5330b890f2a6"] = [
                    {"m": 11, "n": 3, "Key": "volume", "Value": percent_10_3_volume},
                    {"m": 11, "n": 3, "Key": "assignMaterialName", "Value": percent_10_3_liquid_material_name}
                ]

            # 6. 转换为JSON字符串并创建任务
            json_str = json.dumps([order_data], ensure_ascii=False)
            self.hardware_interface._logger.info(f"创建90%10%小瓶投料任务参数: {json_str}")

            # 7. 调用create_order方法创建任务
            result = self.hardware_interface.create_order(json_str)
            self.hardware_interface._logger.info(f"创建90%10%小瓶投料任务结果: {result}")

            # 8. 解析结果获取order_id
            order_id = None
            if isinstance(result, str):
                # result 格式: "{'3a1d895c-4d39-d504-1398-18f5a40bac1e': [{'id': '...', ...}]}"
                # 第一个键就是order_id (UUID)
                try:
                    # 尝试解析字符串为字典
                    import ast
                    result_dict = ast.literal_eval(result)
                    # 获取第一个键作为order_id
                    if result_dict and isinstance(result_dict, dict):
                        first_key = list(result_dict.keys())[0]
                        order_id = first_key
                        self.hardware_interface._logger.info(f"✓ 成功提取order_id: {order_id}")
                    else:
                        self.hardware_interface._logger.warning(f"result_dict格式异常: {result_dict}")
                except Exception as e:
                    self.hardware_interface._logger.error(f"✗ 无法从结果中提取order_id: {e}, result类型={type(result)}")
            elif isinstance(result, dict):
                # 如果已经是字典
                if result:
                    first_key = list(result.keys())[0]
                    order_id = first_key
                    self.hardware_interface._logger.info(f"✓ 成功提取order_id(dict): {order_id}")

            if not order_id:
                self.hardware_interface._logger.warning(
                    f"⚠ 未能提取order_id，result={result[:100] if isinstance(result, str) else result}"
                )

            # 返回成功结果和构建的JSON数据
            return json.dumps({
                "suc": True,
                "order_code": order_code,
                "order_id": order_id,
                "result": result,
                "order_params": order_data
            })

        except BioyondException:
            # 重新抛出BioyondException
            raise
        except Exception as e:
            # 捕获其他未预期的异常，转换为BioyondException
            error_msg = f"创建90%10%小瓶投料任务时发生未预期的错误: {str(e)}"
            self.hardware_interface._logger.error(error_msg)
            raise BioyondException(error_msg)

    # 二胺溶液配置任务创建方法
    def create_diamine_solution_task(self,
                                    order_name: str = None,
                                    material_name: str = None,
                                    target_weigh: str = None,
                                    volume: str = None,
                                    liquid_material_name: str = "NMP",
                                    speed: str = None,
                                    temperature: str = None,
                                    delay_time: str = None,
                                    hold_m_name: str = None) -> dict:
        """
        创建二胺溶液配置任务

        参数说明:
        - order_name: 任务名称，如果为None则使用默认名称
        - material_name: 固体物料名称，必填
        - target_weigh: 固体目标重量，必填
        - volume: 液体体积，必填
        - liquid_material_name: 液体物料名称，默认为NMP
        - speed: 搅拌速度，如果为None则使用默认值400
        - temperature: 温度，如果为None则使用默认值20
        - delay_time: 延迟时间，如果为None则使用默认值600
        - hold_m_name: 库位名称，如"ODA-1"，用于查找对应的holdMId

        返回: 任务创建结果

        异常:
        - BioyondException: 各种错误情况下的统一异常
        """
        try:
            # 1. 参数验证
            if not material_name:
                raise BioyondException("material_name 是必填参数")
            if not target_weigh:
                raise BioyondException("target_weigh 是必填参数")
            if not volume:
                raise BioyondException("volume 是必填参数")
            if not hold_m_name:
                raise BioyondException("hold_m_name 是必填参数")


            # 2. 生成任务编码和设置默认值
            order_code = "task_oda_" + str(int(datetime.now().timestamp()))
            if order_name is None:
                order_name = f"二胺溶液配置-{material_name}"
            if speed is None:
                speed = "400"
            if temperature is None:
                temperature = "20"
            if delay_time is None:
                delay_time = "600"

            # 3. 工作流ID - 二胺溶液配置工作流
            workflow_id = "3a15d4a1-3bbe-76f9-a458-292896a338f5"

            # 4. 查询工作流对应的holdMID
            material_info = self.hardware_interface.material_id_query(workflow_id)
            if not material_info:
                raise BioyondException(f"无法查询工作流 {workflow_id} 的物料信息")

            # 获取locations列表
            locations = material_info.get("locations", []) if isinstance(material_info, dict) else []
            if not locations:
                raise BioyondException(f"工作流 {workflow_id} 没有找到库位信息")

            # 查找指定名称的库位
            hold_mid = None
            for location in locations:
                if location.get("holdMName") == hold_m_name:
                    hold_mid = location.get("holdMId")
                    break

            if not hold_mid:
                raise BioyondException(f"未找到库位名称为 {hold_m_name} 的库位，请检查名称是否正确")

            extend_properties = f"{{\"{ hold_mid }\": {{}}}}"
            self.hardware_interface._logger.info(f"找到库位 {hold_m_name} 对应的holdMId: {hold_mid}")

            # 5. 构建任务参数
            order_data = {
                "orderCode": order_code,
                "orderName": order_name,
                "workflowId": workflow_id,
                "borderNumber": 1,
                "paramValues": {
                    # 固体物料参数
                    "3a15d4a1-3bde-f5bc-053f-1ae0bf1f357e": [
                        {"m": 3, "n": 2, "Key": "targetWeigh", "Value": target_weigh},
                        {"m": 3, "n": 2, "Key": "assignMaterialName", "Value": material_name}
                    ],
                    # 液体物料参数
                    "3a15d4a1-3bde-d584-b309-e661ae8f1c01": [
                        {"m": 3, "n": 3, "Key": "volume", "Value": volume},
                        {"m": 3, "n": 3, "Key": "assignMaterialName", "Value": liquid_material_name}
                    ],
                    # 搅拌参数
                    "3a15d4a1-3bde-8ec4-1ced-92efc97ed73d": [
                        {"m": 3, "n": 6, "Key": "speed", "Value": speed},
                        {"m": 3, "n": 6, "Key": "temperature", "Value": temperature}
                    ],
                    # 延迟时间参数
                    "3a15d4a1-3bde-3b92-83ff-8923a0addbbc": [
                        {"m": 3, "n": 7, "Key": "DelayTime", "Value": delay_time}
                    ]
                },
                "ExtendProperties": extend_properties
            }

            # 6. 转换为JSON字符串并创建任务
            json_str = json.dumps([order_data], ensure_ascii=False)
            self.hardware_interface._logger.info(f"创建二胺溶液配置任务参数: {json_str}")

            # 7. 调用create_order方法创建任务
            result = self.hardware_interface.create_order(json_str)
            self.hardware_interface._logger.info(f"创建二胺溶液配置任务结果: {result}")

            # 8. 解析结果获取order_id
            order_id = None
            if isinstance(result, str):
                try:
                    import ast
                    result_dict = ast.literal_eval(result)
                    if result_dict and isinstance(result_dict, dict):
                        first_key = list(result_dict.keys())[0]
                        order_id = first_key
                        self.hardware_interface._logger.info(f"✓ 成功提取order_id: {order_id}")
                    else:
                        self.hardware_interface._logger.warning(f"result_dict格式异常: {result_dict}")
                except Exception as e:
                    self.hardware_interface._logger.error(f"✗ 无法从结果中提取order_id: {e}")
            elif isinstance(result, dict):
                if result:
                    first_key = list(result.keys())[0]
                    order_id = first_key
                    self.hardware_interface._logger.info(f"✓ 成功提取order_id(dict): {order_id}")

            if not order_id:
                self.hardware_interface._logger.warning(f"⚠ 未能提取order_id")

            # 返回成功结果和构建的JSON数据
            return json.dumps({
                "suc": True,
                "order_code": order_code,
                "order_id": order_id,
                "result": result,
                "order_params": order_data
            })

        except BioyondException:
            # 重新抛出BioyondException
            raise
        except Exception as e:
            # 捕获其他未预期的异常，转换为BioyondException
            error_msg = f"创建二胺溶液配置任务时发生未预期的错误: {str(e)}"
            self.hardware_interface._logger.error(error_msg)
            raise BioyondException(error_msg)

    # 批量创建二胺溶液配置任务
    def batch_create_diamine_solution_tasks(self,
                                           solutions,
                                           liquid_material_name: str = "NMP",
                                           speed: str = None,
                                           temperature: str = None,
                                           delay_time: str = None) -> str:
        """
        批量创建二胺溶液配置任务

        参数说明:
        - solutions: 溶液列表（数组）或JSON字符串，格式如下:
          [
              {
                  "name": "MDA",
                  "order": 0,
                  "solid_mass": 5.0,
                  "solvent_volume": 20,
                  ...
              },
              ...
          ]
        - liquid_material_name: 液体物料名称，默认为"NMP"
        - speed: 搅拌速度，如果为None则使用默认值400
        - temperature: 温度，如果为None则使用默认值20
        - delay_time: 延迟时间，如果为None则使用默认值600

        返回: JSON字符串格式的任务创建结果

        异常:
        - BioyondException: 各种错误情况下的统一异常
        """
        try:
            # 参数类型转换：如果是字符串则解析为列表
            if isinstance(solutions, str):
                try:
                    solutions = json.loads(solutions)
                except json.JSONDecodeError as e:
                    raise BioyondException(f"solutions JSON解析失败: {str(e)}")

            # 参数验证
            if not isinstance(solutions, list):
                raise BioyondException("solutions 必须是列表类型或有效的JSON数组字符串")

            if not solutions:
                raise BioyondException("solutions 列表不能为空")

            # 批量创建任务
            results = []
            success_count = 0
            failed_count = 0

            for idx, solution in enumerate(solutions):
                try:
                    # 提取参数
                    name = solution.get("name")
                    solid_mass = solution.get("solid_mass")
                    solvent_volume = solution.get("solvent_volume")
                    order = solution.get("order")

                    if not all([name, solid_mass is not None, solvent_volume is not None]):
                        self.hardware_interface._logger.warning(
                            f"跳过第 {idx + 1} 个溶液：缺少必要参数"
                        )
                        results.append({
                            "index": idx + 1,
                            "name": name,
                            "success": False,
                            "error": "缺少必要参数"
                        })
                        failed_count += 1
                        continue

                    # 生成库位名称（直接使用物料名称）
                    # 如果需要其他命名规则，可以在这里调整
                    hold_m_name = name

                    # 调用单个任务创建方法
                    result = self.create_diamine_solution_task(
                        order_name=f"二胺溶液配置-{name}",
                        material_name=name,
                        target_weigh=str(solid_mass),
                        volume=str(solvent_volume),
                        liquid_material_name=liquid_material_name,
                        speed=speed,
                        temperature=temperature,
                        delay_time=delay_time,
                        hold_m_name=hold_m_name
                    )

                    # 解析返回结果以获取order_code和order_id
                    result_data = json.loads(result) if isinstance(result, str) else result
                    order_code = result_data.get("order_code")
                    order_id = result_data.get("order_id")
                    order_params = result_data.get("order_params", {})

                    results.append({
                        "index": idx + 1,
                        "name": name,
                        "success": True,
                        "order_code": order_code,
                        "order_id": order_id,
                        "hold_m_name": hold_m_name,
                        "order_params": order_params
                    })
                    success_count += 1
                    self.hardware_interface._logger.info(
                        f"成功创建二胺溶液配置任务: {name}, order_code={order_code}, order_id={order_id}"
                    )

                except BioyondException as e:
                    results.append({
                        "index": idx + 1,
                        "name": solution.get("name", "unknown"),
                        "success": False,
                        "error": str(e)
                    })
                    failed_count += 1
                    self.hardware_interface._logger.error(
                        f"创建第 {idx + 1} 个任务失败: {str(e)}"
                    )
                except Exception as e:
                    results.append({
                        "index": idx + 1,
                        "name": solution.get("name", "unknown"),
                        "success": False,
                        "error": f"未知错误: {str(e)}"
                    })
                    failed_count += 1
                    self.hardware_interface._logger.error(
                        f"创建第 {idx + 1} 个任务时发生未知错误: {str(e)}"
                    )

            # 提取所有成功任务的order_code和order_id
            order_codes = [r["order_code"] for r in results if r["success"]]
            order_ids = [r["order_id"] for r in results if r["success"]]

            # 返回汇总结果
            summary = {
                "total": len(solutions),
                "success": success_count,
                "failed": failed_count,
                "order_codes": order_codes,
                "order_ids": order_ids,
                "details": results
            }

            self.hardware_interface._logger.info(
                f"批量创建二胺溶液配置任务完成: 总数={len(solutions)}, "
                f"成功={success_count}, 失败={failed_count}"
            )

            # 构建返回结果
            summary["return_info"] = {
                "order_codes": order_codes,
                "order_ids": order_ids,
            }

            return summary

        except BioyondException:
            raise
        except Exception as e:
            error_msg = f"批量创建二胺溶液配置任务时发生未预期的错误: {str(e)}"
            self.hardware_interface._logger.error(error_msg)
            raise BioyondException(error_msg)

    def brief_step_parameters(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """获取简要步骤参数（站点项目接口）

        参数:
            data: 查询参数字典

        返回值:
            dict: 接口返回数据
        """
        return self._post_project_api("/api/lims/order/brief-step-paramerers", data)

    def project_order_report(self, order_id: str) -> Dict[str, Any]:
        """查询项目端订单报告（兼容旧路径）

        参数:
            order_id: 订单ID

        返回值:
            dict: 报告数据
        """
        return self._post_project_api("/api/lims/order/project-order-report", order_id)

    def workflow_sample_locations(self, workflow_id: str) -> Dict[str, Any]:
        """查询工作流样品库位（站点项目接口）

        参数:
            workflow_id: 工作流ID

        返回值:
            dict: 位置信息数据
        """
        return self._post_project_api("/api/lims/storage/workflow-sample-locations", workflow_id)


    # 批量创建90%10%小瓶投料任务
    def batch_create_90_10_vial_feeding_tasks(self,
                                              titration,
                                              hold_m_name: str = None,
                                              speed: str = None,
                                              temperature: str = None,
                                              delay_time: str = None,
                                              liquid_material_name: str = "NMP") -> str:
        """
        批量创建90%10%小瓶投料任务（仅创建1个任务，但包含所有90%和10%物料）

        参数说明:
        - titration: 滴定信息的字典或JSON字符串，格式如下:
          {
              "name": "BTDA",
              "main_portion": 1.9152351915461294,  # 主称固体质量(g) -> 90%物料
              "titration_portion": 0.05923407808905555,  # 滴定固体质量(g) -> 10%物料固体
              "titration_solvent": 3.050555021586361  # 滴定溶液体积(mL) -> 10%物料液体
          }
        - hold_m_name: 库位名称，如"C01"。必填参数
        - speed: 搅拌速度，如果为None则使用默认值400
        - temperature: 温度，如果为None则使用默认值40
        - delay_time: 延迟时间，如果为None则使用默认值600
        - liquid_material_name: 10%物料的液体物料名称，默认为"NMP"

        返回: JSON字符串格式的任务创建结果

        异常:
        - BioyondException: 各种错误情况下的统一异常
        """
        try:
            # 参数类型转换：如果是字符串则解析为字典
            if isinstance(titration, str):
                try:
                    titration = json.loads(titration)
                except json.JSONDecodeError as e:
                    raise BioyondException(f"titration参数JSON解析失败: {str(e)}")

            # 参数验证
            if not isinstance(titration, dict):
                raise BioyondException("titration 必须是字典类型或有效的JSON字符串")

            if not hold_m_name:
                raise BioyondException("hold_m_name 是必填参数")

            if not titration:
                raise BioyondException("titration 参数不能为空")

            # 提取滴定数据
            name = titration.get("name")
            main_portion = titration.get("main_portion")  # 主称固体质量
            titration_portion = titration.get("titration_portion")  # 滴定固体质量
            titration_solvent = titration.get("titration_solvent")  # 滴定溶液体积

            if not all([name, main_portion is not None, titration_portion is not None, titration_solvent is not None]):
                raise BioyondException("titration 数据缺少必要参数")

            # 调用单个任务创建方法
            result = self.create_90_10_vial_feeding_task(
                order_name=f"90%10%小瓶投料-{name}",
                speed=speed,
                temperature=temperature,
                delay_time=delay_time,
                # 90%物料 - 主称固体直接使用main_portion
                percent_90_1_assign_material_name=name,
                percent_90_1_target_weigh=str(round(main_portion, 6)),
                # 10%物料 - 滴定固体 + 滴定溶剂（只使用第1个10%小瓶）
                percent_10_1_assign_material_name=name,
                percent_10_1_target_weigh=str(round(titration_portion, 6)),
                percent_10_1_volume=str(round(titration_solvent, 6)),
                percent_10_1_liquid_material_name=liquid_material_name,
                hold_m_name=hold_m_name
            )

            # 解析返回结果以获取order_code和order_id
            result_data = json.loads(result) if isinstance(result, str) else result
            order_code = result_data.get("order_code")
            order_id = result_data.get("order_id")
            order_params = result_data.get("order_params", {})

            # 构建详细信息（保持原有结构）
            detail = {
                "index": 1,
                "name": name,
                "success": True,
                "order_code": order_code,
                "order_id": order_id,
                "hold_m_name": hold_m_name,
                "90_vials": {
                    "count": 1,
                    "weight_per_vial": round(main_portion, 6),
                    "total_weight": round(main_portion, 6)
                },
                "10_vials": {
                    "count": 1,
                    "solid_weight": round(titration_portion, 6),
                    "liquid_volume": round(titration_solvent, 6)
                },
                "order_params": order_params
            }

            # 构建批量结果格式（与diamine_solution_tasks保持一致）
            summary = {
                "total": 1,
                "success": 1,
                "failed": 0,
                "order_codes": [order_code],
                "order_ids": [order_id],
                "details": [detail]
            }

            self.hardware_interface._logger.info(
                f"成功创建90%10%小瓶投料任务: {name}, order_code={order_code}, order_id={order_id}"
            )

            # 构建返回结果
            summary["return_info"] = {
                "order_codes": [order_code],
                "order_ids": [order_id],
            }

            return summary

        except BioyondException:
            raise
        except Exception as e:
            error_msg = f"批量创建90%10%小瓶投料任务时发生未预期的错误: {str(e)}"
            self.hardware_interface._logger.error(error_msg)
            raise BioyondException(error_msg)

    def _extract_actuals_from_report(self, report) -> Dict[str, Any]:
        data = report.get('data') if isinstance(report, dict) else None
        actual_target_weigh = None
        actual_volume = None
        if data:
            extra = data.get('extraProperties') or {}
            if isinstance(extra, dict):
                for v in extra.values():
                    obj = None
                    try:
                        obj = json.loads(v) if isinstance(v, str) else v
                    except Exception:
                        obj = None
                    if isinstance(obj, dict):
                        tw = obj.get('targetWeigh')
                        vol = obj.get('volume')
                        if tw is not None:
                            try:
                                actual_target_weigh = float(tw)
                            except Exception:
                                pass
                        if vol is not None:
                            try:
                                actual_volume = float(vol)
                            except Exception:
                                pass
        return {
            'actualTargetWeigh': actual_target_weigh,
            'actualVolume': actual_volume
        }

    # 等待多个任务完成并获取实验报告
    def wait_for_multiple_orders_and_get_reports(self,
                                                  batch_create_result: str = None,
                                                  timeout: int = 7200,
                                                  check_interval: int = 10) -> Dict[str, Any]:
        """
        同时等待多个任务完成并获取实验报告

        参数说明:
        - batch_create_result: 批量创建任务的返回结果JSON字符串，包含order_codes和order_ids数组
        - timeout: 超时时间（秒），默认7200秒（2小时）
        - check_interval: 检查间隔（秒），默认10秒

        返回: 包含所有任务状态和报告的字典
        {
            "total": 2,
            "completed": 2,
            "timeout": 0,
            "elapsed_time": 120.5,
            "reports": [
                {
                    "order_code": "task_vial_1",
                    "order_id": "uuid1",
                    "status": "completed",
                    "completion_status": 30,
                    "report": {...}
                },
                ...
            ]
        }

        异常:
        - BioyondException: 所有任务都超时或发生错误
        """
        try:
            # 参数类型转换
            timeout = int(timeout) if timeout else 7200
            check_interval = int(check_interval) if check_interval else 10

            # 验证batch_create_result参数
            if not batch_create_result or batch_create_result == "":
                raise BioyondException("batch_create_result参数为空，请确保从batch_create节点正确连接handle")

            # 解析batch_create_result JSON对象
            try:
                # 清理可能存在的截断标记 [...]
                if isinstance(batch_create_result, str) and '[...]' in batch_create_result:
                    batch_create_result = batch_create_result.replace('[...]', '[]')

                result_obj = json.loads(batch_create_result) if isinstance(batch_create_result, str) else batch_create_result

                # 兼容外层包装格式 {error, suc, return_value}
                if isinstance(result_obj, dict) and "return_value" in result_obj:
                    inner = result_obj.get("return_value")
                    if isinstance(inner, str):
                        result_obj = json.loads(inner)
                    elif isinstance(inner, dict):
                        result_obj = inner

                # 从summary对象中提取order_codes和order_ids
                order_codes = result_obj.get("order_codes", [])
                order_ids = result_obj.get("order_ids", [])

            except json.JSONDecodeError as e:
                raise BioyondException(f"解析batch_create_result失败: {e}")
            except Exception as e:
                raise BioyondException(f"处理batch_create_result时出错: {e}")

            # 验证提取的数据
            if not order_codes:
                raise BioyondException("batch_create_result中未找到order_codes字段或为空")
            if not order_ids:
                raise BioyondException("batch_create_result中未找到order_ids字段或为空")

            # 确保order_codes和order_ids是列表类型
            if not isinstance(order_codes, list):
                order_codes = [order_codes] if order_codes else []
            if not isinstance(order_ids, list):
                order_ids = [order_ids] if order_ids else []

            codes_list = order_codes
            ids_list = order_ids

            if len(codes_list) != len(ids_list):
                raise BioyondException(
                    f"order_codes数量({len(codes_list)})与order_ids数量({len(ids_list)})不匹配"
                )

            if not codes_list or not ids_list:
                raise BioyondException("order_codes和order_ids不能为空")

            # 初始化跟踪变量
            total = len(codes_list)
            pending_orders = {code: {"order_id": ids_list[i], "completed": False}
                            for i, code in enumerate(codes_list)}
            reports = []

            start_time = time.time()
            self.hardware_interface._logger.info(
                f"开始等待 {total} 个任务完成: {', '.join(codes_list)}"
            )

            # 轮询检查任务状态
            while pending_orders:
                elapsed_time = time.time() - start_time

                # 检查超时
                if elapsed_time > timeout:
                    # 收集超时任务
                    timeout_orders = list(pending_orders.keys())
                    self.hardware_interface._logger.error(
                        f"等待任务完成超时，剩余未完成任务: {', '.join(timeout_orders)}"
                    )

                    # 为超时任务添加记录
                    for order_code in timeout_orders:
                        reports.append({
                            "order_code": order_code,
                            "order_id": pending_orders[order_code]["order_id"],
                            "status": "timeout",
                            "completion_status": None,
                            "report": None,
                            "extracted": None,
                            "elapsed_time": elapsed_time
                        })

                    break

                # 检查每个待完成的任务
                completed_in_this_round = []
                for order_code in list(pending_orders.keys()):
                    order_id = pending_orders[order_code]["order_id"]

                    # 检查任务是否完成
                    if order_code in self.order_completion_status:
                        completion_info = self.order_completion_status[order_code]
                        self.hardware_interface._logger.info(
                            f"检测到任务 {order_code} 已完成，状态: {completion_info.get('status')}"
                        )

                        # 获取实验报告
                        try:
                            report = self.project_order_report(order_id)

                            if not report:
                                self.hardware_interface._logger.warning(
                                    f"任务 {order_code} 已完成但无法获取报告"
                                )
                                report = {"error": "无法获取报告"}
                            else:
                                self.hardware_interface._logger.info(
                                    f"成功获取任务 {order_code} 的实验报告"
                                )

                            reports.append({
                                "order_code": order_code,
                                "order_id": order_id,
                                "status": "completed",
                                "completion_status": completion_info.get('status'),
                                "report": report,
                                "extracted": self._extract_actuals_from_report(report),
                                "elapsed_time": elapsed_time
                            })

                            # 标记为已完成
                            completed_in_this_round.append(order_code)

                            # 清理完成状态记录
                            del self.order_completion_status[order_code]

                        except Exception as e:
                            self.hardware_interface._logger.error(
                                f"查询任务 {order_code} 报告失败: {str(e)}"
                            )
                            reports.append({
                                "order_code": order_code,
                                "order_id": order_id,
                                "status": "error",
                                "completion_status": completion_info.get('status'),
                                "report": None,
                                "extracted": None,
                                "error": str(e),
                                "elapsed_time": elapsed_time
                            })
                            completed_in_this_round.append(order_code)

                # 从待完成列表中移除已完成的任务
                for order_code in completed_in_this_round:
                    del pending_orders[order_code]

                # 如果还有待完成的任务，等待后继续
                if pending_orders:
                    time.sleep(check_interval)

                    # 每分钟记录一次等待状态
                    new_elapsed_time = time.time() - start_time
                    if int(new_elapsed_time) % 60 == 0 and new_elapsed_time > 0:
                        self.hardware_interface._logger.info(
                            f"批量等待任务中... 已完成 {len(reports)}/{total}, "
                            f"待完成: {', '.join(pending_orders.keys())}, "
                            f"已等待 {int(new_elapsed_time/60)} 分钟"
                        )

            # 统计结果
            completed_count = sum(1 for r in reports if r['status'] == 'completed')
            timeout_count = sum(1 for r in reports if r['status'] == 'timeout')
            error_count = sum(1 for r in reports if r['status'] == 'error')

            final_elapsed_time = time.time() - start_time

            summary = {
                "total": total,
                "completed": completed_count,
                "timeout": timeout_count,
                "error": error_count,
                "elapsed_time": round(final_elapsed_time, 2),
                "reports": reports
            }

            self.hardware_interface._logger.info(
                f"批量等待任务完成: 总数={total}, 成功={completed_count}, "
                f"超时={timeout_count}, 错误={error_count}, 耗时={final_elapsed_time:.1f}秒"
            )

            # 返回字典格式，在顶层包含统计信息
            return {
                "return_info": json.dumps(summary, ensure_ascii=False)
            }

        except BioyondException:
            raise
        except Exception as e:
            error_msg = f"批量等待任务完成时发生未预期的错误: {str(e)}"
            self.hardware_interface._logger.error(error_msg)
            raise BioyondException(error_msg)

    def process_order_finish_report(self, report_request, used_materials) -> Dict[str, Any]:
        """
        重写父类方法，处理任务完成报送并记录到 order_completion_status

        Args:
            report_request: WorkstationReportRequest 对象，包含任务完成信息
            used_materials: 物料使用记录列表

        Returns:
            Dict[str, Any]: 处理结果
        """
        try:
            # 调用父类方法
            result = super().process_order_finish_report(report_request, used_materials)

            # 记录任务完成状态
            data = report_request.data
            order_code = data.get('orderCode')

            if order_code:
                self.order_completion_status[order_code] = {
                    'status': data.get('status'),
                    'order_name': data.get('orderName'),
                    'timestamp': datetime.now().isoformat(),
                    'start_time': data.get('startTime'),
                    'end_time': data.get('endTime')
                }

                self.hardware_interface._logger.info(
                    f"已记录任务完成状态: {order_code}, status={data.get('status')}"
                )

            return result

        except Exception as e:
            self.hardware_interface._logger.error(f"处理任务完成报送失败: {e}")
            return {"processed": False, "error": str(e)}

    def transfer_materials_to_reaction_station(
        self,
        target_device_id: str,
        transfer_groups: list
    ) -> dict:
        """
        将配液站完成的物料转移到指定反应站的堆栈库位
        支持多组转移任务,每组包含物料名称、目标堆栈和目标库位

        Args:
            target_device_id: 目标反应站设备ID(所有转移组使用同一个设备)
            transfer_groups: 转移任务组列表,每组包含:
                - materials: 物料名称(字符串,将通过RPC查询)
                - target_stack: 目标堆栈名称(如"堆栈1左")
                - target_sites: 目标库位(如"A01")

        Returns:
            dict: 转移结果
                {
                    "success": bool,
                    "total_groups": int,
                    "successful_groups": int,
                    "failed_groups": int,
                    "target_device_id": str,
                    "details": [...]
                }
        """
        try:
            # 验证参数
            if not target_device_id:
                raise ValueError("目标设备ID不能为空")

            if not transfer_groups:
                raise ValueError("转移任务组列表不能为空")

            if not isinstance(transfer_groups, list):
                raise ValueError("transfer_groups必须是列表类型")

            # 标准化设备ID格式: 确保以 /devices/ 开头
            if not target_device_id.startswith("/devices/"):
                if target_device_id.startswith("/"):
                    target_device_id = f"/devices{target_device_id}"
                else:
                    target_device_id = f"/devices/{target_device_id}"

            self.hardware_interface._logger.info(
                f"目标设备ID标准化为: {target_device_id}"
            )

            self.hardware_interface._logger.info(
                f"开始执行批量物料转移: {len(transfer_groups)}组任务 -> {target_device_id}"
            )

            from .config import WAREHOUSE_MAPPING
            results = []
            successful_count = 0
            failed_count = 0

            for idx, group in enumerate(transfer_groups, 1):
                try:
                    # 提取参数
                    material_name = group.get("materials", "")
                    target_stack = group.get("target_stack", "")
                    target_sites = group.get("target_sites", "")

                    # 验证必填参数
                    if not material_name:
                        raise ValueError(f"第{idx}组: 物料名称不能为空")
                    if not target_stack:
                        raise ValueError(f"第{idx}组: 目标堆栈不能为空")
                    if not target_sites:
                        raise ValueError(f"第{idx}组: 目标库位不能为空")

                    self.hardware_interface._logger.info(
                        f"处理第{idx}组转移: {material_name} -> "
                        f"{target_device_id}/{target_stack}/{target_sites}"
                    )

                    # 通过物料名称从deck获取ResourcePLR对象
                    try:
                        material_resource = self.deck.get_resource(material_name)
                        if not material_resource:
                            raise ValueError(f"在deck中未找到物料: {material_name}")

                        self.hardware_interface._logger.info(
                            f"从deck获取到物料 {material_name}: {material_resource}"
                        )
                    except Exception as e:
                        raise ValueError(
                            f"获取物料 {material_name} 失败: {str(e)}，请确认物料已正确加载到deck中"
                        )

                    # 验证目标堆栈是否存在
                    if target_stack not in WAREHOUSE_MAPPING:
                        raise ValueError(
                            f"未知的堆栈名称: {target_stack}，"
                            f"可选值: {list(WAREHOUSE_MAPPING.keys())}"
                        )

                    # 验证库位是否有效
                    stack_sites = WAREHOUSE_MAPPING[target_stack].get("site_uuids", {})
                    if target_sites not in stack_sites:
                        raise ValueError(
                            f"库位 {target_sites} 不存在于堆栈 {target_stack} 中，"
                            f"可选库位: {list(stack_sites.keys())}"
                        )

                    # 获取目标库位的UUID
                    target_site_uuid = stack_sites[target_sites]
                    if not target_site_uuid:
                        raise ValueError(
                            f"库位 {target_sites} 的 UUID 未配置，请在 WAREHOUSE_MAPPING 中完善"
                        )

                    # 目标位点（包含UUID）
                    future = ROS2DeviceNode.run_async_func(
                        self._ros_node.get_resource_with_dir,
                        True,
                        **{
                            "resource_id": f"/reaction_station_bioyond/Bioyond_Deck/{target_stack}",
                            "with_children": True,
                        },
                    )
                    # 等待异步完成后再获取结果
                    if not future:
                        raise ValueError(f"获取目标堆栈资源future无效: {target_stack}")
                    while not future.done():
                        time.sleep(0.1)
                    target_site_resource = future.result()

                    # 调用父类的 transfer_resource_to_another 方法
                    # 传入ResourcePLR对象和目标位点资源
                    future = self.transfer_resource_to_another(
                        resource=[material_resource],
                        mount_resource=[target_site_resource],
                        sites=[target_sites],
                        mount_device_id=target_device_id
                    )

                    # 等待异步任务完成（轮询直到完成，再取结果）
                    if future:
                        try:
                            while not future.done():
                                time.sleep(0.1)
                            future.result()
                            self.hardware_interface._logger.info(
                                f"异步转移任务已完成: {material_name}"
                            )
                        except Exception as e:
                            raise ValueError(f"转移任务执行失败: {str(e)}")

                    self.hardware_interface._logger.info(
                        f"第{idx}组转移成功: {material_name} -> "
                        f"{target_device_id}/{target_stack}/{target_sites}"
                    )

                    successful_count += 1
                    results.append({
                        "group_index": idx,
                        "success": True,
                        "material_name": material_name,
                        "target_stack": target_stack,
                        "target_site": target_sites,
                        "message": "转移成功"
                    })

                except Exception as e:
                    error_msg = f"第{idx}组转移失败: {str(e)}"
                    self.hardware_interface._logger.error(error_msg)
                    failed_count += 1
                    results.append({
                        "group_index": idx,
                        "success": False,
                        "material_name": group.get("materials", ""),
                        "error": str(e)
                    })

            # 返回汇总结果
            return {
                "success": failed_count == 0,
                "total_groups": len(transfer_groups),
                "successful_groups": successful_count,
                "failed_groups": failed_count,
                "target_device_id": target_device_id,
                "details": results,
                "message": f"完成 {len(transfer_groups)} 组转移任务到 {target_device_id}: "
                          f"{successful_count} 成功, {failed_count} 失败"
            }

        except Exception as e:
            error_msg = f"批量转移物料失败: {str(e)}"
            self.hardware_interface._logger.error(error_msg)
            return {
                "success": False,
                "total_groups": len(transfer_groups) if transfer_groups else 0,
                "successful_groups": 0,
                "failed_groups": len(transfer_groups) if transfer_groups else 0,
                "target_device_id": target_device_id if target_device_id else "",
                "error": error_msg
            }

    def query_resource_by_name(self, material_name: str):
        """
        通过物料名称查询资源对象(适用于Bioyond系统)

        Args:
            material_name: 物料名称

        Returns:
            物料ID或None
        """
        try:
            # Bioyond系统使用material_cache存储物料信息
            if not hasattr(self.hardware_interface, 'material_cache'):
                self.hardware_interface._logger.error(
                    "hardware_interface没有material_cache属性"
                )
                return None

            material_cache = self.hardware_interface.material_cache

            self.hardware_interface._logger.info(
                f"查询物料 '{material_name}', 缓存中共有 {len(material_cache)} 个物料"
            )

            # 调试: 打印前几个物料信息
            if material_cache:
                cache_items = list(material_cache.items())[:5]
                for name, material_id in cache_items:
                    self.hardware_interface._logger.debug(
                        f"缓存物料: name={name}, id={material_id}"
                    )

            # 直接从缓存中查找
            if material_name in material_cache:
                material_id = material_cache[material_name]
                self.hardware_interface._logger.info(
                    f"找到物料: {material_name} -> ID: {material_id}"
                )
                return material_id

            self.hardware_interface._logger.warning(
                f"未找到物料: {material_name} (缓存中无此物料)"
            )

            # 打印所有可用物料名称供参考
            available_materials = list(material_cache.keys())
            if available_materials:
                self.hardware_interface._logger.info(
                    f"可用物料列表(前10个): {available_materials[:10]}"
                )

            return None

        except Exception as e:
            self.hardware_interface._logger.error(
                f"查询物料失败 {material_name}: {str(e)}"
            )
            return None


if __name__ == "__main__":
    bioyond = BioyondDispensingStation(config={
        "api_key": "DE9BDDA0",
        "api_host": "http://192.168.1.200:44388"
    })

    # ============ 原有示例代码 ============

    # 示例1：使用material_id_query查询工作流对应的holdMID
    workflow_id_1 = "3a15d4a1-3bbe-76f9-a458-292896a338f5"  # 二胺溶液配置工作流ID
    workflow_id_2 = "3a19310d-16b9-9d81-b109-0748e953694b"  # 90%10%小瓶投料工作流ID

    #示例2：创建二胺溶液配置任务 - ODA，指定库位名称
    # bioyond.create_diamine_solution_task(
    #         order_code="task_oda_" + str(int(datetime.now().timestamp())),
    #         order_name="二胺溶液配置-ODA",
    #         material_name="ODA-1",
    #         target_weigh="12.000",
    #         volume="60",
    #         liquid_material_name= "NMP",
    #         speed="400",
    #         temperature="20",
    #         delay_time="600",
    #         hold_m_name="烧杯ODA"
    #     )

    # bioyond.create_diamine_solution_task(
    #         order_code="task_pda_" + str(int(datetime.now().timestamp())),
    #         order_name="二胺溶液配置-PDA",
    #         material_name="PDA-1",
    #         target_weigh="4.178",
    #         volume="60",
    #         liquid_material_name= "NMP",
    #         speed="400",
    #         temperature="20",
    #         delay_time="600",
    #         hold_m_name="烧杯PDA-2"
    #     )

    # bioyond.create_diamine_solution_task(
    #         order_code="task_mpda_" + str(int(datetime.now().timestamp())),
    #         order_name="二胺溶液配置-MPDA",
    #         material_name="MPDA-1",
    #         target_weigh="3.298",
    #         volume="50",
    #         liquid_material_name= "NMP",
    #         speed="400",
    #         temperature="20",
    #         delay_time="600",
    #         hold_m_name="烧杯MPDA"
    #     )

    bioyond.material_id_query("3a19310d-16b9-9d81-b109-0748e953694b")
    bioyond.material_id_query("3a15d4a1-3bbe-76f9-a458-292896a338f5")


    #示例4：创建90%10%小瓶投料任务
    # vial_result = bioyond.create_90_10_vial_feeding_task(
    #     order_code="task_vial_" + str(int(datetime.now().timestamp())),
    #     order_name="90%10%小瓶投料-1",
    #     percent_90_1_assign_material_name="BTDA-1",
    #     percent_90_1_target_weigh="7.392",
    #     percent_90_2_assign_material_name="BTDA-1",
    #     percent_90_2_target_weigh="7.392",
    #     percent_90_3_assign_material_name="BTDA-2",
    #     percent_90_3_target_weigh="7.392",
    #     percent_10_1_assign_material_name="BTDA-2",
    #     percent_10_1_target_weigh="1.500",
    #     percent_10_1_volume="20",
    #     percent_10_1_liquid_material_name="NMP",
    #     # percent_10_2_assign_material_name="BTDA-c",
    #     # percent_10_2_target_weigh="1.2",
    #     # percent_10_2_volume="20",
    #     # percent_10_2_liquid_material_name="NMP",
    #     speed="400",
    #     temperature="60",
    #     delay_time="1200",
    #     hold_m_name="8.4分装板-1"
    #     )

    # vial_result = bioyond.create_90_10_vial_feeding_task(
    #     order_code="task_vial_" + str(int(datetime.now().timestamp())),
    #     order_name="90%10%小瓶投料-2",
    #     percent_90_1_assign_material_name="BPDA-1",
    #     percent_90_1_target_weigh="5.006",
    #     percent_90_2_assign_material_name="PMDA-1",
    #     percent_90_2_target_weigh="3.810",
    #     percent_90_3_assign_material_name="BPDA-1",
    #     percent_90_3_target_weigh="8.399",
    #     percent_10_1_assign_material_name="BPDA-1",
    #     percent_10_1_target_weigh="1.200",
    #     percent_10_1_volume="20",
    #     percent_10_1_liquid_material_name="NMP",
    #     percent_10_2_assign_material_name="BPDA-1",
    #     percent_10_2_target_weigh="1.200",
    #     percent_10_2_volume="20",
    #     percent_10_2_liquid_material_name="NMP",
    #     speed="400",
    #     temperature="60",
    #     delay_time="1200",
    #     hold_m_name="8.4分装板-2"
    #     )

    #启动调度器
    #bioyond.scheduler_start()

    #继续调度器
    #bioyond.scheduler_continue()

    result0 = bioyond.stock_material('{"typeMode": 0, "includeDetail": true}')
    result1 = bioyond.stock_material('{"typeMode": 1, "includeDetail": true}')
    result2 = bioyond.stock_material('{"typeMode": 2, "includeDetail": true}')

    matpos1 = bioyond.query_warehouse_by_material_type("3a14196e-b7a0-a5da-1931-35f3000281e9")
    matpos2 = bioyond.query_warehouse_by_material_type("3a14196e-5dfe-6e21-0c79-fe2036d052c4")
    matpos3 = bioyond.query_warehouse_by_material_type("3a14196b-24f2-ca49-9081-0cab8021bf1a")

    #样品板（里面有样品瓶）
    material_data_yp = {
    "typeId": "3a14196e-b7a0-a5da-1931-35f3000281e9",
    #"code": "物料编码001",
    #"barCode": "物料条码001",
    "name": "8.4样品板",
    "unit": "个",
    "quantity": 1,
    "details": [
        {
        "typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
        #"code": "物料编码001",
        "name": "BTDA-1",
        "quantity": 20,
        "x": 1,
        "y": 1,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
        #"code": "物料编码001",
        "name": "BPDA-1",
        "quantity": 20,
        "x": 2,
        "y": 1, #x1y2是A02
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
        #"code": "物料编码001",
        "name": "BTDA-2",
        "quantity": 20,
        "x": 1,
        "y": 2, #x1y2是A02
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
        #"code": "物料编码001",
        "name": "PMDA-1",
        "quantity": 20,
        "x": 2,
        "y": 2, #x1y2是A02
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        }
    ],
    "Parameters":"{}"
    }

    material_data_yp = {
    "typeId": "3a14196e-b7a0-a5da-1931-35f3000281e9",
    #"code": "物料编码001",
    #"barCode": "物料条码001",
    "name": "8.7样品板",
    "unit": "个",
    "quantity": 1,
    "details": [
        {
        "typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
        #"code": "物料编码001",
        "name": "mianfen",
        "quantity": 13,
        "x": 1,
        "y": 1,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196a-cf7d-8aea-48d8-b9662c7dba94",
        #"code": "物料编码001",
        "name": "mianfen2",
        "quantity": 13,
        "x": 1,
        "y": 2, #x1y2是A02
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        }
    ],
    "Parameters":"{}"
    }

    #分装板
    material_data_fzb_1 = {
    "typeId": "3a14196e-5dfe-6e21-0c79-fe2036d052c4",
    #"code": "物料编码001",
    #"barCode": "物料条码001",
    "name": "8.7分装板",
    "unit": "个",
    "quantity": 1,
    "details": [
        {
        "typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
        #"code": "物料编码001",
        "name": "10%小瓶1",
        "quantity": 1,
        "x": 1,
        "y": 1,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
        #"code": "物料编码001",
        "name": "10%小瓶2",
        "quantity": 1,
        "x": 1,
        "y": 2,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
        #"code": "物料编码001",
        "name": "10%小瓶3",
        "quantity": 1,
        "x": 1,
        "y": 3,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
        #"code": "物料编码001",
        "name": "90%小瓶1",
        "quantity": 1,
        "x": 2,
        "y": 1, #x1y2是A02
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
        #"code": "物料编码001",
        "name": "90%小瓶2",
        "quantity": 1,
        "x": 2,
        "y": 2,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
        #"code": "物料编码001",
        "name": "90%小瓶3",
        "quantity": 1,
        "x": 2,
        "y": 3,
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        }
    ],
    "Parameters":"{}"
    }

    material_data_fzb_2 = {
    "typeId": "3a14196e-5dfe-6e21-0c79-fe2036d052c4",
    #"code": "物料编码001",
    #"barCode": "物料条码001",
    "name": "8.4分装板-2",
    "unit": "个",
    "quantity": 1,
    "details": [
        {
        "typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
        #"code": "物料编码001",
        "name": "10%小瓶1",
        "quantity": 1,
        "x": 1,
        "y": 1,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
        #"code": "物料编码001",
        "name": "10%小瓶2",
        "quantity": 1,
        "x": 1,
        "y": 2,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-76be-2279-4e22-7310d69aed68",
        #"code": "物料编码001",
        "name": "10%小瓶3",
        "quantity": 1,
        "x": 1,
        "y": 3,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
        #"code": "物料编码001",
        "name": "90%小瓶1",
        "quantity": 1,
        "x": 2,
        "y": 1, #x1y2是A02
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
        #"code": "物料编码001",
        "name": "90%小瓶2",
        "quantity": 1,
        "x": 2,
        "y": 2,
        #"unit": "单位"
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        },
        {
        "typeId": "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea",
        #"code": "物料编码001",
        "name": "90%小瓶3",
        "quantity": 1,
        "x": 2,
        "y": 3,
        "molecular": 1,
        "Parameters":"{\"molecular\": 1}"
        }
    ],
    "Parameters":"{}"
    }

    #烧杯
    material_data_sb_oda = {
    "typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
    #"code": "物料编码001",
    #"barCode": "物料条码001",
    "name": "mianfen1",
    "unit": "个",
    "quantity": 1,
    "Parameters":"{}"
    }

    material_data_sb_pda_2 = {
    "typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
    #"code": "物料编码001",
    #"barCode": "物料条码001",
    "name": "mianfen2",
    "unit": "个",
    "quantity": 1,
    "Parameters":"{}"
    }

    # material_data_sb_mpda = {
    # "typeId": "3a14196b-24f2-ca49-9081-0cab8021bf1a",
    # #"code": "物料编码001",
    # #"barCode": "物料条码001",
    # "name": "烧杯MPDA",
    # "unit": "个",
    # "quantity": 1,
    # "Parameters":"{}"
    # }


    #result_1 = bioyond.add_material(json.dumps(material_data_yp, ensure_ascii=False))
    #result_2 = bioyond.add_material(json.dumps(material_data_fzb_1, ensure_ascii=False))
    # result_3 = bioyond.add_material(json.dumps(material_data_fzb_2, ensure_ascii=False))
    # result_4 = bioyond.add_material(json.dumps(material_data_sb_oda, ensure_ascii=False))
    # result_5 = bioyond.add_material(json.dumps(material_data_sb_pda_2, ensure_ascii=False))
    # #result会返回id
    # #样品板1id：3a1b3e7d-339d-0291-dfd3-13e2a78fe521


    # #将指定物料入库到指定库位
    #bioyond.material_inbound(result_1, "3a14198e-6929-31f0-8a22-0f98f72260df")
    #bioyond.material_inbound(result_2, "3a14198e-6929-46fe-841e-03dd753f1e4a")
    # bioyond.material_inbound(result_3, "3a14198e-6929-72ac-32ce-9b50245682b8")
    # bioyond.material_inbound(result_4, "3a14198e-d724-e036-afdc-2ae39a7f3383")
    # bioyond.material_inbound(result_5, "3a14198e-d724-d818-6d4f-5725191a24b5")

    #bioyond.material_outbound(result_1, "3a14198e-6929-31f0-8a22-0f98f72260df")

    # bioyond.stock_material('{"typeMode": 2, "includeDetail": true}')

    query_order = {"status":"100", "pageCount": "10"}
    bioyond.order_query(json.dumps(query_order, ensure_ascii=False))

    # id = "3a1bce3c-4f31-c8f3-5525-f3b273bc34dc"
    # bioyond.sample_waste_removal(id)
