diff --git a/.gitignore b/.gitignore index 838331e3..8a224270 100644 --- a/.gitignore +++ b/.gitignore @@ -250,4 +250,5 @@ ros-humble-unilabos-msgs-0.9.13-h6403a04_5.tar.bz2 *.bz2 test_config.py - +# 本地 mock API,不参与上游 PR +/mock_server.py 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/test_eit_workflow.py b/test_eit_workflow.py new file mode 100644 index 00000000..1f9c9f22 --- /dev/null +++ b/test_eit_workflow.py @@ -0,0 +1,393 @@ +#!/usr/bin/env python3 +""" +EIT 跨设备工作流数据传递 & AGV Deck 扁平化上报 本地测试脚本 + +测试目标: + 问题1: 验证 result/handles 注册表声明是否正确,模拟跨设备 manifest 数据流 + 问题2: 验证 AGV Deck 扁平化上报逻辑(有物料时显示,空位不显示) + +运行方式: + cd /Users/fish/Documents/GitHub/Uni-Lab-OS + python3 test_eit_workflow.py + +不发送任何真实硬件命令,不启动 ROS,不连接工站/AGV。 +""" + +import sys +import os +import json +import yaml +import logging + +sys.path.insert(0, os.path.dirname(__file__)) + +logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") +logger = logging.getLogger("test_eit_workflow") + +PASS = "\033[92m✅ PASS\033[0m" +FAIL = "\033[91m❌ FAIL\033[0m" +INFO = "\033[94mℹ️\033[0m" + +results = [] + +def check(name: str, condition: bool, detail: str = ""): + status = PASS if condition else FAIL + results.append(condition) + msg = f" {status} {name}" + if detail: + msg += f" — {detail}" + print(msg) + return condition + + +# ===================================================================== +# 问题 1:跨设备注册表 result/handles 数据流 +# ===================================================================== +print("\n" + "=" * 70) +print("问题 1:跨设备注册表 result/handles 数据流验证") +print("=" * 70) + +# 1.1 加载注册表 +print(f"\n{INFO} 加载注册表...") +with open("unilabos/registry/devices/eit_synthesis_station.yaml") as f: + ss_reg = yaml.safe_load(f) +with open("unilabos/registry/devices/eit_agv.yaml") as f: + agv_reg = yaml.safe_load(f) + +ss_avm = ss_reg["eit_synthesis_station"]["class"]["action_value_mappings"] +agv_avm = agv_reg["eit_agv"]["class"]["action_value_mappings"] + +# 1.2 检查 prepare_batch_in_with_agv_manifest 的 output handle +print(f"\n{INFO} [Step 1] 检查 prepare_batch_in_with_agv_manifest...") +prepare = ss_avm["prepare_batch_in_with_agv_manifest"] +check("result 声明包含 manifest", + prepare.get("result", {}).get("manifest") == "manifest") +check("handles.output 存在且非空", + len(prepare.get("handles", {}).get("output", [])) > 0) +if prepare.get("handles", {}).get("output"): + out = prepare["handles"]["output"][0] + check("output.handler_key == 'manifest'", out["handler_key"] == "manifest") + check("output.data_source == 'executor'", out["data_source"] == "executor") + check("output.data_type == 'object'", out["data_type"] == "object") + +# 1.3 检查 transfer_manifest 的 input/output handle +print(f"\n{INFO} [Step 2] 检查 transfer_manifest...") +transfer = agv_avm["transfer_manifest"] +check("result 声明包含 transfer_result", + transfer.get("result", {}).get("transfer_result") == "transfer_result") +check("handles.input 存在且非空", + len(transfer.get("handles", {}).get("input", [])) > 0) +if transfer.get("handles", {}).get("input"): + inp = transfer["handles"]["input"][0] + check("input.handler_key == 'manifest'", inp["handler_key"] == "manifest") + check("input.data_source == 'handle'", inp["data_source"] == "handle") + check("input.data_type == 'object'", inp["data_type"] == "object") +check("handles.output 存在且非空", + len(transfer.get("handles", {}).get("output", [])) > 0) + +# 1.4 检查 execute_batch_in_payload 的 input handle +print(f"\n{INFO} [Step 3] 检查 execute_batch_in_payload...") +execute = ss_avm["execute_batch_in_payload"] +check("result 声明包含 success", + execute.get("result", {}).get("success") == "success") +check("handles.input 存在且非空", + len(execute.get("handles", {}).get("input", [])) > 0) +if execute.get("handles", {}).get("input"): + inp = execute["handles"]["input"][0] + check("input.handler_key == 'batch_in_payload'", + inp["handler_key"] == "batch_in_payload") + check("input.data_source == 'handle'", inp["data_source"] == "handle") + +# 1.5 检查 unload_task_and_empty_trays_return_manifest 的 output handle +print(f"\n{INFO} [Step 4] 检查 unload_task_and_empty_trays_return_manifest...") +unload = ss_avm["unload_task_and_empty_trays_return_manifest"] +check("result 声明包含 manifest", + unload.get("result", {}).get("manifest") == "manifest") +check("handles.output 存在且非空", + len(unload.get("handles", {}).get("output", [])) > 0) + +# 1.6 模拟工作流连线验证 +print(f"\n{INFO} [Step 5] 模拟前端工作流连线...") + +# 模拟 test_mode 下 _build_test_mode_return 的逻辑 +def build_mock_return(action_mapping: dict, action_name: str) -> dict: + """复刻 host_node._build_test_mode_return 的逻辑""" + mock_return = {"test_mode": True, "action_name": action_name} + handles = action_mapping.get("handles", {}) + if isinstance(handles, dict): + for output_handle in handles.get("output", []): + data_key = output_handle.get("data_key", "") + handler_key = output_handle.get("handler_key", "") + flatten_count = data_key.count("@flatten") + value = {} + for _ in range(flatten_count): + value = [value] + mock_return[handler_key] = value + return mock_return + +# 模拟上料流程 +print(f"\n {'─' * 50}") +print(f" 模拟上料流程: prepare → transfer → execute") +print(f" {'─' * 50}") + +# Step A: prepare_batch_in_with_agv_manifest → 返回 manifest +mock_prepare = build_mock_return(prepare, "prepare_batch_in_with_agv_manifest") +logger.info(f"[prepare] test_mode mock return: {json.dumps(mock_prepare, ensure_ascii=False)}") +check("prepare mock 返回包含 manifest 键", "manifest" in mock_prepare, + f"keys = {list(mock_prepare.keys())}") + +# Step B: transfer_manifest ← 接收 manifest (from prepare) +# 框架会把上游 output handler_key='manifest' 的值传给下游 input handler_key='manifest' +downstream_goal = {"manifest": mock_prepare.get("manifest", {})} +logger.info(f"[transfer] 从上游接收 goal: {json.dumps(downstream_goal, ensure_ascii=False)}") +check("transfer 接收到的 manifest 不为 None", downstream_goal["manifest"] is not None) + +mock_transfer = build_mock_return(transfer, "transfer_manifest") +logger.info(f"[transfer] test_mode mock return: {json.dumps(mock_transfer, ensure_ascii=False)}") +check("transfer mock 返回包含 transfer_result 键", "transfer_result" in mock_transfer) + +# Step C: execute_batch_in_payload ← 接收 batch_in_payload +# 注意: batch_in_payload 来自 manifest 内部,前端连线时用户需要从 prepare 输出中绑定 +execute_goal = {"batch_in_payload": [{"tray_code": "REACTION_TUBE_TRAY_2ML", "layout_code": "TB-1-1"}]} +logger.info(f"[execute] goal: {json.dumps(execute_goal, ensure_ascii=False)}") +check("execute 接收到的 batch_in_payload 是 list", isinstance(execute_goal["batch_in_payload"], list)) + +# 模拟下料流程 +print(f"\n {'─' * 50}") +print(f" 模拟下料流程: unload → transfer") +print(f" {'─' * 50}") + +mock_unload = build_mock_return(unload, "unload_task_and_empty_trays_return_manifest") +logger.info(f"[unload] test_mode mock return: {json.dumps(mock_unload, ensure_ascii=False)}") +check("unload mock 返回包含 manifest 键", "manifest" in mock_unload) + +downstream_goal_2 = {"manifest": mock_unload.get("manifest", {})} +logger.info(f"[transfer] 从 unload 接收 goal: {json.dumps(downstream_goal_2, ensure_ascii=False)}") +check("下料 transfer 接收到的 manifest 不为 None", downstream_goal_2["manifest"] is not None) + +# 1.7 验证框架 convert_from_json 能正确解析 handles +print(f"\n{INFO} [Step 6] 验证框架 handle 解析逻辑...") +try: + # 复刻 convert_from_json.py 中的 get_action_handles 逻辑 + def get_action_handles(action_mapping: dict) -> dict: + result = {"source": [], "target": []} + handles = action_mapping.get("handles", {}) + if isinstance(handles, dict): + for handle in handles.get("input", []): + hk = handle.get("handler_key", "") + if hk: + result["source"].append(hk) + for handle in handles.get("output", []): + hk = handle.get("handler_key", "") + if hk: + result["target"].append(hk) + return result + + prepare_handles = get_action_handles(prepare) + check("prepare output handles: ['manifest']", + prepare_handles["target"] == ["manifest"], + f"实际: {prepare_handles}") + + transfer_handles = get_action_handles(transfer) + check("transfer input handles: ['manifest']", + transfer_handles["source"] == ["manifest"], + f"实际: {transfer_handles}") + check("transfer output handles: ['transfer_result']", + transfer_handles["target"] == ["transfer_result"], + f"实际: {transfer_handles}") + + execute_handles = get_action_handles(execute) + check("execute input handles: ['batch_in_payload']", + execute_handles["source"] == ["batch_in_payload"], + f"实际: {execute_handles}") + + unload_handles = get_action_handles(unload) + check("unload output handles: ['manifest']", + unload_handles["target"] == ["manifest"], + f"实际: {unload_handles}") + +except Exception as e: + check("框架 handle 解析无异常", False, str(e)) + + +# ===================================================================== +# 问题 2:AGV Deck 物料同步 + 扁平化上报 +# ===================================================================== +print("\n" + "=" * 70) +print("问题 2:AGV Deck 结构 + 扁平化上报验证") +print("=" * 70) + +# 2.1 验证 Deck 结构 +print(f"\n{INFO} [Step 1] 验证 AGV Deck 结构...") +from unilabos.resources.eit_agv.decks import EIT_AGV_Deck + +deck = EIT_AGV_Deck(setup=True) +check("Deck 有且仅有 1 个子节点(WareHouse)", + len(deck.children) == 1, + f"实际: {len(deck.children)} 个子节点") + +wh = deck.children[0] +check(f"WareHouse 名称为 'AGV'", + wh.name == "AGV", + f"实际: {wh.name}") + +check("WareHouse 有 4 个槽位(1×4 车载)", + len(wh.children) == 4, + f"实际: {len(wh.children)} 个") + +slot_names = [c.name for c in wh.children] +expected_names = ["AGV-1", "AGV-2", "AGV-3", "AGV-4"] +check(f"槽位名称: {expected_names}", + slot_names == expected_names, + f"实际: {slot_names}") + +# 验证坐标不重叠 +coords = [str(c.location) for c in wh.children] +check("所有槽位坐标不重叠", + len(set(coords)) == 4, + f"坐标: {coords}") + +# 2.2 验证扁平化上报逻辑(空 Deck) +print(f"\n{INFO} [Step 2] 验证扁平化上报 — 空 Deck(所有槽位无物料)...") + +def flatten_deck(deck_obj) -> dict: + """ + 复刻 agv_controller._upload_agv_deck_flattened 的扁平化核心逻辑。 + 不依赖框架内部序列化——只模拟结构遍历: + 遍历 Deck → WareHouse → ResourceHolder → Carrier, + 只提取有 Carrier 的 ResourceHolder 的子节点。 + """ + flat_wh_list = [] + for wh_obj in deck_obj.children: + flat_carriers = [] + for rh in wh_obj.children: + for carrier in rh.children: + flat_carriers.append({"name": carrier.name, "type": type(carrier).__name__}) + flat_wh_list.append({"name": wh_obj.name, "children": flat_carriers}) + return {"name": deck_obj.name, "children": flat_wh_list} + +flat_empty = flatten_deck(deck) +agv_wh_children = flat_empty["children"][0]["children"] # WareHouse AGV 的扁平化子节点 +check("空 Deck 扁平化后 WareHouse 无子节点", + len(agv_wh_children) == 0, + f"实际: {len(agv_wh_children)} 个子节点") +print(f" {INFO} 前端效果: eit_agv → EIT_AGV_Deck → AGV → (空,不显示任何槽位)") + +# 2.3 验证扁平化上报逻辑(有物料) +print(f"\n{INFO} [Step 3] 验证扁平化上报 — 模拟放置物料...") + +from unilabos.resources.eit_synthesis_station import bottle_carriers + +# 模拟给 AGV-1 放一个 FLASH_FILTER_OUTER_BOTTLE_TRAY +slot_1 = wh.children[0] # AGV-1 +carrier = bottle_carriers.EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY( + name=f"FLASH_FILTER_OUTER_BOTTLE_TRAY@{slot_1.name}" +) +slot_1.assign_child_resource(carrier) +check(f"AGV-1 已挂载物料", + len(slot_1.children) == 1, + f"carrier: {carrier.name}") + +# 模拟给 AGV-3 放一个 REAGENT_BOTTLE_TRAY_125ML +slot_3 = wh.children[2] # AGV-3 +carrier_3 = bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_125ML( + name=f"REAGENT_BOTTLE_TRAY_125ML@{slot_3.name}" +) +slot_3.assign_child_resource(carrier_3) +check(f"AGV-3 已挂载物料", + len(slot_3.children) == 1) + +# 扁平化后:只有 AGV-1 和 AGV-3 的 Carrier 显示 +flat_with_materials = flatten_deck(deck) +agv_wh_children = flat_with_materials["children"][0]["children"] +check("扁平化后 WareHouse 有 2 个子节点(仅有物料的槽位)", + len(agv_wh_children) == 2, + f"实际: {len(agv_wh_children)} 个") + +carrier_names = [c["name"] for c in agv_wh_children] +check("子节点是 Carrier(非 ResourceHolder)", + all("TRAY" in n or "BOTTLE" in n for n in carrier_names), + f"名称: {carrier_names}") + +print(f" {INFO} 前端效果:") +print(f" eit_agv → EIT_AGV_Deck → AGV") +for c in agv_wh_children: + print(f" └─ {c['name']}") +print(f" (AGV-2, AGV-4 为空,不显示)") + +# 2.4 验证物料移除后扁平化 +print(f"\n{INFO} [Step 4] 验证移除物料后扁平化...") +slot_1.unassign_child_resource(carrier) +check("AGV-1 物料已移除", len(slot_1.children) == 0) + +flat_after_remove = flatten_deck(deck) +agv_wh_children = flat_after_remove["children"][0]["children"] +check("移除后 WareHouse 只剩 1 个子节点", + len(agv_wh_children) == 1, + f"剩余: {[c['name'] for c in agv_wh_children]}") + +# 2.5 验证 CARRIER_FACTORY 全量覆盖 +print(f"\n{INFO} [Step 5] 验证载架工厂全覆盖(13 种物料类型)...") + +FACTORY_MAP = { + # 与 ResourceCode 枚举中所有 *_TRAY_* 对应的载架工厂 + "REACTION_TUBE_TRAY_2ML": bottle_carriers.EIT_REACTION_TUBE_TRAY_2ML, + "TEST_TUBE_MAGNET_TRAY_2ML": bottle_carriers.EIT_TEST_TUBE_MAGNET_TRAY_2ML, + "REACTION_SEAL_CAP_TRAY": bottle_carriers.EIT_REACTION_SEAL_CAP_TRAY, + "FLASH_FILTER_INNER_BOTTLE_TRAY": bottle_carriers.EIT_FLASH_FILTER_INNER_BOTTLE_TRAY, + "FLASH_FILTER_OUTER_BOTTLE_TRAY": bottle_carriers.EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY, + "TIP_TRAY_50UL": bottle_carriers.EIT_TIP_TRAY_50UL, + "TIP_TRAY_1ML": bottle_carriers.EIT_TIP_TRAY_1ML, + "TIP_TRAY_5ML": bottle_carriers.EIT_TIP_TRAY_5ML, + "POWDER_BUCKET_TRAY_30ML": bottle_carriers.EIT_POWDER_BUCKET_TRAY_30ML, + "REAGENT_BOTTLE_TRAY_2ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_2ML, + "REAGENT_BOTTLE_TRAY_8ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_8ML, + "REAGENT_BOTTLE_TRAY_40ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_40ML, + "REAGENT_BOTTLE_TRAY_125ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_125ML, +} + +all_ok = True +for name, factory in FACTORY_MAP.items(): + try: + c = factory(name=f"test@AGV-1") + ok = c is not None and hasattr(c, "name") + if not ok: + all_ok = False + except Exception as e: + all_ok = False + print(f" {FAIL} {name}: {e}") + +check(f"全部 {len(FACTORY_MAP)} 种物料类型创建成功", all_ok) + + +# ===================================================================== +# 总结 +# ===================================================================== +print("\n" + "=" * 70) +total = len(results) +passed = sum(results) +failed = total - passed +if failed == 0: + print(f"\033[92m全部通过: {passed}/{total} 项检查\033[0m") +else: + print(f"\033[91m有 {failed} 项失败: {passed}/{total} 项通过\033[0m") +print("=" * 70) + +print(f""" +上料连线图: + ┌──────────────────────────────────────┐ ┌─────────────────────┐ ┌──────────────────────────────────┐ + │ eit_synthesis_station │ │ eit_agv │ │ eit_synthesis_station │ + │ prepare_batch_in_with_agv_manifest │ │ transfer_manifest │ │ execute_batch_in_payload │ + │ │ │ │ │ │ + │ output: manifest ─────────────────────────→ input: manifest │ │ input: batch_in_payload │ + │ │ │ output: transfer │ │ │ + └──────────────────────────────────────┘ └─────────────────────┘ └──────────────────────────────────┘ + +下料连线图: + ┌──────────────────────────────────────────────┐ ┌─────────────────────┐ + │ eit_synthesis_station │ │ eit_agv │ + │ unload_task_and_empty_trays_return_manifest │ │ transfer_manifest │ + │ │ │ │ + │ output: manifest ─────────────────────────────────→ input: manifest │ + └──────────────────────────────────────────────┘ └─────────────────────┘ +""") diff --git a/unilabos/app/main.py b/unilabos/app/main.py index 93751262..12afb0b7 100644 --- a/unilabos/app/main.py +++ b/unilabos/app/main.py @@ -414,8 +414,13 @@ def main(): os._exit(0) if not BasicConfig.ak or not BasicConfig.sk: - print_status("后续运行必须拥有一个实验室,请前往 https://uni-lab.bohrium.com 注册实验室!", "warning") - os._exit(1) + if BasicConfig.test_mode: + print_status("测试模式:跳过 ak/sk 检查,使用占位凭据", "warning") + BasicConfig.ak = BasicConfig.ak or "test_ak" + BasicConfig.sk = BasicConfig.sk or "test_sk" + else: + print_status("后续运行必须拥有一个实验室,请前往 https://uni-lab.bohrium.com 注册实验室!", "warning") + os._exit(1) graph: nx.Graph resource_tree_set: ResourceTreeSet resource_links: List[Dict[str, Any]] diff --git a/unilabos/compile/__init__.py b/unilabos/compile/__init__.py index 51ca9a2d..fcd4641b 100644 --- a/unilabos/compile/__init__.py +++ b/unilabos/compile/__init__.py @@ -5,6 +5,7 @@ from .evaporate_protocol import generate_evaporate_protocol from .evacuateandrefill_protocol import generate_evacuateandrefill_protocol from .agv_transfer_protocol import generate_agv_transfer_protocol +from .batch_transfer_protocol import generate_batch_transfer_protocol from .add_protocol import generate_add_protocol from .centrifuge_protocol import generate_centrifuge_protocol from .filter_protocol import generate_filter_protocol @@ -31,6 +32,7 @@ action_protocol_generators = { AddProtocol: generate_add_protocol, AGVTransferProtocol: generate_agv_transfer_protocol, + BatchTransferProtocol: generate_batch_transfer_protocol, AdjustPHProtocol: generate_adjust_ph_protocol, CentrifugeProtocol: generate_centrifuge_protocol, CleanProtocol: generate_clean_protocol, diff --git a/unilabos/compile/_agv_utils.py b/unilabos/compile/_agv_utils.py new file mode 100644 index 00000000..a9e40e58 --- /dev/null +++ b/unilabos/compile/_agv_utils.py @@ -0,0 +1,127 @@ +""" +AGV 编译器共用工具函数 + +从 physical_setup_graph 中发现 AGV 节点配置, +供 agv_transfer_protocol 和 batch_transfer_protocol 复用。 +""" + +from typing import Any, Dict, List, Optional + +import networkx as nx + + +def find_agv_config(G: nx.Graph, agv_id: Optional[str] = None) -> Dict[str, Any]: + """从设备图中发现 AGV 节点,返回其配置 + + 查找策略: + 1. 如果指定 agv_id,直接读取该节点 + 2. 否则查找 class 为 "agv_transport_station" 的节点 + 3. 兜底查找 config 中包含 device_roles 的 workstation 节点 + + Returns: + { + "agv_id": str, + "device_roles": {"navigator": "...", "arm": "..."}, + "route_table": {"A->B": {"nav_command": ..., "arm_pick": ..., "arm_place": ...}}, + "capacity": int, + } + """ + if agv_id and agv_id in G.nodes: + node_data = G.nodes[agv_id] + config = _extract_config(node_data) + if config and "device_roles" in config: + return _build_agv_cfg(agv_id, config, G) + + # 查找 agv_transport_station 类型 + for nid, ndata in G.nodes(data=True): + node_class = _get_node_class(ndata) + if node_class == "agv_transport_station": + config = _extract_config(ndata) + return _build_agv_cfg(nid, config or {}, G) + + # 兜底:查找带有 device_roles 的 workstation + for nid, ndata in G.nodes(data=True): + node_class = _get_node_class(ndata) + if node_class == "workstation": + config = _extract_config(ndata) + if config and "device_roles" in config: + return _build_agv_cfg(nid, config, G) + + raise ValueError("设备图中未找到 AGV 节点(需 class=agv_transport_station 或 config.device_roles)") + + +def get_agv_capacity(G: nx.Graph, agv_id: str) -> int: + """从 AGV 的 Warehouse 子节点计算载具容量""" + for neighbor in G.successors(agv_id) if G.is_directed() else G.neighbors(agv_id): + ndata = G.nodes[neighbor] + node_type = _get_node_type(ndata) + if node_type == "warehouse": + config = _extract_config(ndata) + if config: + x = config.get("num_items_x", 1) + y = config.get("num_items_y", 1) + z = config.get("num_items_z", 1) + return x * y * z + # 如果没有 warehouse 子节点,尝试从配置中读取 + return 0 + + +def split_batches(items: list, capacity: int) -> List[list]: + """按 AGV 容量分批 + + Args: + items: 待转运的物料列表 + capacity: AGV 单批次容量 + + Returns: + 分批后的列表的列表 + """ + if capacity <= 0: + raise ValueError(f"AGV 容量必须 > 0,当前: {capacity}") + return [items[i:i + capacity] for i in range(0, len(items), capacity)] + + +def _extract_config(node_data: dict) -> Optional[dict]: + """从节点数据中提取 config 字段,兼容多种格式""" + # 直接 config 字段 + config = node_data.get("config") + if isinstance(config, dict): + return config + # res_content 嵌套格式 + res_content = node_data.get("res_content") + if hasattr(res_content, "config"): + return res_content.config if isinstance(res_content.config, dict) else None + if isinstance(res_content, dict): + return res_content.get("config") + return None + + +def _get_node_class(node_data: dict) -> str: + """获取节点的 class 字段""" + res_content = node_data.get("res_content") + if hasattr(res_content, "model_dump"): + d = res_content.model_dump() + return d.get("class_", d.get("class", "")) + if isinstance(res_content, dict): + return res_content.get("class_", res_content.get("class", "")) + return node_data.get("class_", node_data.get("class", "")) + + +def _get_node_type(node_data: dict) -> str: + """获取节点的 type 字段""" + res_content = node_data.get("res_content") + if hasattr(res_content, "type"): + return res_content.type or "" + if isinstance(res_content, dict): + return res_content.get("type", "") + return node_data.get("type", "") + + +def _build_agv_cfg(agv_id: str, config: dict, G: nx.Graph) -> Dict[str, Any]: + """构建标准化的 AGV 配置""" + return { + "agv_id": agv_id, + "device_roles": config.get("device_roles", {}), + "route_table": config.get("route_table", {}), + "capacity": get_agv_capacity(G, agv_id), + } diff --git a/unilabos/compile/agv_transfer_protocol.py b/unilabos/compile/agv_transfer_protocol.py index 18c28b6b..aec1cfb8 100644 --- a/unilabos/compile/agv_transfer_protocol.py +++ b/unilabos/compile/agv_transfer_protocol.py @@ -1,4 +1,12 @@ +""" +AGV 单物料转运编译器 + +从 physical_setup_graph 中查询 AGV 配置(device_roles, route_table), +不再硬编码 device_id 和路由表。 +""" + import networkx as nx +from unilabos.compile._agv_utils import find_agv_config def generate_agv_transfer_protocol( @@ -17,37 +25,32 @@ def generate_agv_transfer_protocol( from_repo_id = from_repo_["id"] to_repo_id = to_repo_["id"] - wf_list = { - ("AiChemEcoHiWo", "zhixing_agv"): {"nav_command" : '{"target" : "LM14"}', - "arm_command": '{"task_name" : "camera/250111_biaozhi.urp"}'}, - ("AiChemEcoHiWo", "AGV"): {"nav_command" : '{"target" : "LM14"}', - "arm_command": '{"task_name" : "camera/250111_biaozhi.urp"}'}, - - ("zhixing_agv", "Revvity"): {"nav_command" : '{"target" : "LM13"}', - "arm_command": '{"task_name" : "camera/250111_put_board.urp"}'}, + # 从 G 中查询 AGV 配置 + agv_cfg = find_agv_config(G) + device_roles = agv_cfg["device_roles"] + route_table = agv_cfg["route_table"] - ("AGV", "Revvity"): {"nav_command" : '{"target" : "LM13"}', - "arm_command": '{"task_name" : "camera/250111_put_board.urp"}'}, + route_key = f"{from_repo_id}->{to_repo_id}" + if route_key not in route_table: + raise KeyError(f"AGV 路由表中未找到路线: {route_key},可用路线: {list(route_table.keys())}") - ("Revvity", "HPLC"): {"nav_command": '{"target" : "LM13"}', - "arm_command": '{"task_name" : "camera/250111_hplc.urp"}'}, + route = route_table[route_key] + nav_device = device_roles.get("navigator", device_roles.get("nav")) + arm_device = device_roles.get("arm") - ("HPLC", "Revvity"): {"nav_command": '{"target" : "LM13"}', - "arm_command": '{"task_name" : "camera/250111_lfp.urp"}'}, - } return [ { - "device_id": "zhixing_agv", + "device_id": nav_device, "action_name": "send_nav_task", "action_kwargs": { - "command": wf_list[(from_repo_id, to_repo_id)]["nav_command"] + "command": route["nav_command"] } }, { - "device_id": "zhixing_ur_arm", + "device_id": arm_device, "action_name": "move_pos_task", "action_kwargs": { - "command": wf_list[(from_repo_id, to_repo_id)]["arm_command"] + "command": route.get("arm_command", route.get("arm_place", "")) } } ] diff --git a/unilabos/compile/batch_transfer_protocol.py b/unilabos/compile/batch_transfer_protocol.py new file mode 100644 index 00000000..35c77b0f --- /dev/null +++ b/unilabos/compile/batch_transfer_protocol.py @@ -0,0 +1,228 @@ +""" +批量物料转运编译器 + +将 BatchTransferProtocol 编译为多批次的 nav → pick × N → nav → place × N 动作序列。 +自动按 AGV 容量分批,全程维护三方 children dict 的物料系统一致性。 +""" + +import copy +from typing import Any, Dict, List + +import networkx as nx + +from unilabos.compile._agv_utils import find_agv_config, split_batches + + +def generate_batch_transfer_protocol( + G: nx.Graph, + from_repo: dict, + to_repo: dict, + transfer_resources: list, + from_positions: list, + to_positions: list, +) -> List[Dict[str, Any]]: + """编译批量转运协议为可执行的 action steps + + Args: + G: 设备图 (physical_setup_graph) + from_repo: 来源工站资源 dict({station_id: {..., children: {...}}}) + to_repo: 目标工站资源 dict(含堆栈和位置信息) + transfer_resources: 被转运的物料列表(Resource dict) + from_positions: 来源 slot 位置列表(与 transfer_resources 平行) + to_positions: 目标 slot 位置列表(与 transfer_resources 平行) + + Returns: + action steps 列表,ROS2WorkstationNode 按序执行 + """ + if not transfer_resources: + return [] + + n = len(transfer_resources) + if len(from_positions) != n or len(to_positions) != n: + raise ValueError( + f"transfer_resources({n}), from_positions({len(from_positions)}), " + f"to_positions({len(to_positions)}) 长度不一致" + ) + + # 组合为内部 transfer_items 便于分批处理 + transfer_items = [] + for i in range(n): + res = transfer_resources[i] if isinstance(transfer_resources[i], dict) else {} + transfer_items.append({ + "resource_id": res.get("id", res.get("name", "")), + "resource_uuid": res.get("sample_id", ""), + "from_position": from_positions[i], + "to_position": to_positions[i], + "resource": res, + }) + + # 查询 AGV 配置 + agv_cfg = find_agv_config(G) + agv_id = agv_cfg["agv_id"] + device_roles = agv_cfg["device_roles"] + route_table = agv_cfg["route_table"] + capacity = agv_cfg["capacity"] + + if capacity <= 0: + raise ValueError(f"AGV {agv_id} 容量为 0,请检查 Warehouse 子节点配置") + + nav_device = device_roles.get("navigator", device_roles.get("nav")) + arm_device = device_roles.get("arm") + if not nav_device or not arm_device: + raise ValueError(f"AGV {agv_id} device_roles 缺少 navigator 或 arm: {device_roles}") + + from_repo_ = list(from_repo.values())[0] + to_repo_ = list(to_repo.values())[0] + from_station_id = from_repo_["id"] + to_station_id = to_repo_["id"] + + # 查找路由 + route_to_source = _find_route(route_table, agv_id, from_station_id) + route_to_target = _find_route(route_table, from_station_id, to_station_id) + + # 构建 AGV carrier 的 children dict(用于 compile 阶段状态追踪) + agv_carrier_children: Dict[str, Any] = {} + + # 计算 slot 名称(A01, A02, B01, ...) + agv_slot_names = _get_agv_slot_names(G, agv_cfg) + + # 分批 + batches = split_batches(transfer_items, capacity) + + steps: List[Dict[str, Any]] = [] + + for batch_idx, batch in enumerate(batches): + is_last_batch = (batch_idx == len(batches) - 1) + + # 阶段 1: AGV 导航到来源工站 + steps.append({ + "device_id": nav_device, + "action_name": "send_nav_task", + "action_kwargs": { + "command": route_to_source.get("nav_command", "") + }, + "_comment": f"批次{batch_idx + 1}/{len(batches)}: AGV 导航至来源 {from_station_id}" + }) + + # 阶段 2: 逐个 pick + for item_idx, item in enumerate(batch): + from_pos = item["from_position"] + slot = agv_slot_names[item_idx] if item_idx < len(agv_slot_names) else f"S{item_idx + 1}" + + # compile 阶段更新 children dict + if from_pos in from_repo_.get("children", {}): + resource_data = from_repo_["children"].pop(from_pos) + resource_data["parent"] = agv_id + agv_carrier_children[slot] = resource_data + + steps.append({ + "device_id": arm_device, + "action_name": "move_pos_task", + "action_kwargs": { + "command": route_to_source.get("arm_pick", route_to_source.get("arm_command", "")) + }, + "_transfer_meta": { + "phase": "pick", + "resource_uuid": item.get("resource_uuid", ""), + "resource_id": item.get("resource_id", ""), + "from_parent": from_station_id, + "from_position": from_pos, + "agv_slot": slot, + }, + "_comment": f"Pick {item.get('resource_id', from_pos)} → AGV.{slot}" + }) + + # 阶段 3: AGV 导航到目标工站 + steps.append({ + "device_id": nav_device, + "action_name": "send_nav_task", + "action_kwargs": { + "command": route_to_target.get("nav_command", "") + }, + "_comment": f"批次{batch_idx + 1}: AGV 导航至目标 {to_station_id}" + }) + + # 阶段 4: 逐个 place + for item_idx, item in enumerate(batch): + to_pos = item["to_position"] + slot = agv_slot_names[item_idx] if item_idx < len(agv_slot_names) else f"S{item_idx + 1}" + + # compile 阶段更新 children dict + if slot in agv_carrier_children: + resource_data = agv_carrier_children.pop(slot) + resource_data["parent"] = to_repo_["id"] + to_repo_["children"][to_pos] = resource_data + + steps.append({ + "device_id": arm_device, + "action_name": "move_pos_task", + "action_kwargs": { + "command": route_to_target.get("arm_place", route_to_target.get("arm_command", "")) + }, + "_transfer_meta": { + "phase": "place", + "resource_uuid": item.get("resource_uuid", ""), + "resource_id": item.get("resource_id", ""), + "to_parent": to_station_id, + "to_position": to_pos, + "agv_slot": slot, + }, + "_comment": f"Place AGV.{slot} → {to_station_id}.{to_pos}" + }) + + # 如果还有下一批,AGV 需要返回来源取料 + if not is_last_batch: + steps.append({ + "device_id": nav_device, + "action_name": "send_nav_task", + "action_kwargs": { + "command": route_to_source.get("nav_command", "") + }, + "_comment": f"AGV 返回来源 {from_station_id} 取下一批" + }) + + return steps + + +def _find_route(route_table: Dict[str, Any], from_id: str, to_id: str) -> Dict[str, str]: + """在路由表中查找路线,支持 A->B 和 (A, B) 两种 key 格式""" + # 优先 "A->B" 格式 + key = f"{from_id}->{to_id}" + if key in route_table: + return route_table[key] + # 兼容 tuple key(JSON 中以逗号分隔字符串表示) + tuple_key = f"({from_id}, {to_id})" + if tuple_key in route_table: + return route_table[tuple_key] + raise KeyError(f"路由表中未找到: {key},可用路线: {list(route_table.keys())}") + + +def _get_agv_slot_names(G: nx.Graph, agv_cfg: dict) -> List[str]: + """从设备图中获取 AGV Warehouse 的 slot 名称列表""" + agv_id = agv_cfg["agv_id"] + neighbors = G.successors(agv_id) if G.is_directed() else G.neighbors(agv_id) + for neighbor in neighbors: + ndata = G.nodes[neighbor] + node_type = ndata.get("type", "") + res_content = ndata.get("res_content") + if hasattr(res_content, "type"): + node_type = res_content.type or node_type + elif isinstance(res_content, dict): + node_type = res_content.get("type", node_type) + if node_type == "warehouse": + config = ndata.get("config", {}) + if hasattr(res_content, "config") and isinstance(res_content.config, dict): + config = res_content.config + elif isinstance(res_content, dict): + config = res_content.get("config", config) + num_x = config.get("num_items_x", 1) + num_y = config.get("num_items_y", 1) + num_z = config.get("num_items_z", 1) + # 与 warehouse_factory 一致的命名 + letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + len_x = num_x if num_z == 1 else (num_y if num_x == 1 else num_x) + len_y = num_y if num_z == 1 else (num_z if num_x == 1 else num_z) + return [f"{letters[j]}{i + 1:02d}" for i in range(len_x) for j in range(len_y)] + # 兜底生成通用名称 + capacity = agv_cfg.get("capacity", 4) + return [f"S{i + 1}" for i in range(capacity)] diff --git a/unilabos/devices/eit_agv/config/agv_config.py b/unilabos/devices/eit_agv/config/agv_config.py new file mode 100644 index 00000000..3f7a7322 --- /dev/null +++ b/unilabos/devices/eit_agv/config/agv_config.py @@ -0,0 +1,95 @@ +# config.py +# -*- coding: utf-8 -*- +""" +AGV驱动配置文件 +""" + +# AGV控制器连接配置 +AGV_HOST = "127.0.0.1" +AGV_PORT = 6103 # 查询命令端口 +AGV_PORT_NAVIGATION = 6108 # 路径导航命令端口 +AGV_TIMEOUT = 3.0 + +# 协议常量 +FRAME_HEAD_SIZE = 16 + +# 任务状态查询命令 (厂家示例代码中的1110 = 0x0456) +REQ_CMD_ROBOT_STATUS_TASK = 0x03FC # 任务状态查询请求 +RSP_CMD_ROBOT_STATUS_TASK = 0x2B0C # 任务状态查询响应 +REQ_CMD_TASK_QUERY = 0x0456 # 厂家示例: 任务查询命令 (1110) + +# 路径导航命令 +REQ_CMD_ROBOT_TASK_GOTARGET = 0x0BEB # 路径导航命令 +RSP_CMD_ROBOT_TASK_GOTARGET = 0x32FB # 路径导航响应 + +# 机器人位置查询命令 +REQ_CMD_ROBOT_STATUS_LOC = 0x03EC # 机器人位置查询请求 (1004) +RSP_CMD_ROBOT_STATUS_LOC = 0x2AFC # 机器人位置查询响应 (11004) + +# 机器人电池状态查询命令 +REQ_CMD_ROBOT_STATUS_BATTERY = 0x03EF # 电池状态查询请求 (1007) +RSP_CMD_ROBOT_STATUS_BATTERY = 0x2AFF # 电池状态查询响应 (11007) + +# 任务状态映射 +TASK_STATUS_MAP = { + 0: "NONE", + 1: "WAITING", + 2: "RUNNING", + 3: "SUSPENDED", + 4: "COMPLETED", + 5: "FAILED", + 6: "CANCELED", +} + +# 任务类型映射 +TASK_TYPE_MAP = { + 0: "NO_NAV", + 1: "FREE_NAV_TO_POINT", + 2: "FREE_NAV_TO_SITE", + 3: "PATH_NAV_TO_SITE", + 7: "TRANSLATE_ROTATE", + 100: "OTHER", +} + +# AGV运动参数配置 +AGV_MAX_SPEED = 0.3 # 最大速度, 单位m/s +AGV_MAX_WSPEED = 0.8 # 最大角速度, 单位rad/s +AGV_MAX_ACC = 0.2 # 最大加速度, 单位m/s^2 +AGV_MAX_WACC = 0.5 # 最大角加速度, 单位rad/s^2 + +# AGV查询重试配置 +AGV_QUERY_MAX_RETRIES = 3 # 查询最大重试次数 +AGV_QUERY_RETRY_DELAY = 1.0 # 首次重试等待时间, 单位秒, 后续翻倍(指数退避) + +# 工站位置配置 +STATION_POSITIONS = { + "LM1": { + "name": "synthesis_station", + "description": "合成工站位置" + }, + "LM2": { + "name": "analysis_station", + "description": "分析工站位置" + }, + "CP6": { + "name": "charging_station", + "description": "充电站位置" + }, + "LM3": { + "name": "concentration_station", + "description": "浓缩工站位置" + }, + "LM4": { + "name": "shelf", + "description": "货架位置" + }, + "LM0": { + "name": "maintenance_point", + "description": "检修点位置" + }, + "PP5": { + "name": "charging_transition_point", + "description": "充电过渡点位置" + } +} + diff --git a/unilabos/devices/eit_agv/config/arm_config.py b/unilabos/devices/eit_agv/config/arm_config.py new file mode 100644 index 00000000..3b2dc512 --- /dev/null +++ b/unilabos/devices/eit_agv/config/arm_config.py @@ -0,0 +1,14 @@ +# arm_config.py +# -*- coding: utf-8 -*- +""" +机械臂驱动配置文件 +""" + +# 机械臂控制器连接配置 +ARM_HOST = "127.0.0.1" +ARM_PORT = 6101 +ARM_TIMEOUT = 3000 # 单位: 毫秒 + +# 夹爪控制配置 +# 是否启用夹持到检测功能: True表示夹爪闭合后检测是否夹到物料, False表示不检测 +ENABLE_GRIP_DETECTION = True diff --git a/unilabos/devices/eit_agv/config/arm_positions.yaml b/unilabos/devices/eit_agv/config/arm_positions.yaml new file mode 100644 index 00000000..36ad333f --- /dev/null +++ b/unilabos/devices/eit_agv/config/arm_positions.yaml @@ -0,0 +1,528 @@ +# 机械臂工位坐标配置文件 +# 位姿格式: [x, y, z, rx, ry, rz], 位置单位mm, 姿态单位rad +# 所使用的所有距离单位均为mm,角度均为rad + +# 托盘抓取/放置位置 +# pose存储的是实际抓取点位姿, 运动时先到过渡点(z + |descend_z|), 再下探到抓取点 +tray_position: + # 托盘1抓取/放置位置 + agv_tray_1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -681.1357736587524 + - 438.84578347206116 + - 25.583838364481927 + - 3.1369340419769287 + - -0.005401393864303827 + - -0.05389278009533882 + descend_z: -144 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 50 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "AGV托盘1抓取/放置位置" + + agv_tray_2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -571.0859298706055 + - 436.1766576766968 + - 25.874629616737366 + - 3.1369516849517822 + - -0.005311599466949701 + - -0.05396140739321709 + descend_z: -144 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "AGV托盘2抓取/放置位置" + + agv_tray_3: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -460.97591519355774 + - 433.66706371307373 + - 25.876877829432487 + - 3.136897563934326 + - -0.005446072667837143 + - -0.05386633053421974 + descend_z: -144 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "AGV托盘3抓取/放置位置" + + agv_tray_4: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -350.4909873008728 + - 430.68331480026245 + - 26.014909148216248 + - 3.136922597885132 + - -0.00550597021356225 + - -0.053923387080430984 + descend_z: -144 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "AGV托盘4抓取/放置位置" + + shelf_tray_3-1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -568.3339816703797 + - 564.378377640686 + - 445.62413675239566 + - 3.12782565977478 + - -0.0028824652460105716 + - 3.0891643018734456 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm2 + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托3-1抓取/放置位置" + + shelf_tray_3-2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -458.451895154953 + - 559.5827667359924 + - 444.2204175656128 + - 3.1188027086486816 + - -0.004347024942625314 + - 3.088613078118563 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托3-2抓取/放置位置" + + shelf_tray_3-3: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -349.18305102729795 + - 556.3021270875549 + - 444.12472230842593 + - 3.118878764175415 + - -0.0042788800698407 + - 3.0876830072414876 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托3-3抓取/放置位置" + + shelf_tray_3-4: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -240.0813681259155 + - 552.4144141320801 + - 443.7779126828003 + - 3.119202536605835 + - -0.004063687278766185 + - 3.0866635293972493 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托3-4抓取/放置位置" + + shelf_tray_2-1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -566.7535049095154 + - 566.0402743463135 + - 278.4518836205292 + - 3.1286896887054443 + - -0.0048318202522285286 + - 3.093345925332308 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托2-1抓取/放置位置" + + shelf_tray_2-2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -455.5958193912506 + - 562.6298753862 + - 276.8153188889313 + - 3.1291526975860595 + - -0.009852693873334676 + - 3.0932183713924886 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托2-2抓取/放置位置" + + shelf_tray_2-3: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -347.41863433265684 + - 559.0115158204651 + - 277.7952192490387 + - 3.1290513696899414 + - -0.008729059211554006 + - 3.089800659687519 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托2-3抓取/放置位置" + + shelf_tray_2-4: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -239.07461587333677 + - 555.5853216294861 + - 276.2364683811951 + - 3.1288141432037353 + - -0.003286123703809455 + - 3.090417925388813 + descend_z: -32 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托2-4抓取/放置位置" + + shelf_tray_1-1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -568.9506513252259 + - 567.2160951737976 + - 97.64885289123538 + - 3.127785128616333 + - -0.0037946942817587404 + - 3.086452309162617 + descend_z: -32 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托1-1抓取/放置位置" + + shelf_tray_1-2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -457.55141798400876 + - 564.031538212738 + - 97.50442338636401 + - 3.1275891485443115 + - -0.0018292341761924325 + - 3.094240233422518 + descend_z: -32 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托1-2抓取/放置位置" + + shelf_tray_1-3: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -349.8526496067047 + - 560.6108872537231 + - 96.91079092672351 + - 3.127733630203247 + - -0.0038403524000830947 + - 3.084831759454012 + descend_z: -32 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托1-3抓取/放置位置" + + shelf_tray_1-4: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -238.5269087924957 + - 557.8902928475952 + - 96.97931391647342 + - 3.1340007009735107 + - -0.004337313576478511 + - 3.0917535276424886 + descend_z: -32 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 20 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "货架托1-4抓取/放置位置" + + analysis_station_tray_1-1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -86.2326551910019 + - 762.1331760691834 + - 652.533215532074 + - 3.120131956756592 + - 0.007391351018667221 + - -1.6309793014612197 + descend_z: -50 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 50 #提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.3 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "分析站托盘1-1抓取/放置位置" + + analysis_station_tray_1-2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -234.93284776168824 + - 765.6448433207703 + - 651.0974025819397 + - 3.1254463068847658 + - -0.01263781499619782 + - 1.50891514675951 + descend_z: -50 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 50 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.3 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "分析站托盘1-2抓取/放置位置" + + synthesis_station_tray_2-1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -725.4724611866379 + - 614.9058935688781 + - 334.0895707960511 + - 3.1165872745513914 + - 0.00011250698562897737 + - 3.095631282728672 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘2-1抓取/放置位置" + + synthesis_station_tray_2-2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -616.1432375538254 + - 613.3998034046936 + - 333.89540866569524 + - 3.1165589027404783 + - 0.00013958355967141683 + - 3.095615070265293 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘2-2抓取/放置位置" + + synthesis_station_tray_2-3: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -506.47855898021703 + - 612.4382016705322 + - 334.4801302309418 + - 3.1165875129699705 + - 0.00015194597963735443 + - 3.0956272296128273 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘2-3抓取/放置位置" + + synthesis_station_tray_2-4: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -397.48922338365554 + - 609.9788544224548 + - 334.8073895330811 + - 3.118162029266357 + - 0.00022480222369730468 + - 3.0968090928611756 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘2-4抓取/放置位置" + + synthesis_station_tray_1-1: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -724.1093104112053 + - 617.0890328930664 + - 164.33291987136843 + - 3.116654270172119 + - -0.008952309796854853 + - 3.096324037712574 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘1-1抓取/放置位置" + + synthesis_station_tray_1-2: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -615.8975993621254 + - 616.3320539044189 + - 164.15307775691988 + - 3.116654270172119 + - 5.067549719123045e-05 + - 3.0963226221022606 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘1-2抓取/放置位置" + + synthesis_station_tray_1-3: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -506.4669863789368 + - 614.3691042016601 + - 165.07894357448592 + - 3.116629411712646 + - -0.00034651699899914193 + - 3.096271587358952 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘1-3抓取/放置位置" + + synthesis_station_tray_1-4: + pose: # 抓取点位姿, 位置单位mm, 姿态单位rad + - -396.522152923336 + - 612.3422381924438 + - 164.81291607574465 + - 3.1166750125885008 + - 0.00027354782187566156 + - 3.096347641151905 + descend_z: -15 # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm + lift_z: 10 # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 加速度百分比 (0-1) + description: "合成站托盘1-4抓取/放置位置" + +# 夹爪类型定义 (2种夹爪, 适配不同托盘) +grippers: + gripper_type_a: + slot: 1 # 快换料位编号(bg1, 对应DI3) + description: "A型托盘夹爪" + gripper_type_b: + slot: 2 # 快换料位编号(bg2, 对应DI4) + description: "B型托盘夹爪" + +# 夹爪存放位置 (用于自动换爪, 需要实际标定坐标) +gripper_storage: + gripper_type_a: + pose: [-660.0802540779114, -234.6295565366745, 231.00166022777557, 3.1409313678741455, 0.002824182389304042, 3.087721109390259] # TODO: 需要实际标定 + descend_z: -50 # 对接下探距离, 单位mm + lift_z: 30 # 脱离提升距离, 单位mm + speed: 0.6 + acceleration: 0.3 + description: "A型夹爪存放位置" + gripper_type_b: + pose: [-500.0, 400.0, 200.0, -3.14159, 0.0, 0.0] # TODO: 需要实际标定 + descend_z: -50 + lift_z: 30 + speed: 0.6 + acceleration: 0.3 + description: "B型夹爪存放位置" + +# 物料类型配置 +materials: + + REACTION_TUBE_TRAY_2ML: + gripper: gripper_type_a + descend_z_offset: 0 # 下探距离偏移(mm), 正值表示少下探, 负值表示多下探 + lift_z_offset: 0 # 提升距离偏移(mm), 正值表示多提升, 负值表示少提升 + description: "2 mL 反应试管托盘" + + TEST_TUBE_MAGNET_TRAY_2ML: + gripper: gripper_type_a + descend_z_offset: 20 + lift_z_offset: 0 + description: "2 mL 试管磁子托盘" + + REACTION_SEAL_CAP_TRAY: + gripper: gripper_type_a + descend_z_offset: 0 + lift_z_offset: 0 + description: "反应密封盖托盘" + + FLASH_FILTER_INNER_BOTTLE_TRAY: + gripper: gripper_type_a + descend_z_offset: 5 + lift_z_offset: 0 + description: "闪滤瓶内瓶托盘" + + FLASH_FILTER_OUTER_BOTTLE_TRAY: + gripper: gripper_type_a + descend_z_offset: 9 + lift_z_offset: 0 + description: "闪滤瓶外瓶托盘" + + TIP_TRAY_50UL: + gripper: gripper_type_a + descend_z_offset: 0 + lift_z_offset: 0 + description: "50 μL Tip 头托盘" + + TIP_TRAY_1ML: + gripper: gripper_type_a + descend_z_offset: 0 + lift_z_offset: 0 + description: "1 mL Tip 头托盘" + + TIP_TRAY_5ML: + gripper: gripper_type_a + descend_z_offset: 0 + lift_z_offset: 0 + description: "5 mL Tip 头托盘" + + POWDER_BUCKET_TRAY_30ML: + gripper: gripper_type_a + descend_z_offset: 2 + lift_z_offset: 0 + description: "30 mL 粉桶托盘" + + REAGENT_BOTTLE_TRAY_2ML: + gripper: gripper_type_a + descend_z_offset: 5 + lift_z_offset: 0 + description: "2 mL 试剂瓶托盘" + + REAGENT_BOTTLE_TRAY_8ML: + gripper: gripper_type_a + descend_z_offset: 5 + lift_z_offset: 0 + description: "8 mL 试剂瓶托盘" + + REAGENT_BOTTLE_TRAY_40ML: + gripper: gripper_type_a + descend_z_offset: 5 + lift_z_offset: 0 + description: "40 mL 试剂瓶托盘" + + REAGENT_BOTTLE_TRAY_125ML: + gripper: gripper_type_a + descend_z_offset: 0 + lift_z_offset: 0 + description: "125 mL 试剂瓶托盘" + +# 安全位置 +safe_positions: + + # 机械臂回零位置(关节角度) + home_1: + pose: [-566.0403299999999, 191.43068, 376.44481, -3.14153742, -1.43e-06, -0.05532694] # 关节角度, 单位rad + joints: [-0.048593830317258835, 0.06705363094806671, -2.1632132530212402, 0.5269968509674072, 1.56870436668396, 1.5804846286773682] + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 关节角加速度, 单位rad/s² + description: "机械臂回零位置" + + # 机械臂回零位置(关节角度) + home_2: + pose: [-566.0403299999999, 191.43068, 376.44481, -3.14153742, 0.0, 3.000] # 关节角度, 单位rad + joints: [-0.04862978309392929, 0.06706561148166656, -2.163177251815796, 0.5269728899002075, 1.5688121318817139, -1.4748575687408447] + speed: 0.6 # 运动速度百分比 (0-1) + acceleration: 0.3 # 关节角加速度, 单位rad/s² + description: "机械臂回零位置" + +# 全局运动参数 +global_params: + default_speed: 1.0 # 默认速度百分比 + default_acceleration: 0.3 # 默认加速度百分比 + collision_level: 3 # 碰撞检测等级(0-5) +station_calibration: + shelf: + x: 0.060735382080068234 + y: 18.408122813262985 + z: -0.44160468032838907 + dx: -0.0011390867462157672 + dy: -0.003025016464881599 + dz: -0.00023092341542244044 + description: shelf工站校准偏移值 + synthesis_station: + x: 12.737158440341956 + y: -17.657398943786596 + z: 1.6540352944945935 + dx: 0.0 + dy: -0.0019855169111043214 + dz: -0.0035206339774131734 + description: synthesis_station工站校准偏移值 + analysis_station: + x: -18.418885326805107 + y: -18.966133336944637 + z: -1.5557394120788715 + dx: 0.0 + dy: -0.0024044271322041753 + dz: 0.007813615777730945 + description: analysis_station工站校准偏移值 diff --git a/unilabos/devices/eit_agv/controller/agv_controller.py b/unilabos/devices/eit_agv/controller/agv_controller.py new file mode 100644 index 00000000..4ec00b6f --- /dev/null +++ b/unilabos/devices/eit_agv/controller/agv_controller.py @@ -0,0 +1,5618 @@ +# coding:utf-8 +""" +功能: + AGV控制器模块, 封装AGV机械臂的高级控制功能 + 提供基于配置文件的坐标管理和运动控制 +""" + +import json +import logging +import time +import threading +from unilabos.ros.nodes.base_device_node import ROS2DeviceNode +from unilabos.utils.import_manager import get_class +from ..driver.arm_driver import ArmDriver +from ..driver.agv_driver import AGVDriver, AGVDriverConfig +from ..utils.position_manager import PositionManager +from ..config.agv_config import ( + STATION_POSITIONS, + AGV_HOST, + AGV_PORT, + AGV_PORT_NAVIGATION, + AGV_TIMEOUT, + TASK_STATUS_MAP, + AGV_QUERY_MAX_RETRIES, + AGV_QUERY_RETRY_DELAY, +) +from ..config.arm_config import ENABLE_GRIP_DETECTION +from ..data.shelf_manager import ShelfManager + +logger = logging.getLogger(__name__) + + +class AGVController: + """ + 功能: + AGV控制器类, 提供机械臂的高级控制接口 + 所有动作方法会自动检查并连接机械臂 + """ + + def __init__(self, ip=None, port=None, timeout=None): + """ + 功能: + 初始化AGV控制器 + 参数: + ip: 机械臂IP地址, 默认使用配置文件中的值 + port: 机械臂端口, 默认使用配置文件中的值 + timeout: socket超时时间, 单位毫秒, 默认使用配置文件中的值 + """ + self.arm = ArmDriver(ip, port, timeout) + self.position_manager = PositionManager() + self.current_station = None # 当前所在工站 + self.shelf_manager = ShelfManager() # 货架物料状态管理器 + + logger.info("AGV控制器初始化完成") + + def _ensure_connected(self): + """ + 功能: + 确保机械臂已连接, 如果未连接则自动连接 + 返回: + bool, True表示已连接, False表示连接失败 + """ + if self.arm.is_connected: + return True + + logger.info("机械臂未连接, 正在自动连接...") + return self.arm.connect() + + def connect(self): + """ + 功能: + 连接机械臂 + 返回: + bool, True表示连接成功, False表示连接失败 + """ + return self.arm.connect() + + def disconnect(self): + """ + 功能: + 断开机械臂连接 + 返回: + bool, True表示断开成功, False表示断开失败 + """ + return self.arm.disconnect() + + def arm_go_home(self, block=True, home_name=None): + """ + 功能: + 控制机械臂回零位置, 根据当前姿态选择最接近的home坐标或使用指定的home位置 + 根据当前Y坐标判断返回路径: Y > 450先回Y轴, Y <= 450先回Z轴 + 参数: + block: 是否阻塞执行, True表示等待运动完成, False表示立即返回 + home_name: 指定的home位置名称, 如"home_1", 如果为None则自动选择最接近的home + 返回: + 阻塞执行时返回任务结束状态, 非阻塞执行时返回任务ID + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行回零动作") + return None + + # 获取当前位姿和关节角度 + current_pose = self.arm.get_tcp_pose() + current_y = current_pose[1] + current_joints = self.arm.get_joints_position() + current_joint6 = current_joints[5] # 第6个关节(索引为5) + + logger.debug(f"当前位姿: {current_pose}") + logger.debug(f"当前Y坐标: {current_y}mm, 当前关节6: {current_joint6:.4f}") + + # 获取所有safe_positions中的home坐标 + if 'safe_positions' not in self.position_manager.positions: + logger.error("未找到安全位置配置(safe_positions)") + return None + + safe_positions = self.position_manager.positions['safe_positions'] + if len(safe_positions) == 0: + logger.error("安全位置配置为空") + return None + + # 如果指定了home_name, 直接使用指定的home位置 + if home_name is not None: + if home_name not in safe_positions: + logger.error(f"未找到指定的home位置: {home_name}") + return None + selected_home_name = home_name + selected_home_position = safe_positions[home_name] + logger.debug(f"使用指定的home位置: {selected_home_name}") + else: + # 筛选出所有home开头的位置 + home_positions = {name: pos for name, pos in safe_positions.items() if name.startswith('home')} + + if len(home_positions) == 0: + logger.error("未找到任何home位置配置") + return None + + # 计算与当前关节6最接近的home坐标 + min_distance = float('inf') + selected_home_name = None + selected_home_position = None + + for home_name_iter, home_pos in home_positions.items(): + if not home_pos.has_joints(): + logger.warning(f"{home_name_iter}缺少关节数据, 跳过") + continue + + # 计算关节6的差异(绝对值) + home_joint6 = home_pos.joints[5] + distance = abs(current_joint6 - home_joint6) + + logger.debug(f"{home_name_iter}: joint6={home_joint6:.4f}, 关节6差异={distance:.4f}") + + if distance < min_distance: + min_distance = distance + selected_home_name = home_name_iter + selected_home_position = home_pos + + if selected_home_position is None: + logger.error("未找到合适的home位置") + return None + + logger.debug(f"选择最接近的home位置: {selected_home_name}, 关节6差异={min_distance:.4f}") + + safe_position = selected_home_position + speed = safe_position.speed + acceleration = safe_position.acceleration + + try: + # 根据当前Y坐标判断返回路径 + if current_y > 450: + # Y > 450, 机械臂伸出车体, 先将Y轴向安全姿态运动250mm + logger.debug("Y > 450, 机械臂伸出车体, 先将Y轴向安全姿态运动250mm") + + # 步骤1: Y轴向安全姿态方向移动250mm + pose_step1 = [ + current_pose[0], + current_pose[1] - 250, # Y轴向安全姿态方向移动250mm + current_pose[2], + current_pose[3], + current_pose[4], + current_pose[5] + ] + result = self.arm.move_linear( + pose=pose_step1, + v=speed * 0.5, + a=acceleration, + block=block + ) + logger.debug(f"Y轴向安全姿态运动250mm完成: {result}") + else: + # Y <= 450, 车内坐标, 先回Z到home点 + logger.debug("Y <= 450, 车内坐标, 先回Z到home点") + + # 步骤1: 先运动Z到home的Z值 + pose_step1 = [ + current_pose[0], + current_pose[1], + safe_position.pose[2], # 使用home的Z值 + current_pose[3], + current_pose[4], + current_pose[5] + ] + result = self.arm.move_linear( + pose=pose_step1, + v=speed, + a=acceleration, + block=block + ) + logger.debug(f"Z轴运动到home完成: {result}") + + # 步骤2: 调整姿态到安全姿态 (使用关节运动避免多圈问题) + current_pose = self.arm.get_tcp_pose() + target_pose_for_orientation = [ + current_pose[0] / 1000.0, # 转换为m + current_pose[1] / 1000.0, + current_pose[2] / 1000.0, + safe_position.pose[3], + safe_position.pose[4], + safe_position.pose[5] + ] + # 使用逆运动学计算目标关节角度 + current_joints = self.arm.get_joints_position() + target_joints = self.arm.calculate_inverse_kinematics( + pose=target_pose_for_orientation, + q_near=current_joints # 使用当前关节角度作为参考, 确保选解合理 + ) + # 使用关节运动 (move_to_joints会自动调整j6角度到合理范围) + result = self.arm.move_to_joints( + joints_list=target_joints, + v=speed, + a=acceleration, + block=block + ) + logger.debug(f"姿态调整完成(关节运动): {result}") + + # 步骤3: 运动到home位置(只使用关节运动) + logger.debug(f"运动到home位置: {selected_home_name}") + result = self.arm.move_to_joints( + joints_list=safe_position.joints, + v=speed, + a=acceleration, + block=block + ) + + if block: + logger.debug(f"机械臂回零完成, 结果: {result}") + else: + logger.debug(f"机械臂回零指令已发送, 任务ID: {result}") + + return result + + except Exception as e: + logger.error(f"机械臂回零失败: {e}") + return None + + def pick_tray(self, tray_name, descend_z=None, lift_z=None, transition_z_offset=0, block=True): + """ + 功能: + 通用取托盘逻辑, 按照标准流程抓取托盘 + 参数: + tray_name: 托盘位置名称, 例如"agv_tray_1", 从配置文件tray_position中读取 + descend_z: 下探距离(mm), 为None时从配置文件读取 + lift_z: 提升距离(mm), 为None时从配置文件读取 + transition_z_offset: 过渡点Z值偏移量(mm), 默认为0 + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + bool, True表示抓取成功, False表示抓取失败 + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行取托盘动作") + return False + + # 获取托盘位置配置 + tray_position = self.position_manager.get_position('tray_position', tray_name) + if tray_position is None: + logger.error(f"未找到托盘位置配置: tray_position.{tray_name}") + return False + + # 判断是否需要应用站点偏移量 + station_offset = None + matched_station = None + if not tray_name.startswith('agv'): + # 读取配置文件中的所有站点校准信息 + try: + import yaml + with open(self.position_manager.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is not None and 'station_calibration' in config: + # 遍历所有站点名称, 检查tray_name是否以某个站点名称开头 + for station_name in config['station_calibration'].keys(): + if tray_name.startswith(station_name): + matched_station = station_name + logger.debug(f"托盘 {tray_name} 匹配到站点 {station_name}, 尝试获取校准偏移量") + + # 获取该站点的校准偏移量 + station_offset = self.position_manager.get_calibration_offset(station_name) + if station_offset is not None: + logger.debug(f"找到站点 {station_name} 的校准偏移量: x={station_offset['x']:.6f}, y={station_offset['y']:.6f}, z={station_offset['z']:.6f}, dx={station_offset['dx']:.6f}, dy={station_offset['dy']:.6f}, dz={station_offset['dz']:.6f}") + break + + if matched_station is None: + logger.debug(f"托盘 {tray_name} 未匹配到任何站点校准配置, 使用原始坐标") + except Exception as e: + logger.warning(f"读取站点校准配置失败: {e}, 使用原始坐标") + + logger.info(f"开始执行取托盘流程: {tray_name}") + + try: + # 步骤1: 运动到安全姿态 + logger.debug("步骤1: 运动到安全姿态") + result = self.arm_go_home(block=block) + logger.debug(f"安全姿态运动完成: {result}") + + # 步骤2: 张开夹爪 + logger.debug("步骤2: 张开夹爪") + self.arm.open_gripper(block=True) + logger.debug("夹爪已张开") + + # 步骤3: 运动到过渡点 (先rx,ry,rz, 然后z, 最后xy且速度减半) + logger.debug("步骤3: 运动到过渡点") + # 获取抓取点位姿(配置文件中存储的是抓取点) + grasp_pose = tray_position.pose.copy() + # 获取下探距离, 用于计算过渡点 + config_descend_z = tray_position.descend_z if hasattr(tray_position, 'descend_z') else -32 + # 计算过渡点: 过渡点z = 抓取点z - descend_z (descend_z为负值, 所以减去它等于加上绝对值) + transition_pose = grasp_pose.copy() + transition_pose[2] = grasp_pose[2] - config_descend_z + logger.debug(f"抓取点z={grasp_pose[2]:.2f}, descend_z={config_descend_z}, 过渡点z={transition_pose[2]:.2f}") + + # 如果有站点偏移量, 则应用到过渡点位姿 + if station_offset is not None: + transition_pose[0] += station_offset['x'] + transition_pose[1] += station_offset['y'] + transition_pose[2] += station_offset['z'] + transition_pose[3] += station_offset['dx'] + transition_pose[4] += station_offset['dy'] + transition_pose[5] += station_offset['dz'] + logger.debug(f"应用站点偏移量后的过渡点位姿: {transition_pose}") + + # 应用过渡点Z偏移量(用于物料高度补偿) + if transition_z_offset != 0: + transition_pose[2] += transition_z_offset + logger.debug(f"应用过渡点Z偏移量{transition_z_offset}mm后的过渡点位姿: {transition_pose}") + + # 3.1: 先运动rx,ry,rz到位 (使用关节运动避免多圈问题) + current_pose = self.arm.get_tcp_pose() + target_pose_for_orientation = [ + current_pose[0] / 1000.0, # 转换为m + current_pose[1] / 1000.0, + current_pose[2] / 1000.0, + transition_pose[3], + transition_pose[4], + transition_pose[5] + ] + # 使用逆运动学计算目标关节角度 + current_joints = self.arm.get_joints_position() + target_joints = self.arm.calculate_inverse_kinematics( + pose=target_pose_for_orientation, + q_near=current_joints # 使用当前关节角度作为参考, 确保选解合理 + ) + # 使用关节运动 (move_to_joints会自动调整j6角度到合理范围) + result = self.arm.move_to_joints( + joints_list=target_joints, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"姿态调整完成(关节运动): {result}") + + # 3.2: 根据目标点Y坐标判断运动策略 + if transition_pose[1] < 450: + # Y < 450, 在车内, xyz直接同时运动到位 + logger.debug("目标点在车内(Y<450), xyz直接运动到位") + result = self.arm.move_linear( + pose=transition_pose, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"过渡点运动完成: {result}") + else: + # Y >= 450, 在车外, 先运动到y=400, 然后y前伸 + logger.debug("目标点在车外(Y>=450), 先运动到y=400") + current_pose = self.arm.get_tcp_pose() + intermediate_pose = [ + transition_pose[0], + 400, + transition_pose[2], + current_pose[3], + current_pose[4], + current_pose[5] + ] + result = self.arm.move_linear( + pose=intermediate_pose, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"运动到y=450完成: {result}") + + # 然后y前伸到目标点 + logger.debug("y前伸到目标点") + result = self.arm.move_linear( + pose=transition_pose, + v=tray_position.speed * 0.2, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"过渡点运动完成: {result}") + + # 步骤4: 下探到抓取点位置 (只有Z轴变化) + logger.debug("步骤4: 下探到抓取点位置") + # 计算抓取点位姿(应用站点偏移量) + target_grasp_pose = grasp_pose.copy() + if station_offset is not None: + target_grasp_pose[0] += station_offset['x'] + target_grasp_pose[1] += station_offset['y'] + target_grasp_pose[2] += station_offset['z'] + target_grasp_pose[3] += station_offset['dx'] + target_grasp_pose[4] += station_offset['dy'] + target_grasp_pose[5] += station_offset['dz'] + # 如果传入了自定义descend_z, 则使用自定义值计算目标z + if descend_z is not None: + current_pose = self.arm.get_tcp_pose() + target_grasp_pose[2] = current_pose[2] + descend_z + # 应用物料高度偏移到抓取点(与过渡点保持一致的偏移) + if transition_z_offset != 0: + target_grasp_pose[2] += transition_z_offset + logger.debug(f"应用物料高度偏移{transition_z_offset}mm到抓取点") + logger.debug(f"目标抓取点位姿: {target_grasp_pose}") + result = self.arm.move_linear( + pose=target_grasp_pose, + v=tray_position.speed * 0.2, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"下探完成: {result}") + + # 步骤5: 夹紧夹爪并检查是否夹紧 + logger.debug("步骤5: 夹紧夹爪") + self.arm.close_gripper(block=True) + + # 根据配置决定是否进行夹持检测 + if ENABLE_GRIP_DETECTION: + # 等待夹爪稳定并检查状态 + time.sleep(1) + + if not self.arm.is_gripper_gripped(): + logger.error("夹爪未夹紧, 可能未夹到托盘") + # 张开夹爪并返回失败 + self.arm.open_gripper(block=True) + return False + logger.debug("夹爪夹紧成功") + else: + logger.debug("夹持检测已禁用, 跳过检测") + + # 步骤6: 提升一定距离 (只有Z轴变化) + logger.debug("步骤6: 提升托盘") + # 如果未传入lift_z参数, 则从配置文件读取 + if lift_z is None: + lift_z = tray_position.lift_z if hasattr(tray_position, 'lift_z') else 0.1 + current_pose = self.arm.get_tcp_pose() + logger.debug(f"当前位姿: {current_pose}, 提升距离: {lift_z}mm") + lift_pose = [ + current_pose[0], + current_pose[1], + current_pose[2] + lift_z, + current_pose[3], + current_pose[4], + current_pose[5] + ] + logger.debug(f"目标位姿: {lift_pose}") + result = self.arm.move_linear( + pose=lift_pose, + v=tray_position.speed * 0.25, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"提升完成: {result}") + + # 步骤7: 回到home位置 + logger.debug("步骤7: 回到home位置") + result = self.arm_go_home(block=block) + logger.debug(f"回到home位置完成: {result}") + + logger.info(f"取托盘流程完成: {tray_name}") + return True + + except Exception as e: + logger.error(f"取托盘流程失败: {e}") + # 发生异常时张开夹爪 + try: + self.arm.open_gripper(block=True) + except Exception: + pass + return False + + def calibrate_station(self, block=True): + """ + 功能: + 工站点位校准, 自动查询当前站点并运行内置校准程序, 保存偏移值到配置文件 + 参数: + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + dict或None, 成功时返回校准偏移值字典{"x": float, "y": float, "z": float, "dx": float, "dy": float, "dz": float}, 失败时返回None + """ + # 查询当前站点 + logger.debug("正在查询当前站点...") + station_info = self.query_current_station() + if station_info is None: + logger.error("查询当前站点失败, 无法执行点位校准") + return None + + station_name = station_info["station_name"] + logger.debug(f"当前站点: {station_info['station_id']} - {station_name} ({station_info['description']})") + + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行点位校准") + return None + + # 工站名称与校准程序的映射 + calibration_programs = { + "shelf": "shelf_calibration.jspf", + "synthesis_station": "synthesis_station_calibration.jspf", + "analysis_station": "analysis_station_calibration.jspf" + } + + # 检查工站名称是否有效 + if station_name not in calibration_programs: + logger.error(f"无效的工站名称: {station_name}, 支持的工站: {list(calibration_programs.keys())}") + return None + + program_name = calibration_programs[station_name] + logger.debug(f"开始执行工站点位校准: {station_name}, 程序: {program_name}") + + try: + # 运行校准程序 + result = self.arm.run_program(program_name, block=block) + logger.debug(f"校准程序运行结果: {result}") + + # 等待机械臂运动完成 + logger.debug("等待机械臂运动完成...") + import time + max_wait_time = 120 # 最大等待时间120秒 + check_interval = 0.5 # 每0.5秒检查一次 + elapsed_time = 0 + + while elapsed_time < max_wait_time: + if not self.arm.is_moving(): + # 机械臂停止运动后, 再等待2秒确保变量已更新 + time.sleep(2) + break + time.sleep(check_interval) + elapsed_time += check_interval + + if elapsed_time >= max_wait_time: + logger.warning(f"等待超时({max_wait_time}秒), 机械臂可能仍在运动") + else: + logger.debug(f"机械臂运动完成, 耗时: {elapsed_time:.1f}秒") + + # 获取校准偏移值(从系统变量读取, 单位为m) + logger.debug("正在读取校准偏移值...") + g_pose_x = self.arm.get_system_value_double("g_pose_x") + g_pose_y = self.arm.get_system_value_double("g_pose_y") + g_pose_z = self.arm.get_system_value_double("g_pose_z") + g_pose_dx = self.arm.get_system_value_double("g_pose_dx") + g_pose_dy = self.arm.get_system_value_double("g_pose_dy") + g_pose_dz = self.arm.get_system_value_double("g_pose_dz") + + # 将位置偏移从m转换为mm, 姿态偏移保持rad不变 + calibration_offset = { + "x": g_pose_x * 1000, # m转换为mm + "y": g_pose_y * 1000, # m转换为mm + "z": g_pose_z * 1000, # m转换为mm + "dx": g_pose_dx, # rad保持不变 + "dy": g_pose_dy, # rad保持不变 + "dz": g_pose_dz # rad保持不变 + } + + logger.debug(f"校准偏移值(mm坐标系): {calibration_offset}") + + # 保存偏移值到配置文件 + self.position_manager.save_calibration_offset(station_name, calibration_offset) + logger.debug(f"工站 {station_name} 校准完成, 偏移值已保存到配置文件") + + return calibration_offset + + except Exception as e: + logger.error(f"工站点位校准失败: {e}") + return None + + def _query_with_retry(self, query_func, query_name, max_retries=None, retry_delay=None): + """ + 功能: + 带重试机制的AGV查询通用方法, 每次重试会创建新的AGVDriver连接 + 参数: + query_func: callable, 接收一个AGVDriver实例作为参数, 返回查询结果 + query_name: str, 查询操作名称, 用于日志输出 + max_retries: int, 最大重试次数, 默认使用配置值AGV_QUERY_MAX_RETRIES + retry_delay: float, 首次重试延迟秒数, 后续指数退避, 默认使用AGV_QUERY_RETRY_DELAY + 返回: + 查询结果或None, 所有重试均失败时返回None + """ + if max_retries is None: + max_retries = AGV_QUERY_MAX_RETRIES + if retry_delay is None: + retry_delay = AGV_QUERY_RETRY_DELAY + + for attempt in range(1, max_retries + 1): + agv_driver = AGVDriver(AGVDriverConfig( + host=AGV_HOST, + port=AGV_PORT, + port_navigation=AGV_PORT_NAVIGATION, + timeout_s=AGV_TIMEOUT, + debug_hex=False + )) + + try: + agv_driver.connect() + result = query_func(agv_driver) + return result + except Exception as e: + if attempt < max_retries: + # 计算指数退避延迟 + delay = retry_delay * (2 ** (attempt - 1)) + logger.warning( + "%s第%d次尝试失败: %s, %.1f秒后重试", + query_name, attempt, e, delay + ) + time.sleep(delay) + else: + logger.error( + "%s第%d次尝试失败(已达最大重试次数): %s", + query_name, attempt, e + ) + finally: + agv_driver.close() + + return None + + def query_current_station(self): + """ + 功能: + 查询AGV当前所在站点, 带重试机制 + 返回: + dict或None, 成功时返回包含站点信息的字典{"station_id": str, "station_name": str, "description": str}, 失败时返回None + """ + + def _do_query(agv_driver): + """执行站点查询并映射结果""" + location_info = agv_driver.query_robot_location() + current_station_id = location_info.get("current_station", "") + logger.debug("查询到当前站点ID: %s", current_station_id) + + if current_station_id in STATION_POSITIONS: + station_info = STATION_POSITIONS[current_station_id] + result = { + "station_id": current_station_id, + "station_name": station_info["name"], + "description": station_info["description"] + } + # 更新当前工站 + self.current_station = current_station_id + logger.info( + "当前站点: %s - %s (%s)", + current_station_id, station_info["name"], station_info["description"] + ) + return result + else: + logger.warning("未知的站点ID: %s", current_station_id) + return { + "station_id": current_station_id, + "station_name": "未知站点", + "description": "未在配置中找到该站点" + } + + return self._query_with_retry(_do_query, "查询站点") + + def query_battery_status(self, simple=True): + """ + 功能: + 查询AGV电池状态, 带重试机制 + 参数: + simple: True表示只返回电池电量, False返回完整信息, 默认True + 返回: + dict或None, 成功时返回包含电池状态的字典, 失败时返回None + - battery_level: 电池电量, 范围[0, 1] + - battery_temp: 电池温度, 单位℃ (仅完整模式) + - charging: 是否正在充电 (仅完整模式) + - 其他字段见AGVDriver.query_battery_status文档 + """ + + def _do_query(agv_driver): + """执行电池状态查询""" + battery_info = agv_driver.query_battery_status(simple=simple) + if battery_info.get("ret_code") == 0: + battery_level = battery_info.get("battery_level") + if battery_level is not None: + logger.info("电池电量: %.1f%%", battery_level * 100) + return battery_info + else: + raise ValueError("未获取到电池电量") + else: + raise RuntimeError( + "查询电池状态返回错误: %s" % battery_info.get("err_msg", "未知错误") + ) + + return self._query_with_retry(_do_query, "查询电池状态") + + def query_nav_task_status(self): + """ + 功能: + 查询AGV当前导航任务状态, 带重试机制, 用于判断AGV是否正在执行导航任务 + 参数: + 无 + 返回: + dict或None, 成功时返回包含导航状态的字典, 失败时返回None + - task_status: int, 状态码(0=NONE, 1=WAITING, 2=RUNNING, 3=SUSPENDED, 4=COMPLETED, 5=FAILED, 6=CANCELED) + - task_status_name: str, 状态名称 + """ + + def _do_query(agv_driver): + """执行导航状态查询""" + nav_info = agv_driver.query_agv_nav_status(simple=True) + if nav_info is None: + raise RuntimeError("查询导航状态返回空") + + task_status = nav_info.get("task_status") + task_status_name = TASK_STATUS_MAP.get(task_status, "UNKNOWN") + logger.debug("当前导航状态: %s(%s)", task_status_name, task_status) + return { + "task_status": task_status, + "task_status_name": task_status_name, + } + + return self._query_with_retry(_do_query, "查询导航任务状态") + + def navigate_to_station(self, station_id): + """ + 功能: + 控制AGV底盘移动到指定工站 + 参数: + station_id: 工站ID, 例如"LM1", "LM2"等, 从配置文件STATION_POSITIONS中读取 + 返回: + dict或None, 成功时返回导航响应字典, 失败时返回None + """ + # 验证工站ID是否有效 + if station_id not in STATION_POSITIONS: + logger.error(f"无效的工站ID: {station_id}, 可用的工站: {list(STATION_POSITIONS.keys())}") + return None + + station_info = STATION_POSITIONS[station_id] + logger.info(f"开始移动到工站: {station_id} ({station_info['description']})") + + # 初始化AGV驱动 + agv_driver = AGVDriver(AGVDriverConfig( + host=AGV_HOST, + port=AGV_PORT, + port_navigation=AGV_PORT_NAVIGATION, + timeout_s=AGV_TIMEOUT, + debug_hex=False + )) + + try: + # 连接到AGV导航端口 + logger.debug("正在连接到AGV导航端口...") + agv_driver.connect_navigation() + logger.debug("连接成功") + + # 调用导航函数移动到目标工站 + logger.debug(f"正在导航到目标工站: {station_id}...") + result = agv_driver.navigate_to_target(target_id=station_id) + + # 检查导航响应 + if result.get("ret_code") == 0: + logger.debug(f"导航指令发送成功, 响应: {result}") + # 更新当前工站 + self.current_station = station_id + logger.debug(f"当前工站已设置为: {station_id}") + return result + else: + logger.error(f"导航指令发送失败, 错误码: {result.get('ret_code')}, 错误信息: {result.get('err_msg', '未知错误')}") + return None + + except Exception as e: + logger.error(f"导航过程出错: {e}") + return None + finally: + # 关闭AGV连接 + agv_driver.close() + logger.debug("AGV连接已关闭") + + def safe_send_navigate_command(self, station_id): + """ + 功能: + 安全发送AGV导航命令到指定工站, 发送前先执行机械臂回零操作(固定回home_1), 发送完命令立即返回 + 参数: + station_id: 工站ID, 例如"LM1", "LM2"等, 从配置文件STATION_POSITIONS中读取 + 返回: + dict或None, 成功时返回导航命令响应字典, 失败时返回None + """ + logger.info(f"开始安全发送导航命令到工站: {station_id}") + + # 步骤1: 机械臂回零到home_1 + logger.info("步骤1: 执行机械臂回零操作(home_1)") + home_result = self.arm_go_home(block=True, home_name="home_1") + if home_result is None: + logger.error("机械臂回零失败, 取消AGV导航命令发送") + return None + logger.debug(f"机械臂回零完成: {home_result}") + + # 步骤2: 发送AGV导航命令到目标工站 + logger.info("步骤2: 发送AGV导航命令到目标工站") + + # 验证工站ID是否有效 + if station_id not in STATION_POSITIONS: + logger.error(f"无效的工站ID: {station_id}, 可用的工站: {list(STATION_POSITIONS.keys())}") + return None + + station_info = STATION_POSITIONS[station_id] + logger.info(f"发送导航命令到工站: {station_id} ({station_info['description']})") + + # 初始化AGV驱动 + agv_driver = AGVDriver(AGVDriverConfig( + host=AGV_HOST, + port=AGV_PORT, + port_navigation=AGV_PORT_NAVIGATION, + timeout_s=AGV_TIMEOUT, + debug_hex=False + )) + + try: + # 连接到AGV导航端口 + logger.debug("正在连接到AGV导航端口...") + agv_driver.connect_navigation() + logger.debug("连接成功") + + # 调用异步导航函数发送命令 + logger.debug(f"正在发送导航命令到目标工站: {station_id}...") + result = agv_driver.send_navigate_command(target_id=station_id) + + # 检查导航响应 + if result.get("ret_code") == 0: + logger.info(f"导航命令发送成功, 响应: {result}") + return result + else: + logger.error(f"导航命令发送失败, 错误码: {result.get('ret_code')}, 错误信息: {result.get('err_msg', '未知错误')}") + return None + + except Exception as e: + logger.error(f"发送导航命令过程出错: {e}") + return None + finally: + # 关闭AGV连接 + agv_driver.close() + logger.debug("AGV连接已关闭") + + def safe_navigate_to_station(self, station_id): + """ + 功能: + 安全移动AGV到指定工站, 移动前先执行机械臂回零操作(固定回home_1) + 参数: + station_id: 工站ID, 例如"LM1", "LM2"等, 从配置文件STATION_POSITIONS中读取 + 返回: + dict或None, 成功时返回导航响应字典, 失败时返回None + """ + logger.info(f"开始安全移动到工站: {station_id}") + + # 步骤1: 机械臂回零到home_1 + logger.debug("步骤1: 执行机械臂回零操作(home_1)") + home_result = self.arm_go_home(block=True, home_name="home_1") + if home_result is None: + logger.error("机械臂回零失败, 取消AGV移动") + return None + logger.debug(f"机械臂回零完成: {home_result}") + + # 步骤2: AGV移动到目标工站 + logger.debug("步骤2: AGV移动到目标工站") + result = self.navigate_to_station(station_id) + + return result + + def go_to_charging_station(self, block=False): + """ + 功能: + 让AGV移动到充电站CP6进行充电, 移动前先执行机械臂回零操作 + 参数: + block: 是否阻塞等待AGV到达, 默认False + - False: 发送导航命令后立即返回 + - True: 等待AGV到达充电站后返回 + 返回: + dict或None, 成功时返回导航响应字典, 失败时返回None + """ + logger.info(f"开始移动到充电站 (block={block})") + + # 根据block参数选择不同的导航方法 + if block: + # 阻塞模式: 使用safe_navigate_to_station等待AGV到达 + result = self.safe_navigate_to_station("CP6") + else: + # 非阻塞模式: 使用safe_send_navigate_command立即返回 + result = self.safe_send_navigate_command("CP6") + + if result is not None: + logger.info("成功到达充电站CP6") + else: + logger.error("移动到充电站失败") + + return result + + def auto_charge_check(self): + """ + 功能: + 自动充电检查函数, 按优先级依次判断并执行充电动作 + 前置守卫(任一成立则跳过本次): + 1. AGV导航任务正在运行(WAITING/RUNNING/SUSPENDED) + 主要逻辑: + - 不在CP6 → 不执行任何操作(not_at_cp6) + - 在CP6且电量<50%: + - 先查询完整电池状态, 已在充电则跳过进出站(already_charging) + - 无法确认是否正在充电则保守跳过(charging_state_unknown) + - 未在充电时执行充电循环CP6->PP5->CP6 + - 到达PP5后检查设备状态, 非空闲则认为被接管, 跳过后续(intercepted_after_pp5) + - 返回CP6后再次查询完整电池状态, 确认是否正在充电(charge_cycle_completed) + - 在CP6且电量>=50%→ 无需动作(battery_sufficient) + 参数: + 无 + 返回: + dict, 包含检查结果的字典: + - status: "success" / "skipped" / "error" + - action: 执行的动作标识 + - battery_level: 电池电量(查询成功时包含) + - charging: 是否正在充电(相关分支时包含) + - message: 详细信息 + """ + logger.info("开始自动充电检查") + + try: + # 前置守卫: AGV导航任务正忙时跳过 + nav_status = self.query_nav_task_status() + if nav_status is not None: + task_status = nav_status.get("task_status") + # 1=WAITING / 2=RUNNING / 3=SUSPENDED 均视为忙碌 + if task_status in {1, 2, 3}: + status_name = nav_status.get("task_status_name", "UNKNOWN") + logger.info(f"AGV导航任务正忙(状态={status_name}), 跳过本次充电检查") + return { + "status": "skipped", + "action": "skipped_busy_nav", + "nav_task_status": task_status, + "nav_task_status_name": status_name, + "message": f"AGV导航任务正忙(状态={status_name}), 跳过本次充电检查" + } + + # 步骤1: 查询当前位置 + logger.info("步骤1: 查询当前位置") + current_station = self.query_current_station() + + if current_station is None: + logger.error("查询当前位置失败") + return { + "status": "error", + "action": "query_location", + "message": "查询当前位置失败" + } + + current_station_id = current_station.get("station_id") + logger.info(f"当前位置: {current_station_id} - {current_station.get('station_name')}") + + # 步骤2: 查询电池电量 + logger.info("步骤2: 查询电池电量") + battery_info = self.query_battery_status(simple=True) + + if battery_info is None: + logger.error("查询电池电量失败") + return { + "status": "error", + "action": "query_battery", + "message": "查询电池电量失败" + } + + battery_level = battery_info.get("battery_level") + logger.info(f"当前电池电量: {battery_level * 100:.1f}%") + + # 步骤3: 若不在CP6, 不执行任何操作直接跳过 + if current_station_id != "CP6": + logger.info(f"当前不在CP6充电站(当前位置={current_station_id}), 跳过充电检查") + return { + "status": "skipped", + "action": "not_at_cp6", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"当前不在CP6充电站(当前位置={current_station_id}), 不执行任何操作" + } + + # 步骤4: 已在CP6, 电量低则执行充电循环 + if battery_level < 0.5: + logger.info("步骤4: 电量低于50%, 先确认当前是否正在充电") + battery_full_info = self.query_battery_status(simple=False) + charging_flag = battery_full_info.get("charging") if battery_full_info is not None else None + + if charging_flag is None: + logger.warning("无法确认AGV当前是否正在充电, 本次不执行进出站") + return { + "status": "skipped", + "action": "charging_state_unknown", + "battery_level": battery_level, + "message": "无法确认是否正在充电, 本次不执行进出站" + } + + if charging_flag is True: + logger.info("检测到AGV当前已在充电, 跳过CP6->PP5->CP6充电循环") + return { + "status": "success", + "action": "already_charging", + "battery_level": battery_level, + "charging": True, + "message": f"电量{battery_level * 100:.1f}%低于50%, 但当前已在充电, 跳过CP6->PP5->CP6" + } + + logger.info(f"电池电量{battery_level * 100:.1f}%低于50%, 当前未在充电, 开始充电循环") + + # 步骤4.1: 移动到PP5充电过渡点 + logger.info("步骤4.1: 移动到PP5充电过渡点") + result_pp5 = self.safe_navigate_to_station("PP5") + + if result_pp5 is None: + logger.error("移动到PP5失败") + return { + "status": "error", + "action": "move_to_pp5", + "battery_level": battery_level, + "message": f"电量{battery_level * 100:.1f}%低于50%, 但移动到PP5失败" + } + + logger.info("成功到达PP5") + + # 步骤4.2: 检查设备是否处于空闲状态, 非空闲则认为途中被接管 + logger.info("步骤4.2: 检查设备空闲状态") + nav_status_after_pp5 = self.query_nav_task_status() + if nav_status_after_pp5 is not None: + task_status_pp5 = nav_status_after_pp5.get("task_status") + # 0=NONE / 4=COMPLETED 视为空闲, 其余视为被接管 + if task_status_pp5 not in {0, 4}: + status_name_pp5 = nav_status_after_pp5.get("task_status_name", "UNKNOWN") + logger.info(f"到达PP5后设备非空闲(状态={status_name_pp5}), 认为途中被接管, 跳过后续步骤") + return { + "status": "skipped", + "action": "intercepted_after_pp5", + "battery_level": battery_level, + "nav_task_status": task_status_pp5, + "nav_task_status_name": status_name_pp5, + "message": f"到达PP5后设备非空闲(状态={status_name_pp5}), 认为途中被接管, 跳过后续步骤" + } + logger.info("设备处于空闲状态, 继续返回CP6") + + # 步骤4.3: 从PP5返回CP6 + logger.info("步骤4.3: 从PP5返回CP6") + result_cp6 = self.safe_navigate_to_station("CP6") + + if result_cp6 is None: + logger.error("从PP5返回CP6失败") + return { + "status": "error", + "action": "return_to_cp6", + "battery_level": battery_level, + "message": f"电量{battery_level * 100:.1f}%低于50%, 已到达PP5但返回CP6失败" + } + + logger.info("已返回CP6, 检查充电状态") + + # 步骤4.4: 查询完整电池状态, 确认是否正在充电 + logger.info("步骤4.4: 查询完整电池状态, 确认充电状态") + battery_full_info = self.query_battery_status(simple=False) + + if battery_full_info is not None and battery_full_info.get("charging"): + logger.info("确认AGV正在充电, 充电循环成功") + return { + "status": "success", + "action": "charge_cycle_completed", + "battery_level": battery_level, + "charging": True, + "message": f"电量{battery_level * 100:.1f}%低于50%, 已完成充电循环(CP6->PP5->CP6), 确认正在充电" + } + else: + charging_val = battery_full_info.get("charging") if battery_full_info is not None else None + logger.warning(f"已返回CP6但未检测到充电状态(charging={charging_val})") + return { + "status": "success", + "action": "charge_cycle_completed_no_charging", + "battery_level": battery_level, + "charging": False, + "message": f"电量{battery_level * 100:.1f}%低于50%, 已完成充电循环(CP6->PP5->CP6), 但未检测到正在充电" + } + else: + logger.info(f"电池电量{battery_level * 100:.1f}%充足, 无需充电") + return { + "status": "success", + "action": "battery_sufficient", + "battery_level": battery_level, + "message": f"电池电量{battery_level * 100:.1f}%充足, 无需充电" + } + + except Exception as e: + logger.error(f"自动充电检查过程中发生异常: {e}") + return { + "status": "error", + "action": "exception", + "message": f"自动充电检查过程中发生异常: {e}" + } + + def auto_charge_loop(self, interval_hours=1, retry_wait_minutes=5): + """ + 功能: + 自动充电循环函数, 持续监控AGV充电状态 + - AGV在CP6时: 执行电量检查与充电, 之后等待interval_hours + - AGV不在CP6时: 认为AGV正忙, 等待retry_wait_minutes后重试 + 参数: + interval_hours: AGV在CP6完成检查后的等待时间, 单位小时, 默认1小时 + retry_wait_minutes: AGV不在CP6时的重试间隔, 单位分钟, 默认5分钟 + 返回: + 无(持续运行, 按Ctrl+C中断) + """ + logger.info(f"启动自动充电循环, 检查间隔: {interval_hours}小时, 不在CP6时重试间隔: {retry_wait_minutes}分钟") + + while True: + try: + # 执行充电检查(内部已判断是否在CP6) + result = self.auto_charge_check() + action = result.get("action", "") + status = result.get("status", "") + logger.info(f"充电检查结果: {result}") + + # 根据检查结果决定等待时长 + if status == "skipped": + # AGV不在CP6, 短暂等待后重试 + wait_seconds = retry_wait_minutes * 60 + logger.info(f"跳过充电检查(原因={action}), 等待{retry_wait_minutes}分钟后重试...") + else: + # 完成了充电检查, 等待较长时间再检查 + wait_seconds = interval_hours * 3600 + logger.info(f"充电检查完成, 等待{interval_hours}小时后进行下次检查...") + + # 分段睡眠, 每60秒一次, 使KeyboardInterrupt能及时响应 + self._interruptible_sleep(wait_seconds) + + except KeyboardInterrupt: + logger.info("用户中断自动充电循环") + break + except Exception as e: + logger.error(f"自动充电循环中发生异常: {e}") + logger.info("等待5分钟后重试...") + self._interruptible_sleep(300) + + def auto_charge_pp5_cp6_check(self): + """ + 功能: + 基于PP5待命点和CP6充电站的自动充电检查函数. + 前置守卫: + 1. AGV导航任务正在运行时, 直接跳过本次检查. + 主要逻辑: + - 在PP5且电量<50%时, 进入CP6充电. + - 在PP5且电量>=50%时, 继续在PP5待命. + - 在CP6且电量>90%时, 返回PP5待命. + - 在CP6且电量<=90%时, 继续在CP6待命. + - 既不在PP5也不在CP6时, 视为工作途中并跳过本次检查. + 参数: + 无 + 返回: + dict, 包含检查结果的字典: + - status: "success" / "skipped" / "error" + - action: 执行的动作标识 + - battery_level: 电池电量, 查询成功时包含 + - current_station: 当前站点ID, 查询成功时包含 + - message: 详细信息 + """ + logger.info("开始PP5/CP6自动充电检查") + + try: + # 导航任务忙碌时直接跳过, 避免监控逻辑和现场任务争抢控制权. + nav_status = self.query_nav_task_status() + if nav_status is not None: + task_status = nav_status.get("task_status") + if task_status in {1, 2, 3}: + status_name = nav_status.get("task_status_name", "UNKNOWN") + logger.info(f"AGV导航任务正忙(状态={status_name}), 跳过本次PP5/CP6充电检查") + return { + "status": "skipped", + "action": "skipped_busy_nav", + "nav_task_status": task_status, + "nav_task_status_name": status_name, + "message": f"AGV导航任务正忙(状态={status_name}), 跳过本次PP5/CP6充电检查" + } + + logger.info("步骤1: 查询当前位置") + current_station = self.query_current_station() + if current_station is None: + logger.error("查询当前位置失败") + return { + "status": "error", + "action": "query_location", + "message": "查询当前位置失败" + } + + current_station_id = current_station.get("station_id") + logger.info(f"当前位置: {current_station_id} - {current_station.get('station_name')}") + + logger.info("步骤2: 查询电池电量") + battery_info = self.query_battery_status(simple=True) + if battery_info is None: + logger.error("查询电池电量失败") + return { + "status": "error", + "action": "query_battery", + "current_station": current_station_id, + "message": "查询电池电量失败" + } + + battery_level = battery_info.get("battery_level") + if battery_level is None: + logger.error("查询电池电量失败, 返回结果缺少battery_level") + return { + "status": "error", + "action": "query_battery", + "current_station": current_station_id, + "message": "查询电池电量失败, 返回结果缺少battery_level" + } + + logger.info(f"当前电池电量: {battery_level * 100:.1f}%") + + if current_station_id == "PP5": + if battery_level < 0.5: + logger.info("步骤3: AGV在PP5且电量低于50%, 准备进入CP6充电") + result_cp6 = self.safe_navigate_to_station("CP6") + if result_cp6 is None: + logger.error("从PP5移动到CP6失败") + return { + "status": "error", + "action": "move_to_cp6", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"电量{battery_level * 100:.1f}%低于50%, 但从PP5移动到CP6失败" + } + + logger.info("AGV已从PP5移动到CP6充电站") + return { + "status": "success", + "action": "pp5_to_cp6_for_charge", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"电量{battery_level * 100:.1f}%低于50%, 已从PP5移动到CP6充电" + } + + logger.info("AGV在PP5待命, 当前电量无需进入CP6") + return { + "status": "success", + "action": "standby_at_pp5", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"电量{battery_level * 100:.1f}%达到待命要求, 继续在PP5待命" + } + + if current_station_id == "CP6": + if battery_level > 0.9: + logger.info("步骤3: AGV在CP6且电量高于90%, 准备返回PP5待命") + result_pp5 = self.safe_navigate_to_station("PP5") + if result_pp5 is None: + logger.error("从CP6移动到PP5失败") + return { + "status": "error", + "action": "move_to_pp5", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"电量{battery_level * 100:.1f}%高于90%, 但从CP6移动到PP5失败" + } + + logger.info("AGV已从CP6返回PP5待命点") + return { + "status": "success", + "action": "cp6_to_pp5_after_charge", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"电量{battery_level * 100:.1f}%高于90%, 已从CP6返回PP5待命" + } + + logger.info("AGV在CP6待命, 当前电量尚未达到离站阈值") + return { + "status": "success", + "action": "standby_at_cp6", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"电量{battery_level * 100:.1f}%未高于90%, 继续在CP6待命" + } + + logger.info(f"AGV当前位置={current_station_id}, 视为工作途中, 跳过本次PP5/CP6充电检查") + return { + "status": "skipped", + "action": "working_in_progress", + "battery_level": battery_level, + "current_station": current_station_id, + "message": f"当前位置={current_station_id}, 不在PP5或CP6, 视为工作途中并跳过本次检查" + } + + except Exception as e: + logger.error(f"PP5/CP6自动充电检查过程中发生异常: {e}") + return { + "status": "error", + "action": "exception", + "message": f"PP5/CP6自动充电检查过程中发生异常: {e}" + } + + def auto_charge_pp5_cp6_loop(self, interval_hours=1, retry_wait_minutes=5): + """ + 功能: + 基于PP5待命点和CP6充电站的自动充电循环函数. + - 检查成功时, 等待interval_hours后执行下一轮. + - 检查被跳过或出错时, 等待retry_wait_minutes后重试. + 参数: + interval_hours: 检查成功后的等待时间, 单位小时, 默认1小时. + retry_wait_minutes: 检查跳过或出错后的重试间隔, 单位分钟, 默认5分钟. + 返回: + 无, 持续运行直到用户中断. + """ + logger.info( + f"启动PP5/CP6自动充电循环, 检查间隔: {interval_hours}小时, " + f"重试间隔: {retry_wait_minutes}分钟" + ) + + while True: + try: + result = self.auto_charge_pp5_cp6_check() + action = result.get("action", "") + status = result.get("status", "") + logger.info(f"PP5/CP6充电检查结果: {result}") + + if status == "success": + wait_seconds = interval_hours * 3600 + logger.info(f"检查成功(action={action}), 等待{interval_hours}小时后进行下次检查...") + else: + wait_seconds = retry_wait_minutes * 60 + logger.info(f"检查未完成(action={action}, status={status}), 等待{retry_wait_minutes}分钟后重试...") + + self._interruptible_sleep(wait_seconds) + + except KeyboardInterrupt: + logger.info("用户中断PP5/CP6自动充电循环") + break + except Exception as e: + logger.error(f"PP5/CP6自动充电循环中发生异常: {e}") + logger.info(f"等待{retry_wait_minutes}分钟后重试...") + self._interruptible_sleep(retry_wait_minutes * 60) + + def _interruptible_sleep(self, total_seconds): + """ + 功能: + 分段睡眠, 将长时间睡眠拆分为多个60秒片段 + 使KeyboardInterrupt信号能在最多60秒内得到响应 + 参数: + total_seconds: 总睡眠时间, 单位秒 + 返回: + 无 + """ + elapsed = 0 + while elapsed < total_seconds: + # 每次最多睡60秒, 剩余不足60秒则按实际时间睡 + step = min(60, total_seconds - elapsed) + time.sleep(step) + elapsed += step + + def move_to_grasp_position(self, tray_name, block=True): + """ + 功能: + 运动到抓取点位, 执行取托盘流程的前4步(到下探完成为止) + 参数: + tray_name: 托盘位置名称, 例如"agv_tray_1", 从配置文件tray_position中读取 + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + bool, True表示运动成功, False表示运动失败 + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行运动到抓取点位") + return False + + # 获取托盘位置配置 + tray_position = self.position_manager.get_position('tray_position', tray_name) + if tray_position is None: + logger.error(f"未找到托盘位置配置: tray_position.{tray_name}") + return False + + # 判断是否需要应用站点偏移量 + station_offset = None + matched_station = None + if not tray_name.startswith('agv'): + # 读取配置文件中的所有站点校准信息 + try: + import yaml + with open(self.position_manager.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is not None and 'station_calibration' in config: + # 遍历所有站点名称, 检查tray_name是否以某个站点名称开头 + for station_name in config['station_calibration'].keys(): + if tray_name.startswith(station_name): + matched_station = station_name + logger.debug(f"托盘 {tray_name} 匹配到站点 {station_name}, 尝试获取校准偏移量") + + # 获取该站点的校准偏移量 + station_offset = self.position_manager.get_calibration_offset(station_name) + if station_offset is not None: + logger.debug(f"找到站点 {station_name} 的校准偏移量: x={station_offset['x']:.6f}, y={station_offset['y']:.6f}, z={station_offset['z']:.6f}, dx={station_offset['dx']:.6f}, dy={station_offset['dy']:.6f}, dz={station_offset['dz']:.6f}") + break + + if matched_station is None: + logger.debug(f"托盘 {tray_name} 未匹配到任何站点校准配置, 使用原始坐标") + except Exception as e: + logger.warning(f"读取站点校准配置失败: {e}, 使用原始坐标") + + logger.debug(f"开始执行运动到抓取点位流程: {tray_name}") + + try: + # 步骤1: 运动到安全姿态 + logger.debug("步骤1: 运动到安全姿态") + result = self.arm_go_home(block=block) + logger.debug(f"安全姿态运动完成: {result}") + + # 步骤2: 张开夹爪 + logger.debug("步骤2: 张开夹爪") + self.arm.open_gripper(block=True) + logger.debug("夹爪已张开") + + # 步骤3: 运动到过渡点 (先rx,ry,rz, 然后z, 最后xy且速度减半) + logger.debug("步骤3: 运动到过渡点") + # 获取抓取点位姿(配置文件中存储的是抓取点) + grasp_pose = tray_position.pose.copy() + # 获取下探距离, 用于计算过渡点 + config_descend_z = tray_position.descend_z if hasattr(tray_position, 'descend_z') else -32 + # 计算过渡点: 过渡点z = 抓取点z - descend_z (descend_z为负值, 所以减去它等于加上绝对值) + transition_pose = grasp_pose.copy() + transition_pose[2] = grasp_pose[2] - config_descend_z + logger.debug(f"抓取点z={grasp_pose[2]:.2f}, descend_z={config_descend_z}, 过渡点z={transition_pose[2]:.2f}") + + # 如果有站点偏移量, 则应用到过渡点位姿 + if station_offset is not None: + transition_pose[0] += station_offset['x'] + transition_pose[1] += station_offset['y'] + transition_pose[2] += station_offset['z'] + transition_pose[3] += station_offset['dx'] + transition_pose[4] += station_offset['dy'] + transition_pose[5] += station_offset['dz'] + logger.debug(f"应用站点偏移量后的过渡点位姿: {transition_pose}") + + # 3.1: 先运动rx,ry,rz到位 (使用关节运动避免多圈问题) + current_pose = self.arm.get_tcp_pose() + target_pose_for_orientation = [ + current_pose[0] / 1000.0, # 转换为m + current_pose[1] / 1000.0, + current_pose[2] / 1000.0, + transition_pose[3], + transition_pose[4], + transition_pose[5] + ] + # 使用逆运动学计算目标关节角度 + current_joints = self.arm.get_joints_position() + target_joints = self.arm.calculate_inverse_kinematics( + pose=target_pose_for_orientation, + q_near=current_joints # 使用当前关节角度作为参考, 确保选解合理 + ) + # 使用关节运动 (move_to_joints会自动调整j6角度到合理范围) + result = self.arm.move_to_joints( + joints_list=target_joints, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"姿态调整完成(关节运动): {result}") + + # 3.2: 根据目标点Y坐标判断运动策略 + if transition_pose[1] < 450: + # Y < 450, 在车内, xyz直接同时运动到位 + logger.debug("目标点在车内(Y<450), xyz直接运动到位") + result = self.arm.move_linear( + pose=transition_pose, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"过渡点运动完成: {result}") + else: + # Y >= 450, 在车外, 先运动到y=400, 然后y前伸 + logger.debug("目标点在车外(Y>=450), 先运动到y=400") + current_pose = self.arm.get_tcp_pose() + intermediate_pose = [ + transition_pose[0], + 400, + transition_pose[2], + current_pose[3], + current_pose[4], + current_pose[5] + ] + result = self.arm.move_linear( + pose=intermediate_pose, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"运动到y=450完成: {result}") + + # 然后y前伸到目标点 + logger.debug("y前伸到目标点") + result = self.arm.move_linear( + pose=transition_pose, + v=tray_position.speed * 0.5, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"过渡点运动完成: {result}") + + # 步骤4: 下探到抓取点位置 (只有Z轴变化) + logger.debug("步骤4: 下探到抓取点位置") + # 计算抓取点位姿(应用站点偏移量) + target_grasp_pose = grasp_pose.copy() + if station_offset is not None: + target_grasp_pose[0] += station_offset['x'] + target_grasp_pose[1] += station_offset['y'] + target_grasp_pose[2] += station_offset['z'] + target_grasp_pose[3] += station_offset['dx'] + target_grasp_pose[4] += station_offset['dy'] + target_grasp_pose[5] += station_offset['dz'] + logger.debug(f"目标抓取点位姿: {target_grasp_pose}") + result = self.arm.move_linear( + pose=target_grasp_pose, + v=tray_position.speed * 0.5, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"下探完成: {result}") + + logger.debug(f"运动到抓取点位流程完成: {tray_name}") + return True + + except Exception as e: + logger.error(f"运动到抓取点位流程失败: {e}") + # 发生异常时张开夹爪 + try: + self.arm.open_gripper(block=True) + except Exception: + pass + return False + + def put_tray(self, tray_name, descend_z=None, lift_z=None, transition_z_offset=0, block=True): + """ + 功能: + 通用放托盘逻辑, 按照标准流程放置托盘 + 参数: + tray_name: 托盘位置名称, 例如"agv_tray_1", 从配置文件tray_position中读取 + descend_z: 下探距离(mm), 为None时从配置文件读取 + lift_z: 提升距离(mm), 为None时从配置文件读取 + transition_z_offset: 过渡点Z值偏移量(mm), 默认为0 + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + bool, True表示放置成功, False表示放置失败 + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行放托盘动作") + return False + + # 获放托盘位置配置 + tray_position = self.position_manager.get_position('tray_position', tray_name) + if tray_position is None: + logger.error(f"未找到托盘位置配置: tray_position.{tray_name}") + return False + + # 判断是否需要应用站点偏移量 + station_offset = None + matched_station = None + if not tray_name.startswith('agv'): + # 读取配置文件中的所有站点校准信息 + try: + import yaml + with open(self.position_manager.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is not None and 'station_calibration' in config: + # 遍历所有站点名称, 检查tray_name是否以某个站点名称开头 + for station_name in config['station_calibration'].keys(): + if tray_name.startswith(station_name): + matched_station = station_name + logger.info(f"托盘 {tray_name} 匹配到站点 {station_name}, 尝试获取校准偏移量") + + # 获取该站点的校准偏移量 + station_offset = self.position_manager.get_calibration_offset(station_name) + if station_offset is not None: + logger.info(f"找到站点 {station_name} 的校准偏移量: x={station_offset['x']:.6f}, y={station_offset['y']:.6f}, z={station_offset['z']:.6f}, dx={station_offset['dx']:.6f}, dy={station_offset['dy']:.6f}, dz={station_offset['dz']:.6f}") + break + + if matched_station is None: + logger.info(f"托盘 {tray_name} 未匹配到任何站点校准配置, 使用原始坐标") + except Exception as e: + logger.warning(f"读取站点校准配置失败: {e}, 使用原始坐标") + + logger.info(f"开始执行放托盘流程: {tray_name}") + + try: + # 步骤1: 运动到安全姿态 + logger.debug("步骤1: 运动到安全姿态") + result = self.arm_go_home(block=block) + logger.debug(f"安全姿态运动完成: {result}") + + # 步骤2: 运动到过渡点 (先rx,ry,rz, 然后z, 最后xy且速度减半) + logger.debug("步骤2: 运动到过渡点") + # 获取抓取点位姿(配置文件中存储的是抓取点) + grasp_pose = tray_position.pose.copy() + # 获取下探距离, 用于计算过渡点 + config_descend_z = tray_position.descend_z if hasattr(tray_position, 'descend_z') else -32 + # 计算过渡点: 过渡点z = 抓取点z - descend_z (descend_z为负值, 所以减去它等于加上绝对值) + transition_pose = grasp_pose.copy() + transition_pose[2] = grasp_pose[2] - config_descend_z + logger.debug(f"抓取点z={grasp_pose[2]:.2f}, descend_z={config_descend_z}, 过渡点z={transition_pose[2]:.2f}") + + # 如果有站点偏移量, 则应用到过渡点位姿 + if station_offset is not None: + transition_pose[0] += station_offset['x'] + transition_pose[1] += station_offset['y'] + transition_pose[2] += station_offset['z'] + transition_pose[3] += station_offset['dx'] + transition_pose[4] += station_offset['dy'] + transition_pose[5] += station_offset['dz'] + logger.debug(f"应用站点偏移量后的过渡点位姿: {transition_pose}") + + # 应用过渡点Z偏移量(用于物料高度补偿) + if transition_z_offset != 0: + transition_pose[2] += transition_z_offset + logger.debug(f"应用过渡点Z偏移量{transition_z_offset}mm后的过渡点位姿: {transition_pose}") + + # 2.1: 先运动rx,ry,rz到位 (使用关节运动避免多圈问题) + current_pose = self.arm.get_tcp_pose() + target_pose_for_orientation = [ + current_pose[0] / 1000.0, # 转换为m + current_pose[1] / 1000.0, + current_pose[2] / 1000.0, + transition_pose[3], + transition_pose[4], + transition_pose[5] + ] + # 使用逆运动学计算目标关节角度 + current_joints = self.arm.get_joints_position() + target_joints = self.arm.calculate_inverse_kinematics( + pose=target_pose_for_orientation, + q_near=current_joints # 使用当前关节角度作为参考, 确保选解合理 + ) + # 使用关节运动 (move_to_joints会自动调整j6角度到合理范围) + result = self.arm.move_to_joints( + joints_list=target_joints, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"姿态调整完成(关节运动): {result}") + + # 2.2: 根据目标点Y坐标判断运动策略 + if transition_pose[1] < 450: + # Y < 450, 在车内, xyz直接同时运动到位 + logger.debug("目标点在车内(Y<450), xyz直接运动到位") + result = self.arm.move_linear( + pose=transition_pose, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"过渡点运动完成: {result}") + else: + # Y >= 450, 在车外, 先运动到y=400, 然后y前伸 + logger.debug("目标点在车外(Y>=450), 先运动到y=400") + current_pose = self.arm.get_tcp_pose() + intermediate_pose = [ + transition_pose[0], + 400, + transition_pose[2], + current_pose[3], + current_pose[4], + current_pose[5] + ] + result = self.arm.move_linear( + pose=intermediate_pose, + v=tray_position.speed, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"运动到y=400完成: {result}") + + # 然后y前伸到目标点 + logger.info("y前伸到目标点") + result = self.arm.move_linear( + pose=transition_pose, + v=tray_position.speed * 0.2, + a=tray_position.acceleration, + block=block + ) + logger.info(f"过渡点运动完成: {result}") + + # 步骤3: 下探到抓取点位置 (只有Z轴变化) + logger.debug("步骤3: 下探到抓取点位置") + # 计算抓取点位姿(应用站点偏移量) + target_grasp_pose = grasp_pose.copy() + if station_offset is not None: + target_grasp_pose[0] += station_offset['x'] + target_grasp_pose[1] += station_offset['y'] + target_grasp_pose[2] += station_offset['z'] + target_grasp_pose[3] += station_offset['dx'] + target_grasp_pose[4] += station_offset['dy'] + target_grasp_pose[5] += station_offset['dz'] + # 如果传入了自定义descend_z, 则使用自定义值计算目标z + if descend_z is not None: + current_pose = self.arm.get_tcp_pose() + target_grasp_pose[2] = current_pose[2] + descend_z + # 应用物料高度偏移到抓取点(与过渡点保持一致的偏移) + if transition_z_offset != 0: + target_grasp_pose[2] += transition_z_offset + logger.debug(f"应用物料高度偏移{transition_z_offset}mm到抓取点") + logger.debug(f"目标抓取点位姿: {target_grasp_pose}") + result = self.arm.move_linear( + pose=target_grasp_pose, + v=tray_position.speed * 0.1, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"下探完成: {result}") + + # 步骤4: 松开夹爪并检查是否松开 + logger.debug("步骤4: 松开夹爪") + self.arm.open_gripper(block=True) + + # 等待夹爪稳定并检查状态 + import time + time.sleep(1) + + if not self.arm.is_gripper_opened(): + logger.error("夹爪未松开") + return False + logger.debug("夹爪松开成功") + + # 步骤5: 提升一定距离 (只有Z轴变化) + logger.debug("步骤5: 提升") + # 如果未传入lift_z参数, 则从配置文件读取 + if lift_z is None: + lift_z = tray_position.lift_z if hasattr(tray_position, 'lift_z') else 0.1 + current_pose = self.arm.get_tcp_pose() + logger.debug(f"当前位姿: {current_pose}, 提升距离: {lift_z}mm") + lift_pose = [ + current_pose[0], + current_pose[1], + current_pose[2] + lift_z, + current_pose[3], + current_pose[4], + current_pose[5] + ] + logger.debug(f"目标位姿: {lift_pose}") + result = self.arm.move_linear( + pose=lift_pose, + v=tray_position.speed * 0.5, + a=tray_position.acceleration, + block=block + ) + logger.debug(f"提升完成: {result}") + + # 步骤6: 回到home位置 + logger.debug("步骤6: 回到home位置") + result = self.arm_go_home(block=block) + logger.debug(f"回到home位置完成: {result}") + + logger.info(f"放托盘流程完成: {tray_name}") + return True + + except Exception as e: + logger.error(f"放托盘流程失败: {e}") + return False + + # ==================== 夹爪管理 ==================== + + def get_current_gripper(self): + """ + 功能: + 获取当前安装的夹爪名称 + 返回: + str或None, 当前夹爪名称 + """ + return self.arm.get_current_gripper() + + def change_gripper(self, target_gripper, block=True): + """ + 功能: + 自动更换夹爪 + 参数: + target_gripper: 目标夹爪名称, 如"gripper_type_a" + block: 是否阻塞执行 + 返回: + bool, True表示更换成功 + """ + # 检查目标夹爪配置是否存在 + gripper_config = self.position_manager.get_gripper(target_gripper) + if gripper_config is None: + logger.error(f"未找到夹爪配置: {target_gripper}") + return False + + # 如果当前已经是目标夹爪, 直接返回 + current_gripper = self.arm.get_current_gripper() + if current_gripper == target_gripper: + logger.info(f"当前已安装目标夹爪: {target_gripper}") + return True + + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行换爪操作") + return False + + logger.info(f"开始更换夹爪: {current_gripper} -> {target_gripper}") + + try: + # 步骤1: 回到安全位置 + logger.info("步骤1: 回到安全位置") + self.arm_go_home(block=block) + + # 步骤2: 如果当前有夹爪, 先放回原位 + if current_gripper is not None: + logger.info(f"步骤2: 放回当前夹爪 {current_gripper}") + success = self._put_gripper(current_gripper, block=block) + if not success: + logger.error(f"放回夹爪失败: {current_gripper}") + return False + else: + logger.info("步骤2: 当前无夹爪, 跳过放回步骤") + + # 步骤3: 取出目标夹爪 + logger.info(f"步骤3: 取出目标夹爪 {target_gripper}") + success = self._pick_gripper(target_gripper, block=block) + if not success: + logger.error(f"取出夹爪失败: {target_gripper}") + return False + + # 步骤4: 更新当前夹爪状态 + self.arm.set_current_gripper(target_gripper) + + # 步骤5: 回到安全位置 + logger.info("步骤5: 回到安全位置") + self.arm_go_home(block=block) + + logger.info(f"夹爪更换完成: {target_gripper}") + return True + + except Exception as e: + logger.error(f"更换夹爪失败: {e}") + return False + + def _pick_gripper(self, gripper_name, block=True): + """ + 功能: + 从存放位置取出夹爪 + 参数: + gripper_name: 夹爪名称 + block: 是否阻塞执行 + 返回: + bool, True表示取出成功 + """ + # 获取夹爪存放位置 + storage_pos = self.position_manager.get_gripper_storage_position(gripper_name) + if storage_pos is None: + logger.error(f"未找到夹爪存放位置: {gripper_name}") + return False + + # 获取夹爪配置 + gripper_config = self.position_manager.get_gripper(gripper_name) + if gripper_config is None: + logger.error(f"未找到夹爪配置: {gripper_name}") + return False + + # 检查夹爪是否在料位上 + if not self.arm.check_quick_change_slot(gripper_config.slot): + logger.error(f"夹爪料位{gripper_config.slot}上没有夹爪") + return False + + logger.info(f"开始取出夹爪: {gripper_name}, 料位: {gripper_config.slot}") + + try: + # 松开快换装置 + logger.info("松开快换装置") + self.arm.release_quick_change(block=True) + + # 运动到存放位置上方 + logger.info("运动到夹爪存放位置上方") + self.arm.move_linear( + pose=storage_pos.pose, + v=storage_pos.speed, + a=storage_pos.acceleration, + block=block + ) + + # 下探对接夹爪 + logger.info("下探对接夹爪") + current_pose = self.arm.get_tcp_pose() + descend_pose = [ + current_pose[0], + current_pose[1], + current_pose[2] + storage_pos.descend_z, + current_pose[3], + current_pose[4], + current_pose[5] + ] + self.arm.move_linear( + pose=descend_pose, + v=storage_pos.speed * 0.5, + a=storage_pos.acceleration, + block=block + ) + + # 锁紧快换装置 + logger.info("锁紧快换装置") + self.arm.lock_quick_change(block=True) + import time + time.sleep(0.5) # 等待气缸动作完成 + + # 提升 + logger.info("提升夹爪") + current_pose = self.arm.get_tcp_pose() + lift_pose = [ + current_pose[0], + current_pose[1], + current_pose[2] + storage_pos.lift_z, + current_pose[3], + current_pose[4], + current_pose[5] + ] + self.arm.move_linear( + pose=lift_pose, + v=storage_pos.speed * 0.5, + a=storage_pos.acceleration, + block=block + ) + + logger.info(f"成功取出夹爪: {gripper_name}") + return True + + except Exception as e: + logger.error(f"取出夹爪失败: {e}") + return False + + def _put_gripper(self, gripper_name, block=True): + """ + 功能: + 将夹爪放回存放位置 + 参数: + gripper_name: 夹爪名称 + block: 是否阻塞执行 + 返回: + bool, True表示放回成功 + """ + # 获取夹爪存放位置 + storage_pos = self.position_manager.get_gripper_storage_position(gripper_name) + if storage_pos is None: + logger.error(f"未找到夹爪存放位置: {gripper_name}") + return False + + logger.info(f"开始放回夹爪: {gripper_name}") + + try: + # 运动到存放位置上方 + logger.info("运动到夹爪存放位置上方") + self.arm.move_linear( + pose=storage_pos.pose, + v=storage_pos.speed, + a=storage_pos.acceleration, + block=block + ) + + # 下探到存放位置 + logger.info("下探到存放位置") + current_pose = self.arm.get_tcp_pose() + descend_pose = [ + current_pose[0], + current_pose[1], + current_pose[2] + storage_pos.descend_z, + current_pose[3], + current_pose[4], + current_pose[5] + ] + self.arm.move_linear( + pose=descend_pose, + v=storage_pos.speed * 0.5, + a=storage_pos.acceleration, + block=block + ) + + # 松开快换装置 + logger.info("松开快换装置") + self.arm.release_quick_change(block=True) + import time + time.sleep(0.5) # 等待气缸动作完成 + + # 提升离开 + logger.info("提升离开") + current_pose = self.arm.get_tcp_pose() + lift_pose = [ + current_pose[0], + current_pose[1], + current_pose[2] + storage_pos.lift_z, + current_pose[3], + current_pose[4], + current_pose[5] + ] + self.arm.move_linear( + pose=lift_pose, + v=storage_pos.speed * 0.5, + a=storage_pos.acceleration, + block=block + ) + + # 清除当前夹爪状态 + self.arm.set_current_gripper(None) + + logger.info(f"成功放回夹爪: {gripper_name}") + return True + + except Exception as e: + logger.error(f"放回夹爪失败: {e}") + return False + + # ==================== 物料适配转移 ==================== + + def pick_tray_with_material(self, tray_name, material_type=None, block=True): + """ + 功能: + 根据物料类型取托盘, 自动选择夹爪和调整夹持高度 + 参数: + tray_name: 托盘位置名称 + material_type: 物料类型名称, 为None时使用默认参数 + block: 是否阻塞执行 + 返回: + bool, True表示抓取成功 + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败") + return False + + # 获取物料配置 + material_config = None + if material_type is not None: + material_config = self.position_manager.get_material(material_type) + if material_config is None: + logger.warning(f"未找到物料配置: {material_type}, 使用默认参数") + + # 如果有物料配置, 检查并更换夹爪 + if material_config is not None: + required_gripper = material_config.gripper + current_gripper = self.arm.get_current_gripper() + if current_gripper != required_gripper: + logger.info(f"物料 {material_type} 需要夹爪 {required_gripper}, 当前夹爪: {current_gripper}") + success = self.change_gripper(required_gripper, block=block) + if not success: + logger.error(f"更换夹爪失败, 无法继续取托盘") + return False + + # 获取托盘位置配置 + tray_position = self.position_manager.get_position('tray_position', tray_name) + if tray_position is None: + logger.error(f"未找到托盘位置配置: {tray_name}") + return False + + # 计算过渡点Z偏移量(用于物料高度补偿) + transition_z_offset = 0 + if material_config is not None: + # 物料偏移量应用到过渡点Z值, 使过渡点更高 + transition_z_offset = material_config.descend_z_offset + logger.info(f"应用物料偏移到过渡点Z值: transition_z_offset={transition_z_offset}mm") + + # 执行取托盘流程(使用过渡点Z偏移量) + result = self.pick_tray(tray_name, descend_z=None, lift_z=None, transition_z_offset=transition_z_offset, block=block) + + # 从车载位取走物料 → 清空 Deck 对应槽位 + if result and tray_name.startswith("agv_tray_"): + self._update_agv_deck_slot(tray_name, material_type=None) + + return result + + def put_tray_with_material(self, tray_name, material_type=None, block=True, + *, substance_details=None, tray_display_name=None): + """ + 功能: + 根据物料类型放托盘, 自动调整放置高度 + 参数: + tray_name: 托盘位置名称 + material_type: 物料类型名称, 为None时使用默认参数 + block: 是否阻塞执行 + substance_details: 物料中包含的试剂详情列表(用于 AGV Deck 显示) + tray_display_name: 托盘中文显示名称(用于 AGV Deck 显示) + 返回: + bool, True表示放置成功 + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败") + return False + + # 获取物料配置 + material_config = None + if material_type is not None: + material_config = self.position_manager.get_material(material_type) + if material_config is None: + logger.warning(f"未找到物料配置: {material_type}, 使用默认参数") + + # 获取托盘位置配置 + tray_position = self.position_manager.get_position('tray_position', tray_name) + if tray_position is None: + logger.error(f"未找到托盘位置配置: {tray_name}") + return False + + # 计算过渡点Z偏移量(用于物料高度补偿) + transition_z_offset = 0 + if material_config is not None: + # 物料偏移量应用到过渡点Z值, 使过渡点更高 + transition_z_offset = material_config.descend_z_offset + logger.info(f"应用物料偏移到过渡点Z值: transition_z_offset={transition_z_offset}mm") + + # 执行放托盘流程(使用过渡点Z偏移量) + result = self.put_tray(tray_name, descend_z=None, lift_z=None, transition_z_offset=transition_z_offset, block=block) + + # 物料放到车载位 → 更新 Deck 对应槽位 + if result and tray_name.startswith("agv_tray_"): + self._update_agv_deck_slot( + tray_name, material_type=material_type, + substance_details=substance_details, + tray_display_name=tray_display_name, + ) + + return result + + def transfer_material(self, source_tray, target_tray, material_type=None, block=True): + """ + 功能: + 物料转移, 从源位置取出并放到目标位置 + 参数: + source_tray: 源托盘位置名称 + target_tray: 目标托盘位置名称 + material_type: 物料类型名称 + block: 是否阻塞执行 + 返回: + bool, True表示转移成功 + """ + logger.info(f"开始物料转移: {source_tray} -> {target_tray}, 物料类型: {material_type}") + + # 从源位置取出 + success = self.pick_tray_with_material(source_tray, material_type, block) + if not success: + logger.error(f"从 {source_tray} 取出物料失败") + return False + + # 放到目标位置 + success = self.put_tray_with_material(target_tray, material_type, block) + if not success: + logger.error(f"放置物料到 {target_tray} 失败") + return False + + logger.info(f"物料转移完成: {source_tray} -> {target_tray}") + return True + + def batch_transfer_materials(self, transfer_tasks, block=True, on_station_delivered=None): + """ + 功能: + 批量物料转运, AGV自动移动到各站点并进行点位校准, 支持一次转运最多4个物料. + Phase 2 中每完成一个目标站点的全部放料后, 若提供了 on_station_delivered 回调, + 则在新线程中异步调用该回调, 不阻塞后续站点的转运. + 参数: + transfer_tasks: 转运任务列表, 每个任务为字典{"source_tray": str, "target_tray": str, "material_type": str} + 例如: [{"source_tray": "shelf_tray_1", "target_tray": "agv_tray_1", "material_type": "vial_10ml"}] + block: 是否阻塞执行 + on_station_delivered: 可选回调函数, 签名为 + callback(station_id: str, delivered_task_indices: list[int]) -> None. + Phase 2 每个目标站点全部放料完成后在新 daemon 线程中异步调用. + 回调异常不影响转运流程. 默认为 None. + 返回: + bool, True表示全部转运成功 + """ + # 验证任务数量 + if len(transfer_tasks) == 0: + logger.error("转运任务列表为空") + return False + + if len(transfer_tasks) > 4: + logger.error(f"转运任务数量超过限制, 最多支持4个任务, 当前: {len(transfer_tasks)}") + return False + + logger.info(f"开始批量物料转运, 共{len(transfer_tasks)}个任务") + + # 按站点分组任务 + source_stations = {} # {station_id: [task_index]} + target_stations = {} # {station_id: [task_index]} + + for idx, task in enumerate(transfer_tasks): + source_tray = task.get("source_tray") + target_tray = task.get("target_tray") + material_type = task.get("material_type") + + if not source_tray or not target_tray: + logger.error(f"任务{idx+1}缺少必要参数: source_tray或target_tray") + return False + + logger.info(f"任务{idx+1}: {source_tray} -> {target_tray}, 物料类型: {material_type}") + + # 确定源托盘所属站点 + source_station = self._get_station_from_tray(source_tray) + if source_station is not None: + if source_station not in source_stations: + source_stations[source_station] = [] + source_stations[source_station].append(idx) + + # 确定目标托盘所属站点 + target_station = self._get_station_from_tray(target_tray) + if target_station is not None: + if target_station not in target_stations: + target_stations[target_station] = [] + target_stations[target_station].append(idx) + + # 存储物料在AGV货架上的位置映射 + task_to_agv_tray = {} # {task_index: agv_tray_name} + agv_tray_names = ["agv_tray_1", "agv_tray_2", "agv_tray_3", "agv_tray_4"] + + try: + # 阶段1: 从各源站点取料并放到AGV货架 + logger.info("阶段1: 从源站点取料并放到AGV货架") + + for station_id, task_indices in source_stations.items(): + logger.info(f"处理源站点: {station_id}") + + # AGV移动到源站点 + logger.info(f"AGV移动到站点: {station_id}") + result = self.safe_navigate_to_station(station_id) + if result is None: + logger.error(f"AGV移动到站点{station_id}失败") + return False + + # 等待AGV到达 + logger.info("等待AGV到达站点...") + import time + time.sleep(2) + + # 进行点位校准 + logger.info(f"执行站点{station_id}点位校准") + calibration_result = self.calibrate_station(block=block) + if calibration_result is None: + logger.error(f"站点{station_id}校准失败, 停止运行") + return False + else: + logger.info(f"站点{station_id}校准成功") + + # 依次取出该站点的所有物料并放到AGV货架 + for task_idx in task_indices: + task = transfer_tasks[task_idx] + source_tray = task["source_tray"] + material_type = task.get("material_type") + + # 分配AGV货架位置 + agv_tray = agv_tray_names[task_idx] + task_to_agv_tray[task_idx] = agv_tray + + logger.info(f"任务{task_idx+1}: 从{source_tray}取料") + success = self.pick_tray_with_material(source_tray, material_type, block) + if not success: + logger.error(f"从{source_tray}取出物料失败") + return False + + logger.info(f"任务{task_idx+1}: 放置到AGV货架{agv_tray}") + success = self.put_tray_with_material( + agv_tray, material_type, block, + substance_details=task.get("substance_details"), + tray_display_name=task.get("tray_display_name"), + ) + if not success: + logger.error(f"放置物料到{agv_tray}失败") + return False + + logger.info(f"任务{task_idx+1}: 成功放置到AGV货架, 已完成{len(task_to_agv_tray)}/{len(transfer_tasks)}个取料") + + # 阶段2: 从AGV货架取料并放置到各目标站点 + logger.info("阶段2: 从AGV货架取料并放置到目标站点") + + for station_id, task_indices in target_stations.items(): + logger.debug(f"\n处理目标站点: {station_id}") + + # AGV移动到目标站点 + logger.info(f"AGV移动到站点: {station_id}") + result = self.safe_navigate_to_station(station_id) + if result is None: + logger.error(f"AGV移动到站点{station_id}失败") + return False + + # 等待AGV到达 + logger.info("等待AGV到达站点...") + import time + time.sleep(2) + + # 进行点位校准 + logger.info(f"执行站点{station_id}点位校准") + calibration_result = self.calibrate_station(block=block) + if calibration_result is None: + logger.error(f"站点{station_id}校准失败, 停止运行") + return False + else: + logger.info(f"站点{station_id}校准成功") + + # 依次从AGV货架取料并放置到目标位置 + for task_idx in task_indices: + task = transfer_tasks[task_idx] + target_tray = task["target_tray"] + material_type = task.get("material_type") + agv_tray = task_to_agv_tray[task_idx] + + logger.info(f"任务{task_idx+1}: 从AGV货架{agv_tray}取料") + success = self.pick_tray_with_material(agv_tray, material_type, block) + if not success: + logger.error(f"从{agv_tray}取出物料失败") + return False + + logger.info(f"任务{task_idx+1}: 放置到目标位置{target_tray}") + success = self.put_tray_with_material(target_tray, material_type, block) + if not success: + logger.error(f"放置物料到{target_tray}失败") + return False + + logger.info(f"任务{task_idx+1}: 成功放置到目标位置") + + # 该站点全部放料完成, 异步触发回调(不阻塞后续站点转运) + if on_station_delivered is not None: + _sid = station_id + _indices = list(task_indices) + + def _fire_callback(sid=_sid, indices=_indices): + try: + on_station_delivered(sid, indices) + except Exception as cb_exc: + logger.error( + f"on_station_delivered 回调执行异常 (station={sid}): {cb_exc}" + ) + + t = threading.Thread( + target=_fire_callback, daemon=True, name=f"agv_cb_{_sid}" + ) + t.start() + logger.info(f"站点 {station_id} 卸货完成, 已异步触发回调") + + logger.info(f"批量物料转运完成, 共完成{len(transfer_tasks)}个任务") + return True + + except Exception as e: + logger.error(f"批量物料转运失败: {e}") + return False + + def transfer_manifest( + self, + manifest, + *, + block=True, + auto_run_analysis=True, + go_to_charging_station_after=True, + batch_size=4, + ): + """ + 功能: + 执行标准化转运 manifest。 + manifest 由上游设备或 UniLab 编排层生成,至少需包含 transfer_tasks 字段。 + 参数: + manifest: Dict, 需包含 transfer_tasks 列表。 + block: 是否阻塞等待。 + auto_run_analysis: 是否在分析站卸货后自动提交分析任务。 + go_to_charging_station_after: 任务完成后是否返回充电站。 + batch_size: 每批最大托盘数,默认 4。 + 返回: + Dict, 包含批次结果、分析提交结果与错误信息。 + """ + if not isinstance(manifest, dict): + return { + "success": False, + "total_trays": 0, + "transferred_trays": 0, + "batches": [], + "errors": ["manifest 必须为 dict"], + "analysis_results": {}, + } + + transfer_tasks_all = manifest.get("transfer_tasks") or [] + if not isinstance(transfer_tasks_all, list): + return { + "success": False, + "total_trays": 0, + "transferred_trays": 0, + "batches": [], + "errors": ["manifest.transfer_tasks 必须为 list"], + "analysis_results": {}, + } + + if len(transfer_tasks_all) == 0: + logger.warning("manifest 中没有有效的转运任务") + return { + "success": True, + "total_trays": 0, + "transferred_trays": 0, + "batches": [], + "errors": list(manifest.get("errors", [])), + "analysis_results": {}, + } + + if batch_size <= 0: + batch_size = 4 + + errors = list(manifest.get("errors", [])) + batches_result = [] + transferred_count = 0 + analysis_submitted = False + analysis_results = {} + analysis_task_ids = { + str(task_id).strip() + for task_id in (manifest.get("analysis_task_ids") or []) + if str(task_id).strip() + } + analysis_tasks_count = int(manifest.get("analysis_tasks_count", 0) or 0) + + for batch_index in range(0, len(transfer_tasks_all), batch_size): + batch_tasks = transfer_tasks_all[batch_index:batch_index + batch_size] + batch_num = batch_index // batch_size + 1 + logger.info(f"开始执行 manifest 第 {batch_num} 批转运, 共 {len(batch_tasks)} 个托盘") + + batch_analysis_local_indices = set() + analysis_station_id = None + if analysis_task_ids and analysis_tasks_count > 0: + batch_end = min(batch_index + batch_size, len(transfer_tasks_all)) + batch_analysis_local_indices = { + i - batch_index for i in range(batch_index, batch_end) + if i < analysis_tasks_count + } + if batch_analysis_local_indices: + sample_tray = batch_tasks[min(batch_analysis_local_indices)].get("target_tray") + if sample_tray: + analysis_station_id = self._get_station_from_tray(sample_tray) + + def _on_station_delivered(station_id, task_indices): + nonlocal analysis_submitted + if station_id != analysis_station_id or analysis_submitted: + return + + analysis_submitted = True + logger.info("分析站卸货完成(回调触发), 立即提交分析任务: %s", sorted(analysis_task_ids)) + + from unilabos.devices.eit_analysis_station.controller.analysis_controller import ( + AnalysisStationController, + ) + analysis_ctrl = AnalysisStationController() + + for tid in sorted(analysis_task_ids): + try: + analysis_results[tid] = analysis_ctrl.run_analysis(task_id=tid) + logger.info("分析任务提交完成, task_id=%s", tid) + except Exception as exc: + error_msg = f"分析任务提交失败, task_id={tid}: {exc}" + logger.error(error_msg) + analysis_results[tid] = {"success": False, "error": error_msg} + + should_attach_callback = ( + auto_run_analysis + and len(analysis_task_ids) > 0 + and not analysis_submitted + and len(batch_analysis_local_indices) > 0 + and analysis_station_id is not None + ) + + try: + result = self.batch_transfer_materials( + batch_tasks, + block=block, + on_station_delivered=_on_station_delivered if should_attach_callback else None, + ) + except Exception as exc: + error_msg = f"第 {batch_num} 批 manifest 转运异常: {exc}" + logger.error(error_msg) + errors.append(error_msg) + batches_result.append( + { + "batch_num": batch_num, + "tasks": batch_tasks, + "success": False, + "error": str(exc), + } + ) + break + + batch_result = { + "batch_num": batch_num, + "tasks": batch_tasks, + "success": result, + } + batches_result.append(batch_result) + + if result: + transferred_count += len(batch_tasks) + logger.info(f"第 {batch_num} 批 manifest 转运成功") + + if ( + auto_run_analysis + and len(analysis_task_ids) > 0 + and not analysis_submitted + and transferred_count >= analysis_tasks_count + ): + analysis_submitted = True + logger.warning("分析回调未触发,走兜底路径提交分析任务: %s", sorted(analysis_task_ids)) + from unilabos.devices.eit_analysis_station.controller.analysis_controller import ( + AnalysisStationController, + ) + analysis_ctrl = AnalysisStationController() + for tid in sorted(analysis_task_ids): + try: + analysis_results[tid] = analysis_ctrl.run_analysis(task_id=tid) + logger.info("分析任务提交完成(兜底), task_id=%s", tid) + except Exception as exc: + error_msg = f"分析任务提交失败(兜底), task_id={tid}: {exc}" + logger.error(error_msg) + analysis_results[tid] = {"success": False, "error": error_msg} + else: + error_msg = f"第 {batch_num} 批 manifest 转运失败" + logger.error(error_msg) + errors.append(error_msg) + break + + success = transferred_count == len(transfer_tasks_all) and len(errors) == 0 + charging_result = None + if success and go_to_charging_station_after: + try: + charging_result = self.go_to_charging_station(block=block) + if charging_result is not None: + logger.info("manifest 转运完成, AGV 已返回充电站") + else: + error_msg = "manifest 转运完成, 但 AGV 返回充电站失败" + logger.warning(error_msg) + errors.append(error_msg) + success = False + except Exception as exc: + error_msg = f"manifest 转运后返回充电站异常: {exc}" + logger.error(error_msg) + errors.append(error_msg) + success = False + + # pick/put 已实时更新 Deck,无需额外同步 + + return { + "success": success, + "manifest_type": manifest.get("manifest_type", "generic_transfer"), + "total_trays": len(transfer_tasks_all), + "transferred_trays": transferred_count, + "batches": batches_result, + "errors": errors, + "analysis_results": analysis_results, + "charging_result": charging_result, + } + + def batch_transfer_cycle_test(self, transfer_tasks, cycle_count=1, block=True): + """ + 功能: + 批量物料转运循环测试, 执行正向转运->充电站->反向转运->充电站的循环 + 参数: + transfer_tasks: 转运任务列表, 每个任务为字典{"source_tray": str, "target_tray": str, "material_type": str} + 例如: [{"source_tray": "shelf_tray_1", "target_tray": "agv_tray_1", "material_type": "vial_10ml"}] + cycle_count: 循环次数, 默认为1 + block: 是否阻塞执行 + 返回: + dict, 包含测试结果的字典: + - success: bool, 是否全部成功 + - completed_cycles: int, 完成的循环次数 + - total_cycles: int, 总循环次数 + - failed_at: str或None, 失败的阶段描述 + """ + logger.info(f"=" * 80) + logger.info(f"开始批量物料转运循环测试, 共{cycle_count}轮循环") + logger.info(f"=" * 80) + + # 验证任务列表 + if len(transfer_tasks) == 0: + logger.error("转运任务列表为空") + return { + "success": False, + "completed_cycles": 0, + "total_cycles": cycle_count, + "failed_at": "任务验证失败: 任务列表为空" + } + + if len(transfer_tasks) > 4: + logger.error(f"转运任务数量超过限制, 最多支持4个任务, 当前: {len(transfer_tasks)}") + return { + "success": False, + "completed_cycles": 0, + "total_cycles": cycle_count, + "failed_at": f"任务验证失败: 任务数量超过限制({len(transfer_tasks)}>4)" + } + + # 显示任务信息 + logger.info(f"转运任务列表:") + for idx, task in enumerate(transfer_tasks, 1): + logger.info(f"任务{idx}: {task['source_tray']} <-> {task['target_tray']}, 物料类型: {task.get('material_type', '未指定')}") + + completed_cycles = 0 + + try: + for cycle in range(cycle_count): + logger.info(f"\n{'=' * 80}") + logger.info(f"第 {cycle + 1}/{cycle_count} 轮循环开始") + logger.info(f"{'=' * 80}") + + # 步骤1: 正向转运(源->目标) + logger.info(f"\n步骤1: 正向转运(源->目标)") + logger.info(f"-" * 80) + result = self.batch_transfer_materials(transfer_tasks, block=block) + if not result: + logger.error(f"第{cycle + 1}轮正向转运失败") + return { + "success": False, + "completed_cycles": completed_cycles, + "total_cycles": cycle_count, + "failed_at": f"第{cycle + 1}轮正向转运" + } + logger.info(f"第{cycle + 1}轮正向转运完成") + + # 步骤2: AGV移动到充电站 + logger.info(f"\n步骤2: AGV移动到充电站") + logger.info(f"-" * 80) + result = self.go_to_charging_station(block=True) + if result is None: + logger.error(f"第{cycle + 1}轮正向转运后移动到充电站失败") + return { + "success": False, + "completed_cycles": completed_cycles, + "total_cycles": cycle_count, + "failed_at": f"第{cycle + 1}轮正向转运后移动到充电站" + } + logger.info(f"第{cycle + 1}轮正向转运后成功到达充电站") + + # 步骤3: 反向转运(目标->源) + logger.info(f"\n步骤3: 反向转运(目标->源)") + logger.info(f"-" * 80) + # 构建反向任务列表(交换源和目标) + reverse_tasks = [] + for task in transfer_tasks: + reverse_task = { + "source_tray": task["target_tray"], + "target_tray": task["source_tray"], + "material_type": task.get("material_type") + } + reverse_tasks.append(reverse_task) + + result = self.batch_transfer_materials(reverse_tasks, block=block) + if not result: + logger.error(f"第{cycle + 1}轮反向转运失败") + return { + "success": False, + "completed_cycles": completed_cycles, + "total_cycles": cycle_count, + "failed_at": f"第{cycle + 1}轮反向转运" + } + logger.info(f"第{cycle + 1}轮反向转运完成") + + # 步骤4: AGV移动到充电站 + logger.info(f"\n步骤4: AGV移动到充电站") + logger.info(f"-" * 80) + result = self.go_to_charging_station(block=True) + if result is None: + logger.error(f"第{cycle + 1}轮反向转运后移动到充电站失败") + return { + "success": False, + "completed_cycles": completed_cycles, + "total_cycles": cycle_count, + "failed_at": f"第{cycle + 1}轮反向转运后移动到充电站" + } + logger.info(f"第{cycle + 1}轮反向转运后成功到达充电站") + + # 完成一轮循环 + completed_cycles += 1 + logger.info(f"\n{'=' * 80}") + logger.info(f"第 {cycle + 1}/{cycle_count} 轮循环完成") + logger.info(f"{'=' * 80}") + + # 全部循环完成 + logger.info(f"\n{'=' * 80}") + logger.info(f"批量物料转运循环测试全部完成! 共完成{completed_cycles}轮循环") + logger.info(f"{'=' * 80}") + + return { + "success": True, + "completed_cycles": completed_cycles, + "total_cycles": cycle_count, + "failed_at": None + } + + except Exception as e: + logger.error(f"批量物料转运循环测试失败: {e}") + return { + "success": False, + "completed_cycles": completed_cycles, + "total_cycles": cycle_count, + "failed_at": f"异常: {str(e)}" + } + + def transfer_analysis_to_shelf( + self, + source_trays=None, + material_type="FLASH_FILTER_OUTER_BOTTLE_TRAY", + poll_interval=30.0, + poll_timeout=7200.0, + block=True, + ): + """ + 功能: + 从分析站取走完成检测的样品, 放置到货架空位上. + 先轮询智达进样设备状态, 等待其变为 Idle 后执行转运. + 转运和货架状态更新完成后, 让 AGV 返回充电站待命. + 参数: + source_trays: 源托盘列表, 默认 ["analysis_station_tray_1-2"] + material_type: 物料类型标识, 默认 "FLASH_FILTER_OUTER_BOTTLE_TRAY" + poll_interval: 状态轮询间隔(秒), 默认 30 + poll_timeout: 轮询超时(秒), 默认 7200(2小时) + block: 是否阻塞执行 + 返回: + bool, True 表示转运和回充都成功, False 表示任一步失败 + """ + if source_trays is None: + source_trays = ["analysis_station_tray_1-2"] + + logger.info("开始分析站→货架样品转运, 源托盘: %s", source_trays) + + # 步骤1: 轮询智达设备状态, 等待变为 Idle + logger.info("步骤1: 轮询智达进样设备状态, 等待变为 Idle ...") + from unilabos.devices.eit_analysis_station.driver.zhida_driver import ZhidaClient + + client = ZhidaClient() + try: + client.connect() + logger.info("已连接到智达进样设备") + except Exception as e: + logger.error("连接智达进样设备失败: %s", e) + return False + + try: + elapsed = 0.0 + while elapsed < poll_timeout: + status_detail = client.get_status_detail() + base_status = status_detail["base_status"] + raw_status = status_detail["raw_status"] or "(空)" + logger.info( + "智达设备当前状态, 主状态: %s, 原始状态: %s, 已等待 %.0f 秒", + base_status, + raw_status, + elapsed, + ) + + if base_status == "Idle": + logger.info("智达设备主状态已空闲, 原始状态: %s, 准备执行转运", raw_status) + break + elif base_status in ("Offline", "Error"): + logger.error( + "智达设备异常状态, 主状态: %s, 原始状态: %s, 终止转运", + base_status, + raw_status, + ) + return False + else: + # Busy / RunSample 等状态, 继续等待 + time.sleep(poll_interval) + elapsed += poll_interval + else: + logger.error("轮询超时(%.0f秒), 设备未变为 Idle, 终止转运", poll_timeout) + return False + finally: + client.close() + + # 步骤2: 查找货架空闲槽位 + logger.info("步骤2: 查找货架空闲槽位, 需要 %d 个", len(source_trays)) + empty_slots = self.shelf_manager.find_empty_slots(len(source_trays)) + + if len(empty_slots) < len(source_trays): + logger.error( + "货架空闲槽位不足: 需要 %d 个, 仅有 %d 个", + len(source_trays), len(empty_slots), + ) + return False + + logger.info("已找到空闲槽位: %s", empty_slots) + + # 步骤3: 构建并执行转运任务 + logger.info("步骤3: 执行批量物料转运") + transfer_tasks = [] + for src, tgt in zip(source_trays, empty_slots): + transfer_tasks.append({ + "source_tray": src, + "target_tray": tgt, + "material_type": material_type, + }) + logger.info("转运任务: %s -> %s", src, tgt) + + result = self.batch_transfer_materials(transfer_tasks, block=block) + if not result: + logger.error("批量物料转运失败") + return False + + # 步骤4: 更新货架状态记录 + logger.info("步骤4: 更新货架物料状态") + for src, tgt in zip(source_trays, empty_slots): + self.shelf_manager.place_material( + slot_name=tgt, + material_type=material_type, + source=src, + description="分析完成样品(自动转运)", + ) + + # 货架状态已经与实际转运结果一致, 回充失败时不回滚状态. + logger.info("步骤5: 返回充电站") + try: + charge_result = self.go_to_charging_station(block=block) + except Exception as exc: + logger.error("分析站→货架样品转运完成, 但返回充电站异常: %s", exc) + return False + + if charge_result is None: + logger.error("分析站→货架样品转运完成, 但返回充电站失败") + return False + + logger.info("分析站→货架样品转运完成, AGV 已返回充电站") + return True + + def _get_station_from_tray(self, tray_name): + """ + 功能: + 根据托盘名称推断所属站点ID + 参数: + tray_name: 托盘位置名称 + 返回: + str或None, 站点ID, 如果是AGV上的托盘则返回None + """ + # 如果是AGV上的托盘, 返回None + if tray_name.startswith('agv'): + return None + + # 遍历所有站点, 查找匹配的站点名称 + for station_id, station_info in STATION_POSITIONS.items(): + station_name = station_info["name"] + if tray_name.startswith(station_name): + return station_id + + logger.warning(f"无法确定托盘{tray_name}所属站点") + return None + + def calibrate_tray_position(self, tray_name, block=True): + """ + 功能: + 托盘点位校准, 运动到抓取点位后等待用户手动矫正, 确认后保存当前TCP位姿到配置文件 + 对于非AGV点位, 会减去站点校准偏移量后存储原始TCP位姿 + 参数: + tray_name: 托盘位置名称, 例如"agv_tray_1", "shelf_tray_1-1" + block: 是否阻塞执行, True表示等待完成 + 返回: + list或None, 成功时返回保存的TCP位姿列表[x, y, z, rx, ry, rz], 失败时返回None + """ + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行点位校准") + return None + + # 获取托盘位置配置 + tray_position = self.position_manager.get_position('tray_position', tray_name) + if tray_position is None: + logger.error(f"未找到托盘位置配置: tray_position.{tray_name}") + return None + + logger.info(f"开始执行托盘点位校准: {tray_name}") + + try: + # 步骤1: 运动到抓取点位 + logger.info("步骤1: 运动到抓取点位") + result = self.move_to_grasp_position(tray_name, block=block) + if not result: + logger.error("运动到抓取点位失败") + return None + logger.info("已到达抓取点位, 请手动矫正机械臂位置") + + # 步骤2: 等待用户确认(在交互式环境中由调用者处理) + # 此处仅返回当前TCP位姿供调用者使用 + + # 步骤3: 获取当前TCP位姿 + current_pose = self.arm.get_tcp_pose() + logger.info(f"当前TCP位姿: {current_pose}") + + # 步骤4: 判断是否为AGV上的点位 + is_agv_position = tray_name.startswith('agv') + + if is_agv_position: + # AGV上的点位, 直接保存当前TCP位姿 + pose_to_save = current_pose + logger.info(f"AGV点位, 直接保存当前TCP位姿: {pose_to_save}") + else: + # 非AGV点位, 需要减去站点校准偏移量 + # 查找匹配的站点 + matched_station = None + import yaml + with open(self.position_manager.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is not None and 'station_calibration' in config: + for station_name in config['station_calibration'].keys(): + if tray_name.startswith(station_name): + matched_station = station_name + break + + if matched_station is not None: + # 获取站点校准偏移量 + station_offset = self.position_manager.get_calibration_offset(matched_station) + if station_offset is not None: + # 原始TCP位姿 = 当前TCP位姿 - 偏移量 + pose_to_save = [ + current_pose[0] - station_offset['x'], + current_pose[1] - station_offset['y'], + current_pose[2] - station_offset['z'], + current_pose[3] - station_offset['dx'], + current_pose[4] - station_offset['dy'], + current_pose[5] - station_offset['dz'] + ] + logger.info(f"非AGV点位, 匹配站点: {matched_station}") + logger.info(f"站点偏移量: x={station_offset['x']:.6f}, y={station_offset['y']:.6f}, z={station_offset['z']:.6f}") + logger.info(f"原始TCP位姿(减去偏移量后): {pose_to_save}") + else: + # 站点未校准, 使用当前位姿 + pose_to_save = current_pose + logger.warning(f"站点 {matched_station} 未校准, 直接保存当前TCP位姿") + else: + # 未匹配到站点, 使用当前位姿 + pose_to_save = current_pose + logger.warning(f"托盘 {tray_name} 未匹配到任何站点, 直接保存当前TCP位姿") + + return pose_to_save + + except Exception as e: + logger.error(f"托盘点位校准失败: {e}") + return None + + def save_calibrated_tray_position(self, tray_name, pose, description=None): + """ + 功能: + 保存校准后的托盘位置到配置文件 + 参数: + tray_name: 托盘位置名称 + pose: TCP位姿列表[x, y, z, rx, ry, rz] + description: 位置描述, 为None时保留原有描述 + 返回: + bool, True表示保存成功 + """ + try: + self.position_manager.save_tray_position(tray_name, pose, description) + logger.info(f"托盘位置 {tray_name} 已保存到配置文件") + return True + except Exception as e: + logger.error(f"保存托盘位置失败: {e}") + return False + + def calibrate_station_offset(self, block=True): + """ + 功能: + 工站整体偏差矫正, 通过选择参考点位计算偏移量并应用到工站所有点位 + 流程: + 1. 选择要校准的工站(agv或当前所在工站) + 2. 如果不是agv, 先进行视觉补偿并记录偏移量 + 3. 让用户选择一个参考点位 + 4. 询问是否运动到该点位 + 5. 用户微调坐标后按回车确认 + 6. 计算偏移量并询问是否应用到工站所有点位 + 参数: + block: 是否阻塞执行, True表示等待完成 + 返回: + dict或None, 成功时返回偏移量字典{"x": float, "y": float, "z": float}, 失败时返回None + """ + import yaml + + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行工站偏差矫正") + return None + + # 查询当前站点 + logger.info("正在查询当前站点...") + station_info = self.query_current_station() + current_station_name = None + if station_info is not None: + current_station_name = station_info["station_name"] + logger.info(f"当前站点: {station_info['station_id']} - {current_station_name}") + else: + logger.warning("查询当前站点失败, 只能校准agv工站") + + # 步骤1: 选择要校准的工站 + station_options = ["agv"] + if current_station_name is not None and current_station_name != "agv": + station_options.append(current_station_name) + + print("\n请选择要校准的工站:") + for idx, station in enumerate(station_options, 1): + print(f" {idx}. {station}") + + station_choice = input(f"请输入工站编号 (1-{len(station_options)}): ").strip() + if not station_choice.isdigit(): + logger.error("输入无效") + return None + + station_index = int(station_choice) - 1 + if station_index < 0 or station_index >= len(station_options): + logger.error("输入超出范围") + return None + + selected_station = station_options[station_index] + logger.info(f"选择校准工站: {selected_station}") + + # 步骤2: 如果不是agv, 先进行视觉补偿 + vision_offset = None + if selected_station != "agv": + print(f"\n是否先进行 {selected_station} 工站的视觉补偿校准?") + vision_confirm = input("确认执行视觉补偿? (y/n): ").strip().lower() + if vision_confirm == 'y': + logger.info(f"执行 {selected_station} 工站视觉补偿校准...") + calibration_result = self.calibrate_station(block=block) + if calibration_result is not None: + vision_offset = calibration_result + logger.info(f"视觉补偿完成, 偏移量: x={vision_offset['x']:.3f}, y={vision_offset['y']:.3f}, z={vision_offset['z']:.3f}") + else: + logger.error("视觉补偿校准失败, 停止运行") + return None + + # 步骤3: 获取该工站的所有点位供用户选择 + tray_positions = self.position_manager.get_category('tray_position') + if tray_positions is None: + logger.error("未找到托盘位置配置") + return None + + # 过滤出该工站的点位 + station_tray_list = [] + for tray_name in tray_positions.keys(): + if tray_name.startswith(selected_station): + station_tray_list.append(tray_name) + + if len(station_tray_list) == 0: + logger.error(f"工站 {selected_station} 没有可用的点位") + return None + + print(f"\n{selected_station} 工站的点位:") + for idx, tray_name in enumerate(station_tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + tray_choice = input(f"请选择参考点位 (1-{len(station_tray_list)}): ").strip() + if not tray_choice.isdigit(): + logger.error("输入无效") + return None + + tray_index = int(tray_choice) - 1 + if tray_index < 0 or tray_index >= len(station_tray_list): + logger.error("输入超出范围") + return None + + selected_tray = station_tray_list[tray_index] + logger.info(f"选择参考点位: {selected_tray}") + + # 获取该点位的原始TCP位姿(配置文件中存储的) + tray_position = self.position_manager.get_position('tray_position', selected_tray) + if tray_position is None or tray_position.pose is None: + logger.error(f"点位 {selected_tray} 没有有效的位姿数据") + return None + + original_pose = tray_position.pose.copy() + logger.info(f"原始TCP位姿(配置文件): {original_pose}") + + # 步骤4: 询问是否运动到该点位 + print(f"\n是否运动到点位 {selected_tray}?") + move_confirm = input("确认运动? (y/n): ").strip().lower() + + if move_confirm == 'y': + logger.info(f"运动到点位 {selected_tray}...") + result = self.move_to_grasp_position(selected_tray, block=block) + if not result: + logger.error("运动到点位失败") + return None + logger.info("已到达点位") + + # 步骤5: 让用户微调坐标 + print("\n请手动微调机械臂位置到正确位置") + print("提示: 可以使用示教器或其他方式调整机械臂位置") + print("微调完成后, 按回车键继续...") + input() + + # 获取当前TCP位姿 + current_pose = self.arm.get_tcp_pose() + logger.info(f"当前TCP位姿(微调后): {current_pose}") + + # 步骤6: 计算偏移量 + # 对于非agv工站, 需要考虑视觉补偿偏移量 + # 原始位姿 + 视觉偏移 = 期望位姿, 实际位姿 = 当前位姿 + # 整体偏移 = 当前位姿 - (原始位姿 + 视觉偏移) + if selected_station != "agv" and vision_offset is not None: + # 计算期望位姿(原始位姿 + 视觉偏移) + expected_pose = [ + original_pose[0] + vision_offset['x'], + original_pose[1] + vision_offset['y'], + original_pose[2] + vision_offset['z'], + original_pose[3] + vision_offset['dx'], + original_pose[4] + vision_offset['dy'], + original_pose[5] + vision_offset['dz'] + ] + logger.info(f"期望TCP位姿(原始+视觉偏移): {expected_pose}") + + # 计算整体偏移量 + offset_x = current_pose[0] - expected_pose[0] + offset_y = current_pose[1] - expected_pose[1] + offset_z = current_pose[2] - expected_pose[2] + offset_rx = current_pose[3] - expected_pose[3] + offset_ry = current_pose[4] - expected_pose[4] + offset_rz = current_pose[5] - expected_pose[5] + else: + # agv工站或无视觉偏移, 直接计算偏移量 + offset_x = current_pose[0] - original_pose[0] + offset_y = current_pose[1] - original_pose[1] + offset_z = current_pose[2] - original_pose[2] + offset_rx = current_pose[3] - original_pose[3] + offset_ry = current_pose[4] - original_pose[4] + offset_rz = current_pose[5] - original_pose[5] + + print(f"\n{'=' * 60}") + print("偏移量计算结果:") + print(f"{'=' * 60}") + print(f"原始TCP位姿(配置文件): x={original_pose[0]:.3f}, y={original_pose[1]:.3f}, z={original_pose[2]:.3f}") + print(f" rx={original_pose[3]:.6f}, ry={original_pose[4]:.6f}, rz={original_pose[5]:.6f}") + print(f"当前TCP位姿(微调后): x={current_pose[0]:.3f}, y={current_pose[1]:.3f}, z={current_pose[2]:.3f}") + print(f" rx={current_pose[3]:.6f}, ry={current_pose[4]:.6f}, rz={current_pose[5]:.6f}") + print(f"计算得到的偏移量: dx={offset_x:.3f}, dy={offset_y:.3f}, dz={offset_z:.3f}") + print(f" drx={offset_rx:.6f}, dry={offset_ry:.6f}, drz={offset_rz:.6f}") + + offset_result = { + "x": offset_x, + "y": offset_y, + "z": offset_z, + "rx": offset_rx, + "ry": offset_ry, + "rz": offset_rz + } + + # 步骤7: 询问是否应用到工站所有点位 + print(f"\n是否将此偏移量应用到 {selected_station} 工站的所有点位?") + print(f"将影响以下 {len(station_tray_list)} 个点位:") + for tray_name in station_tray_list: + print(f" - {tray_name}") + + apply_confirm = input("确认应用偏移量? (y/n): ").strip().lower() + + if apply_confirm != 'y': + logger.info("用户取消应用偏移量") + print(f"\n偏移量未应用, 但已计算:") + print(f" 位置: dx={offset_x:.3f}, dy={offset_y:.3f}, dz={offset_z:.3f}") + print(f" 姿态: drx={offset_rx:.6f}, dry={offset_ry:.6f}, drz={offset_rz:.6f}") + return offset_result + + # 应用偏移量到所有点位 + logger.info(f"开始应用偏移量到 {selected_station} 工站的所有点位...") + + success_count = 0 + fail_count = 0 + + for tray_name in station_tray_list: + try: + # 获取当前点位配置 + tray_pos = self.position_manager.get_position('tray_position', tray_name) + if tray_pos is None or tray_pos.pose is None: + logger.warning(f"点位 {tray_name} 没有有效的位姿数据, 跳过") + fail_count += 1 + continue + + # 计算新的位姿(原始位姿 + 偏移量) + old_pose = tray_pos.pose.copy() + new_pose = [ + old_pose[0] + offset_x, + old_pose[1] + offset_y, + old_pose[2] + offset_z, + old_pose[3] + offset_rx, + old_pose[4] + offset_ry, + old_pose[5] + offset_rz + ] + + # 保存新的位姿 + self.position_manager.save_tray_position(tray_name, new_pose) + logger.info(f"点位 {tray_name} 已更新: x={old_pose[0]:.3f}->{new_pose[0]:.3f}, y={old_pose[1]:.3f}->{new_pose[1]:.3f}, z={old_pose[2]:.3f}->{new_pose[2]:.3f}") + success_count += 1 + + except Exception as e: + logger.error(f"更新点位 {tray_name} 失败: {e}") + fail_count += 1 + + print(f"\n{'=' * 60}") + print("偏移量应用完成!") + print(f"{'=' * 60}") + print(f"成功更新: {success_count} 个点位") + print(f"更新失败: {fail_count} 个点位") + print(f"应用的偏移量:") + print(f" 位置: dx={offset_x:.3f}, dy={offset_y:.3f}, dz={offset_z:.3f}") + print(f" 姿态: drx={offset_rx:.6f}, dry={offset_ry:.6f}, drz={offset_rz:.6f}") + + return offset_result + + def test_all_positions(self, material_type, block=True): + """ + 功能: + 全点位测试函数, 从agv_tray_1取托盘依次放到每个点位再取回, 测试所有点位的准确性 + 测试流程: + 1. 提示用户在agv_tray_1放置托盘 + 2. 执行点位校准 + 3. 从agv_tray_1取托盘, 放到目标点位, 再取回放回agv_tray_1 + 4. 重复步骤3直到测试完所有点位 + 参数: + material_type: 托盘物料类型名称, 用于确定夹爪和高度偏移 + block: 是否阻塞执行, True表示等待完成 + 返回: + dict, 测试结果字典{"success": list, "failed": list, "skipped": list} + """ + import time + + logger.info("=" * 60) + logger.info("开始全点位测试") + logger.info("=" * 60) + + # 获取物料配置 + material_config = self.position_manager.get_material(material_type) + if material_config is None: + logger.error(f"未找到物料配置: {material_type}") + return {"success": [], "failed": [], "skipped": []} + + logger.info(f"物料类型: {material_type} - {material_config.description}") + logger.info(f"使用夹爪: {material_config.gripper}") + + # 自动连接机械臂 + if not self._ensure_connected(): + logger.error("机械臂连接失败, 无法执行全点位测试") + return {"success": [], "failed": [], "skipped": []} + + # 获取所有托盘位置 + tray_positions = self.position_manager.get_category('tray_position') + if tray_positions is None: + logger.error("未找到托盘位置配置") + return {"success": [], "failed": [], "skipped": []} + + # 过滤出当前站点的点位(排除agv开头的点位) + test_positions = [] + if self.current_station is not None and self.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[self.current_station]["name"] + for tray_name in tray_positions.keys(): + # 只测试当前站点的点位, 排除agv开头的点位 + if tray_name.startswith(station_name): + test_positions.append(tray_name) + else: + logger.warning("未设置当前站点, 将测试所有非AGV点位") + for tray_name in tray_positions.keys(): + if not tray_name.startswith('agv'): + test_positions.append(tray_name) + + if len(test_positions) == 0: + logger.error("当前站点没有可测试的点位") + return {"success": [], "failed": [], "skipped": []} + + logger.info(f"待测试点位数量: {len(test_positions)}") + for idx, pos_name in enumerate(test_positions, 1): + logger.info(f" {idx}. {pos_name}") + + # 测试结果记录 + results = { + "success": [], + "failed": [], + "skipped": [] + } + + try: + # 步骤1: 执行点位校准 + logger.info("\n" + "=" * 60) + logger.info("步骤1: 执行点位校准") + logger.info("=" * 60) + + calibration_result = self.calibrate_station(block=block) + if calibration_result is None: + logger.error("点位校准失败, 停止测试") + return results + else: + logger.info(f"点位校准成功: x={calibration_result['x']:.3f}, y={calibration_result['y']:.3f}, z={calibration_result['z']:.3f}") + + # 步骤2: 依次测试每个点位 + logger.info("\n" + "=" * 60) + logger.info("步骤2: 开始逐点位测试") + logger.info("=" * 60) + + for idx, target_position in enumerate(test_positions, 1): + logger.info(f"\n{'=' * 60}") + logger.info(f"测试点位 {idx}/{len(test_positions)}: {target_position}") + logger.info(f"{'=' * 60}") + + try: + # 2.1: 从agv_tray_1取托盘 + logger.info(f"从 agv_tray_1 取托盘...") + pick_result = self.pick_tray_with_material("agv_tray_1", material_type, block=block) + if not pick_result: + logger.error(f"从 agv_tray_1 取托盘失败") + results["failed"].append(target_position) + continue + + # 2.2: 放到目标点位 + logger.info(f"放置托盘到 {target_position}...") + put_result = self.put_tray_with_material(target_position, material_type, block=block) + if not put_result: + logger.error(f"放置托盘到 {target_position} 失败") + # 尝试回到home位置 + self.arm_go_home(block=block) + results["failed"].append(target_position) + continue + + # 2.3: 从目标点位取回托盘 + logger.info(f"从 {target_position} 取回托盘...") + pick_back_result = self.pick_tray_with_material(target_position, material_type, block=block) + if not pick_back_result: + logger.error(f"从 {target_position} 取回托盘失败") + results["failed"].append(target_position) + continue + + # 2.4: 放回agv_tray_1 + logger.info(f"放回托盘到 agv_tray_1...") + put_back_result = self.put_tray_with_material("agv_tray_1", material_type, block=block) + if not put_back_result: + logger.error(f"放回托盘到 agv_tray_1 失败") + results["failed"].append(target_position) + continue + + # 测试成功 + logger.info(f"点位 {target_position} 测试成功!") + results["success"].append(target_position) + + except Exception as e: + logger.error(f"测试点位 {target_position} 时发生异常: {e}") + results["failed"].append(target_position) + # 尝试回到home位置 + try: + self.arm_go_home(block=block) + except Exception: + pass + + # 输出测试结果汇总 + logger.info("\n" + "=" * 60) + logger.info("全点位测试完成") + logger.info("=" * 60) + logger.info(f"成功: {len(results['success'])} 个点位") + for pos in results["success"]: + logger.info(f" - {pos}") + logger.info(f"失败: {len(results['failed'])} 个点位") + for pos in results["failed"]: + logger.info(f" - {pos}") + logger.info(f"跳过: {len(results['skipped'])} 个点位") + for pos in results["skipped"]: + logger.info(f" - {pos}") + + return results + + except Exception as e: + logger.error(f"全点位测试过程中发生异常: {e}") + return results + + +def main(): + """ + 功能: + 交互式测试AGV控制器功能 + """ + # 配置日志输出到控制台 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + print("=" * 60) + print("AGV控制器交互式测试程序") + print("=" * 60) + + # 初始化控制器, 设置超时时间为3分钟(180000ms) + controller = AGVController(timeout=180000) + + # 查询并记录当前站点 + print("\n正在查询当前站点...") + station_info = controller.query_current_station() + current_station_display = "未知" + if station_info is not None: + current_station_display = f"{station_info['station_id']} - {station_info['station_name']} ({station_info['description']})" + print(f"当前站点: {current_station_display}") + else: + print("查询站点失败") + + # 交互式测试循环 + while True: + print("\n" + "=" * 60) + print(f"当前站点: {current_station_display}") + print("=" * 60) + print("请选择要测试的功能:") + print("1. 连接机械臂") + print("2. 断开机械臂连接") + print("3. 机械臂回零") + print("4. 查看当前状态") + print("5. 上电并使能") + print("6. 下使能并下电") + print("7. 测试取托盘") + print("8. 测试放托盘") + print("9. 测试快换控制") + print("10. 测试夹爪控制") + print("11. 查看夹爪状态") + print("12. 查看料盘状态") + print("13. 重新加载点位配置") + print("14. 工站点位校准") + print("15. 查看校准偏移值") + print("16. AGV移动到工站") + print("17. 查询当前站点") + print("18. 运动到抓取点位") + print("19. 物料适配转移") + print("20. 更换夹爪") + print("21. 查看物料/夹爪配置") + print("22. 批量物料转运(含AGV移动和校准)") + print("23. 托盘点位校准") + print("24. 全点位测试") + print("25. 工站整体偏差矫正") + print("26. 查询电池电量") + print("27. 自动充电检查(单次)") + print("28. 启动自动充电循环") + print("29. 批量物料转运循环测试") + print("30. 分析站→货架样品转运") + print("31. 查看/管理货架状态") + print("32. PP5/CP6自动充电检查(单次)") + print("33. 启动PP5/CP6自动充电循环") + print("0. 退出程序") + print("=" * 60) + + choice = input("请输入选项 (0-33): ").strip() + + if choice == "1": + # 连接机械臂 + print("\n--- 连接机械臂 ---") + result = controller.connect() + print(f"连接结果: {'成功' if result else '失败'}") + + elif choice == "2": + # 断开连接 + print("\n--- 断开机械臂连接 ---") + result = controller.disconnect() + print(f"断开结果: {'成功' if result else '失败'}") + + elif choice == "3": + # 机械臂回零 + print("\n--- 机械臂回零 ---") + try: + result = controller.arm_go_home(block=True) + print(f"回零结果: {result}") + except Exception as e: + print(f"错误: {e}") + + elif choice == "4": + # 查看当前状态 + print("\n--- 当前状态 ---") + try: + if not controller._ensure_connected(): + print("机械臂未连接") + continue + + state = controller.arm.get_robot_state() + print(f"机器人状态: {state}") + + joints = controller.arm.get_joints_position() + print(f"当前关节角度: {joints}") + + pose = controller.arm.get_tcp_pose() + print(f"当前TCP位姿: {pose}") + + # 显示当前所在工站 + if controller.current_station is not None: + print(f"当前所在工站: {controller.current_station}") + + # 计算原始TCP姿态(根据站点偏移量反推) + if controller.current_station in STATION_POSITIONS: + station_info = STATION_POSITIONS[controller.current_station] + station_name = station_info["name"] + + # 获取该工站的校准偏移值 + offset = controller.position_manager.get_calibration_offset(station_name) + if offset is not None: + # 原始TCP姿态 = 当前TCP位姿 - 偏移量 + original_pose = [ + pose[0] - offset['x'], + pose[1] - offset['y'], + pose[2] - offset['z'], + pose[3] - offset['dx'], + pose[4] - offset['dy'], + pose[5] - offset['dz'] + ] + print(f"原始TCP姿态(用于新点位存储): {original_pose}") + else: + print(f"工站 {station_name} 尚未校准, 无法计算原始TCP姿态") + else: + print("当前所在工站: 未设置") + + is_moving = controller.arm.is_moving() + print(f"是否在运动: {is_moving}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "5": + # 上电并使能 + print("\n--- 上电并使能 ---") + try: + if not controller._ensure_connected(): + print("机械臂连接失败") + continue + + print("正在上电...") + result = controller.arm.power_on(block=True) + print(f"上电结果: {result}") + + print("正在使能...") + result = controller.arm.enable(block=True) + print(f"使能结果: {result}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "6": + # 下使能并下电 + print("\n--- 下使能并下电 ---") + try: + if not controller._ensure_connected(): + print("机械臂未连接") + continue + + print("正在下使能...") + result = controller.arm.disable(block=True) + print(f"下使能结果: {result}") + + print("正在下电...") + result = controller.arm.power_off(block=True) + print(f"下电结果: {result}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "7": + # 测试取托盘 + print("\n--- 测试取托盘 ---") + + # 从配置文件读取可用的托盘位置 + try: + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 过滤托盘位置: 只显示当前站点的和AGV本身的 + filtered_tray_list = [] + if controller.current_station is not None and controller.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[controller.current_station]["name"] + for tray_name in tray_positions.keys(): + # 显示以当前站点名称开头的托盘或以agv开头的托盘 + if tray_name.startswith(station_name) or tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + else: + # 如果没有当前站点信息, 只显示AGV本身的托盘 + for tray_name in tray_positions.keys(): + if tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + + if not filtered_tray_list: + print("错误: 当前站点没有可用的托盘位置") + continue + + # 显示可用的托盘位置 + print("可用的托盘位置:") + for idx, tray_name in enumerate(filtered_tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + tray_list = filtered_tray_list + + # 用户输入选择 + choice_input = input(f"请输入托盘编号 (1-{len(tray_list)}): ").strip() + + # 验证输入 + if not choice_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + tray_index = int(choice_input) - 1 + if tray_index < 0 or tray_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + continue + + # 获取选中的托盘名称 + selected_tray = tray_list[tray_index] + + print(f"\n开始执行取托盘流程: {selected_tray}") + result = controller.pick_tray(selected_tray, block=True) + print(f"取托盘结果: {'成功' if result else '失败'}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "8": + # 测试放托盘 + print("\n--- 测试放托盘 ---") + + # 从配置文件读取可用的托盘位置 + try: + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 过滤托盘位置: 只显示当前站点的和AGV本身的 + filtered_tray_list = [] + if controller.current_station is not None and controller.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[controller.current_station]["name"] + for tray_name in tray_positions.keys(): + # 显示以当前站点名称开头的托盘或以agv开头的托盘 + if tray_name.startswith(station_name) or tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + else: + # 如果没有当前站点信息, 只显示AGV本身的托盘 + for tray_name in tray_positions.keys(): + if tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + + if not filtered_tray_list: + print("错误: 当前站点没有可用的托盘位置") + continue + + # 显示可用的托盘位置 + print("可用的托盘位置:") + for idx, tray_name in enumerate(filtered_tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + tray_list = filtered_tray_list + + # 用户输入选择 + choice_input = input(f"请输入托盘编号 (1-{len(tray_list)}): ").strip() + + # 验证输入 + if not choice_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + tray_index = int(choice_input) - 1 + if tray_index < 0 or tray_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + continue + + # 获取选中的托盘名称 + selected_tray = tray_list[tray_index] + + print(f"\n开始执行放托盘流程: {selected_tray}") + result = controller.put_tray(selected_tray, block=True) + print(f"放托盘结果: {'成功' if result else '失败'}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "9": + # 测试快换控制 + print("\n--- 测试快换控制 ---") + print("1. 松开快换") + print("2. 夹紧快换") + sub_choice = input("请选择操作 (1-2): ").strip() + + try: + if not controller._ensure_connected(): + print("机械臂未连接") + continue + + if sub_choice == "1": + print("正在松开快换...") + result = controller.arm.release_quick_change(block=True) + print(f"松开快换结果: {result}") + elif sub_choice == "2": + print("正在夹紧快换...") + result = controller.arm.lock_quick_change(block=True) + print(f"夹紧快换结果: {result}") + else: + print("无效的选项") + except Exception as e: + print(f"错误: {e}") + + elif choice == "10": + # 测试夹爪控制 + print("\n--- 测试夹爪控制 ---") + print("1. 张开夹爪") + print("2. 闭合夹爪") + sub_choice = input("请选择操作 (1-2): ").strip() + + try: + if not controller._ensure_connected(): + print("机械臂未连接") + continue + + if sub_choice == "1": + print("正在张开夹爪...") + result = controller.arm.open_gripper(block=True) + print(f"张开夹爪结果: {result}") + + # 等待并检查状态 + import time + time.sleep(1) + if controller.arm.is_gripper_opened(): + print("夹爪已张开到位") + else: + print("警告: 夹爪未张开到位") + + elif sub_choice == "2": + print("正在闭合夹爪...") + result = controller.arm.close_gripper(block=True) + print(f"闭合夹爪结果: {result}") + + # 等待并检查状态 + import time + time.sleep(1) + if controller.arm.is_gripper_gripped(): + print("夹爪已夹紧到位(夹到物料)") + elif controller.arm.is_gripper_empty(): + print("夹爪空夹(未夹到物料)") + else: + print("警告: 夹爪状态未知") + else: + print("无效的选项") + except Exception as e: + print(f"错误: {e}") + + elif choice == "11": + # 查看夹爪状态 + print("\n--- 夹爪状态 ---") + try: + if not controller._ensure_connected(): + print("机械臂未连接") + continue + + state = controller.arm.get_gripper_state() + print(f"夹爪状态: {state}") + + is_opened = controller.arm.is_gripper_opened() + print(f"是否张开到位: {is_opened}") + + is_gripped = controller.arm.is_gripper_gripped() + print(f"是否夹紧到位(有物料): {is_gripped}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "12": + # 查看料盘状态 + print("\n--- 料盘状态 ---") + print("1. 查看所有料位状态") + print("2. 查看指定快换料位") + print("3. 查看指定托盘料位") + sub_choice = input("请选择操作 (1-3): ").strip() + + try: + if not controller._ensure_connected(): + print("机械臂未连接") + continue + + if sub_choice == "1": + status = controller.arm.get_all_slots_status() + print("\n快换料位状态 (bg1-bg3):") + for i, has_material in enumerate(status["quick_change"], 1): + print(f" bg{i}: {'有料' if has_material else '无料'}") + print("\n托盘料位状态 (bg4-bg7):") + for i, has_material in enumerate(status["tray"], 4): + print(f" bg{i}: {'有料' if has_material else '无料'}") + + elif sub_choice == "2": + slot_num_input = input("请输入快换料位编号 (1-3): ").strip() + if not slot_num_input.isdigit(): + print("错误: 请输入有效的数字") + continue + slot_num = int(slot_num_input) + has_material = controller.arm.check_quick_change_slot(slot_num) + print(f"快换料位bg{slot_num}: {'有料' if has_material else '无料'}") + + elif sub_choice == "3": + slot_num_input = input("请输入托盘料位编号 (1-4, 对应bg4-bg7): ").strip() + if not slot_num_input.isdigit(): + print("错误: 请输入有效的数字") + continue + slot_num = int(slot_num_input) + has_material = controller.arm.check_tray_slot(slot_num) + print(f"托盘料位bg{slot_num + 3}: {'有料' if has_material else '无料'}") + + else: + print("无效的选项") + except ValueError: + print("错误: 请输入有效的数字") + except Exception as e: + print(f"错误: {e}") + + elif choice == "13": + # 重新加载点位配置 + print("\n--- 重新加载点位配置 ---") + try: + # 重新初始化PositionManager, 会重新读取yaml配置文件 + controller.position_manager = PositionManager() + print("点位配置已重新加载") + + # 显示加载的配置信息 + print("\n已加载的配置类别:") + categories = ['safe_positions', 'tray_position'] + for category in categories: + positions = controller.position_manager.get_category(category) + if positions: + print(f"\n{category}:") + for name in positions.keys(): + print(f" - {name}") + else: + print(f"\n{category}: 无配置") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "14": + # 工站点位校准 + print("\n--- 工站点位校准 ---") + print("说明: 将自动查询当前站点并执行校准") + print("支持的工站: shelf(货架), synthesis_station(合成站), analysis_station(分析站)") + print(f"当前站点: {current_station_display}") + + try: + print("\n警告: 校准程序将运行机械臂, 请确保周围安全!") + print("提示: 请确保AGV已移动到需要校准的工站") + confirm = input("确认执行校准? (y/n): ").strip().lower() + + if confirm != 'y': + print("已取消校准") + continue + + result = controller.calibrate_station(block=True) + + if result is not None: + print(f"\n校准成功! 偏移值:") + print(f" 位置偏移: x={result['x']:.6f}, y={result['y']:.6f}, z={result['z']:.6f}") + print(f" 姿态偏移: dx={result['dx']:.6f}, dy={result['dy']:.6f}, dz={result['dz']:.6f}") + print(f"偏移值已保存到配置文件") + else: + print("校准失败") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "15": + # 查看校准偏移值 + print("\n--- 查看校准偏移值 ---") + print("支持的工站:") + print("1. shelf (货架)") + print("2. synthesis_station (合成站)") + print("3. analysis_station (分析站)") + print("4. 查看所有工站") + + station_choice = input("请选择工站 (1-4): ").strip() + + try: + if station_choice == "4": + # 查看所有工站 + print("\n所有工站校准偏移值:") + for station in ["shelf", "synthesis_station", "analysis_station"]: + offset = controller.position_manager.get_calibration_offset(station) + if offset is not None: + print(f"\n{station}:") + print(f" 位置偏移: x={offset['x']:.6f}, y={offset['y']:.6f}, z={offset['z']:.6f}") + print(f" 姿态偏移: dx={offset['dx']:.6f}, dy={offset['dy']:.6f}, dz={offset['dz']:.6f}") + else: + print(f"\n{station}: 未校准") + else: + station_map = { + "1": "shelf", + "2": "synthesis_station", + "3": "analysis_station" + } + + if station_choice not in station_map: + print("错误: 无效的工站选择") + continue + + station_name = station_map[station_choice] + offset = controller.position_manager.get_calibration_offset(station_name) + + if offset is not None: + print(f"\n{station_name} 工站校准偏移值:") + print(f" 位置偏移: x={offset['x']:.6f}, y={offset['y']:.6f}, z={offset['z']:.6f}") + print(f" 姿态偏移: dx={offset['dx']:.6f}, dy={offset['dy']:.6f}, dz={offset['dz']:.6f}") + else: + print(f"{station_name} 工站尚未校准") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "16": + # AGV移动到工站 + print("\n--- AGV移动到工站 ---") + try: + # 显示可用的工站 + print("可用的工站:") + station_list = list(STATION_POSITIONS.keys()) + for idx, station_id in enumerate(station_list, 1): + station_info = STATION_POSITIONS[station_id] + print(f" {idx}. {station_id} - {station_info['description']}") + + # 用户输入选择 + choice_input = input(f"请输入工站编号 (1-{len(station_list)}): ").strip() + + # 验证输入 + if not choice_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + station_index = int(choice_input) - 1 + if station_index < 0 or station_index >= len(station_list): + print(f"错误: 请输入1到{len(station_list)}之间的数字") + continue + + # 获取选中的工站ID + selected_station = station_list[station_index] + station_info_selected = STATION_POSITIONS[selected_station] + + print(f"\n开始移动到工站: {selected_station} ({station_info_selected['description']})") + + # 调用安全导航函数(先机械臂回零再移动) + result = controller.safe_navigate_to_station(selected_station) + + if result is not None: + print(f"导航指令发送成功") + print(f"响应信息: {result}") + print(f"当前工站已设置为: {selected_station}") + # 更新显示的站点信息 + current_station_display = f"{selected_station} - {station_info_selected['name']} ({station_info_selected['description']})" + else: + print(f"导航失败, 请查看日志获取详细信息") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "17": + # 查询当前站点 + print("\n--- 查询当前站点 ---") + try: + station_info = controller.query_current_station() + if station_info is not None: + print(f"站点ID: {station_info['station_id']}") + print(f"站点名称: {station_info['station_name']}") + print(f"站点描述: {station_info['description']}") + # 更新显示的站点信息 + current_station_display = f"{station_info['station_id']} - {station_info['station_name']} ({station_info['description']})" + else: + print("查询站点失败") + except Exception as e: + print(f"错误: {e}") + + elif choice == "18": + # 显示当前站点和AGV上的位置 + print("\n--- 显示当前站点和AGV上的位置 ---") + + # 从配置文件读取可用的托盘位置 + try: + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 过滤托盘位置: 只显示当前站点的和AGV本身的 + filtered_tray_list = [] + if controller.current_station is not None and controller.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[controller.current_station]["name"] + for tray_name in tray_positions.keys(): + # 显示以当前站点名称开头的托盘或以agv开头的托盘 + if tray_name.startswith(station_name) or tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + else: + # 如果没有当前站点信息, 只显示AGV本身的托盘 + for tray_name in tray_positions.keys(): + if tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + + if not filtered_tray_list: + print("当前站点没有可用的托盘位置") + continue + + # 显示可用的托盘位置 + print("\n当前站点和AGV上的托盘位置:") + for idx, tray_name in enumerate(filtered_tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + tray_list = filtered_tray_list + + # 用户输入选择 + choice_input = input(f"请输入托盘编号 (1-{len(tray_list)}): ").strip() + + # 验证输入 + if not choice_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + tray_index = int(choice_input) - 1 + if tray_index < 0 or tray_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + continue + + # 获取选中的托盘名称 + selected_tray = tray_list[tray_index] + print(f"\n开始执行运动到抓取点位流程: {selected_tray}") + result = controller.move_to_grasp_position(selected_tray, block=True) + print(f"运动到抓取点位结果: {'成功' if result else '失败'}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "19": + # 物料适配转移 + print("\n--- 物料适配转移 ---") + try: + # 显示可用的物料类型 + materials = controller.position_manager.list_materials() + if not materials: + print("错误: 未找到物料类型配置") + continue + + print("可用的物料类型:") + for idx, material_name in enumerate(materials, 1): + material = controller.position_manager.get_material(material_name) + print(f" {idx}. {material_name} - {material.description} (夹爪: {material.gripper})") + + # 选择物料类型 + material_choice = input(f"请选择物料类型 (1-{len(materials)}, 直接回车跳过): ").strip() + selected_material = None + if material_choice != "": + if not material_choice.isdigit(): + print("错误: 请输入有效的数字") + continue + material_index = int(material_choice) - 1 + if material_index < 0 or material_index >= len(materials): + print(f"错误: 请输入1到{len(materials)}之间的数字") + continue + selected_material = materials[material_index] + + # 显示可用的托盘位置 + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 过滤托盘位置: 只显示当前站点的和AGV本身的 + filtered_tray_list = [] + if controller.current_station is not None and controller.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[controller.current_station]["name"] + for tray_name in tray_positions.keys(): + # 显示以当前站点名称开头的托盘或以agv开头的托盘 + if tray_name.startswith(station_name) or tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + else: + # 如果没有当前站点信息, 只显示AGV本身的托盘 + for tray_name in tray_positions.keys(): + if tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + + if not filtered_tray_list: + print("错误: 当前站点没有可用的托盘位置") + continue + + print("\n可用的托盘位置:") + tray_list = filtered_tray_list + for idx, tray_name in enumerate(tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + # 选择源托盘 + source_choice = input(f"请选择源托盘 (1-{len(tray_list)}): ").strip() + if not source_choice.isdigit(): + print("错误: 请输入有效的数字") + continue + source_index = int(source_choice) - 1 + if source_index < 0 or source_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + continue + source_tray = tray_list[source_index] + + # 选择目标托盘 + target_choice = input(f"请选择目标托盘 (1-{len(tray_list)}): ").strip() + if not target_choice.isdigit(): + print("错误: 请输入有效的数字") + continue + target_index = int(target_choice) - 1 + if target_index < 0 or target_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + continue + target_tray = tray_list[target_index] + + # 执行物料转移 + print(f"\n开始物料转移: {source_tray} -> {target_tray}") + if selected_material is not None: + print(f"物料类型: {selected_material}") + result = controller.transfer_material(source_tray, target_tray, selected_material, block=True) + print(f"物料转移结果: {'成功' if result else '失败'}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "20": + # 更换夹爪 + print("\n--- 更换夹爪 ---") + try: + # 显示当前夹爪 + current_gripper = controller.get_current_gripper() + print(f"当前夹爪: {current_gripper if current_gripper else '无'}") + + # 显示可用的夹爪 + grippers = controller.position_manager.list_grippers() + if not grippers: + print("错误: 未找到夹爪配置") + continue + + print("\n可用的夹爪:") + for idx, gripper_name in enumerate(grippers, 1): + gripper = controller.position_manager.get_gripper(gripper_name) + print(f" {idx}. {gripper_name} - {gripper.description} (料位: {gripper.slot})") + + # 选择目标夹爪 + gripper_choice = input(f"请选择目标夹爪 (1-{len(grippers)}): ").strip() + if not gripper_choice.isdigit(): + print("错误: 请输入有效的数字") + continue + gripper_index = int(gripper_choice) - 1 + if gripper_index < 0 or gripper_index >= len(grippers): + print(f"错误: 请输入1到{len(grippers)}之间的数字") + continue + target_gripper = grippers[gripper_index] + + # 确认操作 + print(f"\n将更换夹爪: {current_gripper if current_gripper else '无'} -> {target_gripper}") + confirm = input("确认执行? (y/n): ").strip().lower() + if confirm != 'y': + print("已取消") + continue + + # 执行换爪 + result = controller.change_gripper(target_gripper, block=True) + print(f"更换夹爪结果: {'成功' if result else '失败'}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "21": + # 查看物料/夹爪配置 + print("\n--- 物料/夹爪配置 ---") + try: + # 显示夹爪配置 + grippers = controller.position_manager.list_grippers() + print("\n夹爪配置:") + if grippers: + for gripper_name in grippers: + gripper = controller.position_manager.get_gripper(gripper_name) + print(f" - {gripper_name}: {gripper.description} (料位: {gripper.slot})") + else: + print(" 无夹爪配置") + + # 显示物料类型配置 + materials = controller.position_manager.list_materials() + print("\n物料类型配置:") + if materials: + for material_name in materials: + material = controller.position_manager.get_material(material_name) + print(f" - {material_name}: {material.description}") + print(f" 夹爪: {material.gripper}") + print(f" 下探偏移: {material.descend_z_offset}mm, 提升偏移: {material.lift_z_offset}mm") + else: + print(" 无物料类型配置") + + # 显示当前夹爪状态 + current_gripper = controller.get_current_gripper() + print(f"\n当前安装的夹爪: {current_gripper if current_gripper else '无'}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "22": + # 批量物料转运 + print("\n--- 批量物料转运(含AGV移动和校准) ---") + print("说明: 支持一次转运最多4个物料, AGV会自动移动到各站点并进行点位校准") + + try: + # 输入任务数量 + task_count_input = input("请输入要转运的物料数量 (1-4): ").strip() + if not task_count_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + task_count = int(task_count_input) + if task_count < 1 or task_count > 4: + print("错误: 任务数量必须在1-4之间") + continue + + # 获取所有可用的托盘位置 + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 获取所有可用的物料类型 + materials = controller.position_manager.list_materials() + + # 构建转运任务列表 + transfer_tasks = [] + + for i in range(task_count): + print(f"\n{'=' * 60}") + print(f"配置任务 {i+1}/{task_count}") + print(f"{'=' * 60}") + + # 显示所有可用的托盘位置 + print("\n可用的托盘位置:") + tray_list = list(tray_positions.keys()) + for idx, tray_name in enumerate(tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + # 选择源托盘 + source_choice = input(f"\n请选择源托盘位置 (1-{len(tray_list)}): ").strip() + if not source_choice.isdigit(): + print("错误: 请输入有效的数字") + break + source_index = int(source_choice) - 1 + if source_index < 0 or source_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + break + source_tray = tray_list[source_index] + + # 选择目标托盘 + target_choice = input(f"请选择目标托盘位置 (1-{len(tray_list)}): ").strip() + if not target_choice.isdigit(): + print("错误: 请输入有效的数字") + break + target_index = int(target_choice) - 1 + if target_index < 0 or target_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + break + target_tray = tray_list[target_index] + + # 选择物料类型(可选) + selected_material = None + if materials: + print("\n可用的物料类型:") + for idx, material_name in enumerate(materials, 1): + material = controller.position_manager.get_material(material_name) + print(f" {idx}. {material_name} - {material.description} (夹爪: {material.gripper})") + + material_choice = input(f"请选择物料类型 (1-{len(materials)}, 直接回车跳过): ").strip() + if material_choice != "": + if not material_choice.isdigit(): + print("错误: 请输入有效的数字") + break + material_index = int(material_choice) - 1 + if material_index < 0 or material_index >= len(materials): + print(f"错误: 请输入1到{len(materials)}之间的数字") + break + selected_material = materials[material_index] + + # 添加任务到列表 + task = { + "source_tray": source_tray, + "target_tray": target_tray, + "material_type": selected_material + } + transfer_tasks.append(task) + + print(f"\n任务{i+1}已配置: {source_tray} -> {target_tray}") + if selected_material: + print(f" 物料类型: {selected_material}") + + # 检查是否成功配置了所有任务 + if len(transfer_tasks) != task_count: + print("\n任务配置未完成, 已取消") + continue + + # 显示任务摘要 + print(f"\n{'=' * 60}") + print("任务摘要:") + print(f"{'=' * 60}") + for idx, task in enumerate(transfer_tasks, 1): + print(f"任务{idx}: {task['source_tray']} -> {task['target_tray']}") + if task['material_type']: + print(f" 物料类型: {task['material_type']}") + + # 确认执行 + print(f"\n警告: 此操作将控制AGV移动并执行物料转运, 请确保周围安全!") + confirm = input("确认执行批量转运? (y/n): ").strip().lower() + + if confirm != 'y': + print("已取消") + continue + + # 执行批量转运 + print(f"\n开始执行批量物料转运...") + result = controller.batch_transfer_materials(transfer_tasks, block=True) + + if result: + print(f"\n{'=' * 60}") + print("批量物料转运成功!") + print(f"{'=' * 60}") + else: + print(f"\n批量物料转运失败, 请查看日志获取详细信息") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "23": + # 托盘点位校准 + print("\n--- 托盘点位校准 ---") + print("说明: 选择点位后运动到抓取点位, 手动矫正后确认, 保存当前TCP位姿到配置文件") + print(" 对于AGV上的点位, 直接保存当前TCP位姿") + print(" 对于非AGV点位, 会减去站点校准偏移量后存储原始TCP位姿") + + try: + # 从配置文件读取可用的托盘位置 + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 过滤托盘位置: 只显示当前站点的和AGV本身的 + filtered_tray_list = [] + if controller.current_station is not None and controller.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[controller.current_station]["name"] + for tray_name in tray_positions.keys(): + # 显示以当前站点名称开头的托盘或以agv开头的托盘 + if tray_name.startswith(station_name) or tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + else: + # 如果没有当前站点信息, 只显示AGV本身的托盘 + for tray_name in tray_positions.keys(): + if tray_name.startswith('agv'): + filtered_tray_list.append(tray_name) + + if not filtered_tray_list: + print("错误: 当前站点没有可用的托盘位置") + continue + + # 显示可用的托盘位置 + print("\n可用的托盘位置:") + for idx, tray_name in enumerate(filtered_tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + tray_list = filtered_tray_list + + # 用户输入选择 + choice_input = input(f"请输入托盘编号 (1-{len(tray_list)}): ").strip() + + # 验证输入 + if not choice_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + tray_index = int(choice_input) - 1 + if tray_index < 0 or tray_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + continue + + # 获取选中的托盘名称 + selected_tray = tray_list[tray_index] + + print(f"\n开始执行托盘点位校准: {selected_tray}") + print("警告: 机械臂将运动到抓取点位, 请确保周围安全!") + confirm = input("确认执行? (y/n): ").strip().lower() + + if confirm != 'y': + print("已取消") + continue + + # 步骤1: 运动到抓取点位 + print("\n步骤1: 运动到抓取点位...") + result = controller.move_to_grasp_position(selected_tray, block=True) + if not result: + print("运动到抓取点位失败") + continue + + print("已到达抓取点位") + + # 步骤2: 等待用户手动矫正 + print("\n步骤2: 请手动矫正机械臂位置") + print("提示: 可以使用示教器或其他方式调整机械臂位置") + print(" 矫正完成后, 按回车键继续...") + input() + + # 步骤3: 获取当前TCP位姿并计算要保存的位姿 + print("\n步骤3: 获取当前TCP位姿...") + current_pose = controller.arm.get_tcp_pose() + print(f"当前TCP位姿: {current_pose}") + + # 判断是否为AGV上的点位 + is_agv_position = selected_tray.startswith('agv') + + if is_agv_position: + # AGV上的点位, 直接保存当前TCP位姿 + pose_to_save = current_pose + print(f"\nAGV点位, 将直接保存当前TCP位姿") + else: + # 非AGV点位, 需要减去站点校准偏移量 + matched_station = None + import yaml + with open(controller.position_manager.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is not None and 'station_calibration' in config: + for station_name_key in config['station_calibration'].keys(): + if selected_tray.startswith(station_name_key): + matched_station = station_name_key + break + + if matched_station is not None: + station_offset = controller.position_manager.get_calibration_offset(matched_station) + if station_offset is not None: + # 原始TCP位姿 = 当前TCP位姿 - 偏移量 + pose_to_save = [ + current_pose[0] - station_offset['x'], + current_pose[1] - station_offset['y'], + current_pose[2] - station_offset['z'], + current_pose[3] - station_offset['dx'], + current_pose[4] - station_offset['dy'], + current_pose[5] - station_offset['dz'] + ] + print(f"\n非AGV点位, 匹配站点: {matched_station}") + print(f"站点偏移量: x={station_offset['x']:.3f}, y={station_offset['y']:.3f}, z={station_offset['z']:.3f}") + print(f"原始TCP位姿(减去偏移量后): {pose_to_save}") + else: + pose_to_save = current_pose + print(f"\n警告: 站点 {matched_station} 未校准, 将直接保存当前TCP位姿") + else: + pose_to_save = current_pose + print(f"\n警告: 托盘 {selected_tray} 未匹配到任何站点, 将直接保存当前TCP位姿") + + # 步骤4: 确认保存 + # 获取原先存储的TCP位姿 + original_tray_position = controller.position_manager.get_position('tray_position', selected_tray) + if original_tray_position is not None and original_tray_position.pose is not None: + print(f"\n原先存储的TCP位姿: {original_tray_position.pose}") + else: + print(f"\n原先存储的TCP位姿: 无") + print(f"将要保存的TCP位姿: {pose_to_save}") + save_confirm = input("确认保存到配置文件? (y/n): ").strip().lower() + + if save_confirm != 'y': + print("已取消保存") + continue + + # 保存到配置文件 + controller.position_manager.save_tray_position(selected_tray, pose_to_save) + print(f"\n托盘位置 {selected_tray} 已成功保存到配置文件!") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "24": + # 全点位测试 + print("\n--- 全点位测试 ---") + print("说明: 从agv_tray_1取托盘依次放到当前站点的每个点位再取回, 测试所有点位的准确性") + print("流程: 1.提示用户在agv_tray_1放置托盘 -> 2.选择托盘种类 -> 3.点位校准 -> 4.逐点位测试") + print(f"当前站点: {current_station_display}") + + try: + # 检查当前站点 + if controller.current_station is None: + print("\n警告: 未设置当前站点, 请先查询或移动到目标站点") + query_confirm = input("是否先查询当前站点? (y/n): ").strip().lower() + if query_confirm == 'y': + station_info = controller.query_current_station() + if station_info is not None: + current_station_display = f"{station_info['station_id']} - {station_info['station_name']} ({station_info['description']})" + print(f"当前站点: {current_station_display}") + else: + print("查询站点失败, 无法继续") + continue + else: + print("已取消") + continue + + # 显示可用的物料类型 + materials = controller.position_manager.list_materials() + if not materials: + print("错误: 未找到物料类型配置") + continue + + print("\n请选择托盘种类:") + for idx, material_name in enumerate(materials, 1): + material = controller.position_manager.get_material(material_name) + print(f" {idx}. {material_name} - {material.description} (夹爪: {material.gripper})") + + material_choice = input(f"请选择托盘种类 (1-{len(materials)}): ").strip() + if not material_choice.isdigit(): + print("错误: 请输入有效的数字") + continue + + material_index = int(material_choice) - 1 + if material_index < 0 or material_index >= len(materials): + print(f"错误: 请输入1到{len(materials)}之间的数字") + continue + + selected_material = materials[material_index] + material_config = controller.position_manager.get_material(selected_material) + print(f"\n已选择托盘种类: {selected_material} - {material_config.description}") + + # 显示待测试的点位 + tray_positions = controller.position_manager.get_category('tray_position') + test_positions = [] + if controller.current_station is not None and controller.current_station in STATION_POSITIONS: + station_name = STATION_POSITIONS[controller.current_station]["name"] + for tray_name in tray_positions.keys(): + if tray_name.startswith(station_name): + test_positions.append(tray_name) + + if len(test_positions) == 0: + print("错误: 当前站点没有可测试的点位") + continue + + print(f"\n待测试点位 ({len(test_positions)} 个):") + for idx, pos_name in enumerate(test_positions, 1): + tray_config = tray_positions[pos_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {pos_name} - {description}") + + # 提示用户放置托盘 + print("\n" + "=" * 60) + print("请在 agv_tray_1 位置放置托盘!") + print("=" * 60) + print(f"托盘种类: {selected_material} - {material_config.description}") + print("\n警告: 此操作将控制机械臂进行全点位测试, 请确保周围安全!") + confirm = input("托盘已放置好, 确认开始测试? (y/n): ").strip().lower() + + if confirm != 'y': + print("已取消") + continue + + # 执行全点位测试 + print("\n开始执行全点位测试...") + results = controller.test_all_positions(selected_material, block=True) + + # 显示测试结果 + print("\n" + "=" * 60) + print("全点位测试结果汇总") + print("=" * 60) + print(f"成功: {len(results['success'])} 个点位") + for pos in results["success"]: + print(f" [OK] {pos}") + print(f"失败: {len(results['failed'])} 个点位") + for pos in results["failed"]: + print(f" [FAIL] {pos}") + print(f"跳过: {len(results['skipped'])} 个点位") + for pos in results["skipped"]: + print(f" [SKIP] {pos}") + + total = len(results['success']) + len(results['failed']) + len(results['skipped']) + if total > 0: + success_rate = len(results['success']) / total * 100 + print(f"\n测试通过率: {success_rate:.1f}%") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "25": + # 工站整体偏差矫正 + print("\n--- 工站整体偏差矫正 ---") + print("说明: 通过选择参考点位计算偏移量并应用到工站所有点位") + print("流程:") + print(" 1. 选择要校准的工站(agv或当前所在工站)") + print(" 2. 如果不是agv, 可选择先进行视觉补偿") + print(" 3. 选择一个参考点位") + print(" 4. 可选择运动到该点位") + print(" 5. 手动微调机械臂位置后按回车确认") + print(" 6. 计算偏移量并可选择应用到工站所有点位") + print(f"当前站点: {current_station_display}") + + try: + print("\n警告: 此操作可能会修改配置文件中的点位数据!") + confirm = input("确认开始工站偏差矫正? (y/n): ").strip().lower() + + if confirm != 'y': + print("已取消") + continue + + result = controller.calibrate_station_offset(block=True) + + if result is not None: + print(f"\n工站偏差矫正完成!") + print(f"计算得到的偏移量: dx={result['x']:.3f}, dy={result['y']:.3f}, dz={result['z']:.3f}") + else: + print("工站偏差矫正失败或已取消") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "26": + # 查询电池电量 + print("\n--- 查询电池电量 ---") + try: + battery_info = controller.query_battery_status(simple=True) + + if battery_info is not None: + battery_level = battery_info.get("battery_level") + print(f"电池电量: {battery_level * 100:.1f}%") + + if battery_level < 0.3: + print("警告: 电池电量过低, 建议立即充电!") + elif battery_level < 0.5: + print("提示: 电池电量较低, 建议充电") + else: + print("电池电量充足") + else: + print("查询电池电量失败") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "27": + # 自动充电检查(单次) + print("\n--- 自动充电检查 ---") + print("说明: 执行一次充电检查") + print(" - 如果不在CP6, 则跳过本次检查") + print(" - 如果在CP6, 先查询电池电量") + print(" - 如果电量低于50%, 先确认是否已在充电") + print(" - 仅在未充电时执行充电循环(CP6->PP5->CP6)") + + try: + result = controller.auto_charge_check() + + print(f"\n充电检查结果:") + print(f" 状态: {result.get('status')}") + print(f" 动作: {result.get('action')}") + print(f" 消息: {result.get('message')}") + + if "battery_level" in result: + battery_level = result.get('battery_level') + print(f" 电池电量: {battery_level * 100:.1f}%") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "28": + # 启动自动充电循环 + print("\n--- 启动自动充电循环 ---") + print("说明: 启动自动充电循环, 每隔指定时间执行一次充电检查") + print("提示: 按Ctrl+C可以中断循环") + + try: + interval_input = input("请输入检查间隔时间(小时, 默认1): ").strip() + + if interval_input == "": + interval_hours = 1 + else: + interval_hours = float(interval_input) + + if interval_hours <= 0: + print("错误: 间隔时间必须大于0") + continue + + print(f"\n启动自动充电循环, 检查间隔: {interval_hours}小时") + print("按Ctrl+C可以中断循环\n") + + controller.auto_charge_loop(interval_hours=interval_hours) + + except KeyboardInterrupt: + print("\n用户中断自动充电循环") + except ValueError: + print("错误: 请输入有效的数字") + except Exception as e: + print(f"错误: {e}") + + elif choice == "29": + # 批量物料转运循环测试 + print("\n--- 批量物料转运循环测试 ---") + print("说明: 执行正向转运->充电站->反向转运->充电站的循环测试") + print(" 支持一次转运最多4个物料, AGV会自动移动到各站点并进行点位校准") + + try: + # 输入循环次数 + cycle_input = input("请输入循环次数 (默认1): ").strip() + if cycle_input == "": + cycle_count = 1 + else: + if not cycle_input.isdigit(): + print("错误: 请输入有效的数字") + continue + cycle_count = int(cycle_input) + if cycle_count < 1: + print("错误: 循环次数必须大于0") + continue + + # 输入任务数量 + task_count_input = input("请输入要转运的物料数量 (1-4): ").strip() + if not task_count_input.isdigit(): + print("错误: 请输入有效的数字") + continue + + task_count = int(task_count_input) + if task_count < 1 or task_count > 4: + print("错误: 任务数量必须在1-4之间") + continue + + # 获取所有可用的托盘位置 + tray_positions = controller.position_manager.get_category('tray_position') + if not tray_positions: + print("错误: 未找到托盘位置配置") + continue + + # 获取所有可用的物料类型 + materials = controller.position_manager.list_materials() + + # 构建转运任务列表 + transfer_tasks = [] + + for i in range(task_count): + print(f"\n{'=' * 60}") + print(f"配置任务 {i+1}/{task_count}") + print(f"{'=' * 60}") + + # 显示所有可用的托盘位置 + print("\n可用的托盘位置:") + tray_list = list(tray_positions.keys()) + for idx, tray_name in enumerate(tray_list, 1): + tray_config = tray_positions[tray_name] + description = tray_config.get('description', '无描述') + print(f" {idx}. {tray_name} - {description}") + + # 选择源托盘 + source_choice = input(f"\n请选择源托盘位置 (1-{len(tray_list)}): ").strip() + if not source_choice.isdigit(): + print("错误: 请输入有效的数字") + break + source_index = int(source_choice) - 1 + if source_index < 0 or source_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + break + source_tray = tray_list[source_index] + + # 选择目标托盘 + target_choice = input(f"请选择目标托盘位置 (1-{len(tray_list)}): ").strip() + if not target_choice.isdigit(): + print("错误: 请输入有效的数字") + break + target_index = int(target_choice) - 1 + if target_index < 0 or target_index >= len(tray_list): + print(f"错误: 请输入1到{len(tray_list)}之间的数字") + break + target_tray = tray_list[target_index] + + # 选择物料类型(可选) + selected_material = None + if materials: + print("\n可用的物料类型:") + for idx, material_name in enumerate(materials, 1): + material = controller.position_manager.get_material(material_name) + print(f" {idx}. {material_name} - {material.description} (夹爪: {material.gripper})") + + material_choice = input(f"请选择物料类型 (1-{len(materials)}, 直接回车跳过): ").strip() + if material_choice != "": + if not material_choice.isdigit(): + print("错误: 请输入有效的数字") + break + material_index = int(material_choice) - 1 + if material_index < 0 or material_index >= len(materials): + print(f"错误: 请输入1到{len(materials)}之间的数字") + break + selected_material = materials[material_index] + + # 添加任务到列表 + task = { + "source_tray": source_tray, + "target_tray": target_tray, + "material_type": selected_material + } + transfer_tasks.append(task) + + print(f"\n任务{i+1}已配置: {source_tray} <-> {target_tray}") + if selected_material: + print(f" 物料类型: {selected_material}") + + # 检查是否成功配置了所有任务 + if len(transfer_tasks) != task_count: + print("\n任务配置未完成, 已取消") + continue + + # 显示任务摘要 + print(f"\n{'=' * 60}") + print("循环测试摘要:") + print(f"{'=' * 60}") + print(f"循环次数: {cycle_count}") + print(f"转运任务:") + for idx, task in enumerate(transfer_tasks, 1): + print(f" 任务{idx}: {task['source_tray']} <-> {task['target_tray']}") + if task['material_type']: + print(f" 物料类型: {task['material_type']}") + + # 确认执行 + print(f"\n警告: 此操作将控制AGV移动并执行{cycle_count}轮循环测试, 请确保周围安全!") + confirm = input("确认执行循环测试? (y/n): ").strip().lower() + + if confirm != 'y': + print("已取消") + continue + + # 执行循环测试 + print(f"\n开始执行批量物料转运循环测试...") + result = controller.batch_transfer_cycle_test(transfer_tasks, cycle_count=cycle_count, block=True) + + # 显示测试结果 + print(f"\n{'=' * 60}") + print("循环测试结果:") + print(f"{'=' * 60}") + print(f"测试状态: {'成功' if result['success'] else '失败'}") + print(f"完成循环: {result['completed_cycles']}/{result['total_cycles']}") + if result['failed_at']: + print(f"失败阶段: {result['failed_at']}") + print(f"{'=' * 60}") + + if result['success']: + print(f"\n批量物料转运循环测试全部完成!") + else: + print(f"\n批量物料转运循环测试失败, 请查看日志获取详细信息") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "30": + # 分析站→货架样品转运 + print("\n--- 分析站→货架样品转运 ---") + print("说明: 轮询智达进样设备状态, 等待空闲后将样品从分析站转运到货架空位") + + try: + # 显示当前货架状态 + controller.shelf_manager.print_status() + + # 询问源托盘 + print("\n默认源托盘: analysis_station_tray_1-2") + source_input = input( + "请输入源托盘(多个用逗号分隔, 直接回车使用默认): " + ).strip() + + if source_input == "": + source_trays = ["analysis_station_tray_1-2"] + else: + source_trays = [s.strip() for s in source_input.split(",") if s.strip() != ""] + + print(f"源托盘: {source_trays}") + + # 询问轮询间隔 + interval_input = input("请输入轮询间隔秒数 (默认30): ").strip() + try: + poll_interval = float(interval_input) if interval_input != "" else 30.0 + except ValueError: + print("无效数值, 使用默认30秒") + poll_interval = 30.0 + + # 确认执行 + print(f"\n将执行以下操作:") + print(f" 源托盘: {source_trays}") + print(f" 轮询间隔: {poll_interval} 秒") + confirm = input("确认执行? (y/n): ").strip().lower() + + if confirm != "y": + print("已取消") + continue + + # 执行转运 + print("\n开始执行分析站→货架样品转运...") + result = controller.transfer_analysis_to_shelf( + source_trays=source_trays, + poll_interval=poll_interval, + ) + + if result: + print("\n分析站→货架样品转运成功!") + controller.shelf_manager.print_status() + else: + print("\n分析站→货架样品转运失败, 请查看日志获取详细信息") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "31": + # 查看/管理货架状态 + print("\n--- 查看/管理货架状态 ---") + + while True: + print("\n 1. 查看当前状态") + print(" 2. 手动清除槽位") + print(" 3. 重置全部槽位") + print(" 0. 返回上级菜单") + + sub_choice = input("请选择操作: ").strip() + + if sub_choice == "0": + break + elif sub_choice == "1": + controller.shelf_manager.print_status() + elif sub_choice == "2": + # 列出有物料的槽位 + controller.shelf_manager.print_status() + status = controller.shelf_manager.get_all_status() + occupied = [ + name for name in status["slots"] + if status["slots"][name] is not None + ] + + if len(occupied) == 0: + print("所有槽位均为空, 无需清除") + continue + + print("\n有物料的槽位:") + for idx, name in enumerate(occupied, 1): + info = status["slots"][name] + print(f" {idx}. {name} - {info.get('material_type', '未知')}") + + slot_input = input( + f"请选择要清除的槽位 (1-{len(occupied)}): " + ).strip() + + if not slot_input.isdigit(): + print("无效输入") + continue + + slot_idx = int(slot_input) - 1 + if slot_idx < 0 or slot_idx >= len(occupied): + print(f"请输入1到{len(occupied)}之间的数字") + continue + + slot_name = occupied[slot_idx] + confirm = input(f"确认清除槽位 {slot_name}? (y/n): ").strip().lower() + if confirm == "y": + result = controller.shelf_manager.remove_material(slot_name) + print(f"清除结果: {'成功' if result else '失败'}") + else: + print("已取消") + + elif sub_choice == "3": + confirm = input("确认重置全部槽位? 此操作不可恢复 (y/n): ").strip().lower() + if confirm == "y": + controller.shelf_manager.reset_all() + print("已重置全部槽位") + else: + print("已取消") + else: + print("无效选择") + + elif choice == "32": + # PP5/CP6自动充电检查 + print("\n--- PP5/CP6自动充电检查 ---") + print("说明: 执行一次PP5/CP6待命充电检查") + print(" - 如果在PP5且电量低于50%, 则进入CP6充电") + print(" - 如果在CP6且电量高于90%, 则返回PP5待命") + print(" - 如果在PP5且电量不低于50%, 则继续在PP5待命") + print(" - 如果在CP6且电量不高于90%, 则继续在CP6待命") + print(" - 如果不在PP5或CP6, 则视为工作途中并跳过本次检查") + + try: + result = controller.auto_charge_pp5_cp6_check() + + print("\n充电检查结果:") + print(f" 状态: {result.get('status')}") + print(f" 动作: {result.get('action')}") + print(f" 消息: {result.get('message')}") + + if "current_station" in result: + print(f" 当前站点: {result.get('current_station')}") + if "battery_level" in result: + battery_level = result.get("battery_level") + print(f" 电池电量: {battery_level * 100:.1f}%") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "33": + # 启动PP5/CP6自动充电循环 + print("\n--- 启动PP5/CP6自动充电循环 ---") + print("说明: 启动PP5/CP6待命充电循环监控") + print("提示: 按Ctrl+C可以中断循环") + + try: + interval_input = input("请输入检查间隔时间(小时, 默认1): ").strip() + retry_input = input("请输入重试间隔时间(分钟, 默认5): ").strip() + + if interval_input == "": + interval_hours = 1 + else: + interval_hours = float(interval_input) + + if retry_input == "": + retry_wait_minutes = 5 + else: + retry_wait_minutes = float(retry_input) + + if interval_hours <= 0: + print("错误: 检查间隔时间必须大于0") + continue + if retry_wait_minutes <= 0: + print("错误: 重试间隔时间必须大于0") + continue + + print( + f"\n启动PP5/CP6自动充电循环, 检查间隔: {interval_hours}小时, " + f"重试间隔: {retry_wait_minutes}分钟" + ) + print("按Ctrl+C可以中断循环\n") + + controller.auto_charge_pp5_cp6_loop( + interval_hours=interval_hours, + retry_wait_minutes=retry_wait_minutes + ) + + except KeyboardInterrupt: + print("\n用户中断PP5/CP6自动充电循环") + except ValueError: + print("错误: 请输入有效的数字") + except Exception as e: + print(f"错误: {e}") + + elif choice == "0": + # 退出程序 + print("\n正在退出...") + if controller.arm.is_connected: + controller.disconnect() + break + + else: + print("无效的选项, 请重新输入") + + print("\n程序已退出") + + +class UniLabAGVController(AGVController): + """ + UniLabOS 注册表适配层。 + + 说明: + - 兼容设备图中传入的 arm_ip/arm_port/arm_timeout/deck/protocol_type 配置。 + - 对外保留 send_nav_task/status 接口,便于复用现有 registry schema。 + """ + + def __init__( + self, + arm_ip=None, + arm_port=None, + arm_timeout=None, + deck=None, + protocol_type=None, + **kwargs, + ): + super().__init__(ip=arm_ip, port=arm_port, timeout=arm_timeout) + self.deck = deck + self.protocol_type = protocol_type or [] + self._extra_config = kwargs + + # ---------- agv_tray_N → WareHouse ResourceHolder 名称映射 ---------- + # agv_tray_1 → AGV-1, agv_tray_2 → AGV-2 ...(与 eit_warehouse_AGV 的 name_by_layout_code 命名一致) + _TRAY_NAME_TO_SLOT_NAME = { + f"agv_tray_{i}": f"AGV-{i}" for i in range(1, 5) + } + + def post_init(self, ros_node) -> None: + """节点初始化完成后,构建 AGV Deck 实例并首次上报。""" + self._ros_node = ros_node + try: + deck = self.deck + if isinstance(deck, dict): + deck_ref = deck.get("data", deck) + child_name = deck_ref.get("_resource_child_name") + if child_name: + try: + deck = ros_node.resource_tracker.figure_resource({"name": child_name}) + except Exception: + deck = None + if deck is None and isinstance(deck_ref, dict) and deck_ref.get("_resource_type"): + deck_cls = get_class(deck_ref["_resource_type"]) + deck = deck_cls(name=child_name or "EIT_AGV_Deck", setup=True) + self.deck = deck + if deck is None or isinstance(deck, dict): + return + except Exception as exc: + logger.error(f"AGV Deck 构建失败: {exc}") + return + + # 首次上报 Deck(扁平化,空 ResourceHolder 不显示) + # 物料状态由 pick/put 操作实时追踪更新,不需要定时同步 shelf + try: + self._upload_agv_deck_flattened() + logger.info("AGV 车载 Deck 首次上报完成(扁平化,空槽位不显示)") + except Exception as exc: + logger.error(f"AGV Deck 首次上报失败: {exc}") + + # ---------- AGV 车载 Deck 物料运行时追踪 ---------- + # 物料类型名称 → 载架工厂函数(与手套箱 TB 区同源) + _AGV_CARRIER_FACTORY = None + + @classmethod + def _get_carrier_factory(cls): + """延迟加载 CARRIER_FACTORY,返回 {枚举名字符串: factory_func} 映射。""" + if cls._AGV_CARRIER_FACTORY is not None: + return cls._AGV_CARRIER_FACTORY + try: + from unilabos.resources.eit_synthesis_station import bottle_carriers + cls._AGV_CARRIER_FACTORY = { + "REAGENT_BOTTLE_TRAY_2ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_2ML, + "REAGENT_BOTTLE_TRAY_8ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_8ML, + "REAGENT_BOTTLE_TRAY_40ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_40ML, + "REAGENT_BOTTLE_TRAY_125ML": bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_125ML, + "POWDER_BUCKET_TRAY_30ML": bottle_carriers.EIT_POWDER_BUCKET_TRAY_30ML, + "TIP_TRAY_1ML": bottle_carriers.EIT_TIP_TRAY_1ML, + "TIP_TRAY_5ML": bottle_carriers.EIT_TIP_TRAY_5ML, + "TIP_TRAY_50UL": bottle_carriers.EIT_TIP_TRAY_50UL, + "REACTION_TUBE_TRAY_2ML": bottle_carriers.EIT_REACTION_TUBE_TRAY_2ML, + "TEST_TUBE_MAGNET_TRAY_2ML": bottle_carriers.EIT_TEST_TUBE_MAGNET_TRAY_2ML, + "REACTION_SEAL_CAP_TRAY": bottle_carriers.EIT_REACTION_SEAL_CAP_TRAY, + "FLASH_FILTER_INNER_BOTTLE_TRAY": bottle_carriers.EIT_FLASH_FILTER_INNER_BOTTLE_TRAY, + "FLASH_FILTER_OUTER_BOTTLE_TRAY": bottle_carriers.EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY, + } + except Exception as exc: + logger.warning(f"加载 CARRIER_FACTORY 失败,将退化为 Container: {exc}") + cls._AGV_CARRIER_FACTORY = {} + return cls._AGV_CARRIER_FACTORY + + def _upload_agv_deck_flattened(self) -> None: + """ + 使用与 Synthesis Station 相同的资源树扁平化逻辑上报 AGV Deck。 + + 扁平化效果: + - 有 Carrier 子节点的 ResourceHolder → Carrier 直接挂到 WareHouse 下(前端可见) + - 无 Carrier 子节点的 ResourceHolder → 被移除(前端不显示,与 TB 区一致) + """ + deck = getattr(self, "deck", None) + if deck is None or isinstance(deck, dict): + return + if not hasattr(self, "_ros_node"): + return + + try: + if hasattr(deck, "_recursive_assign_uuid"): + deck._recursive_assign_uuid(deck) + + from unilabos.resources import resource_tracker + import json + + tree_set = resource_tracker.ResourceTreeSet.from_plr_resources([deck]) + tree_dump = tree_set.dump() + + # ---- 扁平化:移除空的 resource_holder 节点 ---- + # 与 EITSynthesisResourceSynchronizer._update_resource_flattened 同源逻辑 + flattened_trees = [] + for tree_nodes in tree_dump: + nodes_by_uuid = {node.get("uuid"): node for node in tree_nodes if node.get("uuid")} + children_map = {} + for node in tree_nodes: + node_uuid = node.get("uuid") + if not node_uuid: + continue + parent_uuid = node.get("parent_uuid") + children_map.setdefault(parent_uuid, []).append(node_uuid) + + for node_uuid, node in list(nodes_by_uuid.items()): + if node.get("type") != "resource_holder": + continue + parent_uuid = node.get("parent_uuid") + parent = nodes_by_uuid.get(parent_uuid) + if not parent or parent.get("type") not in {"warehouse", "bottle_carrier"}: + continue + # 将此 ResourceHolder 的子节点重新挂到其父节点下 + slot_label = node.get("name") or node.get("id") + for child_uuid in children_map.get(node_uuid, []): + child = nodes_by_uuid.get(child_uuid) + if not child: + continue + child["parent_uuid"] = parent_uuid + # 坐标叠加 + child_pose = child.get("pose", {}) + offset_pose = node.get("pose", {}) + for key in ("position", "position3d"): + child_pos = child_pose.get(key) + offset_pos = offset_pose.get(key) + if not isinstance(child_pos, dict) or not isinstance(offset_pos, dict): + continue + child_pos["x"] = float(child_pos.get("x", 0.0)) + float(offset_pos.get("x", 0.0)) + child_pos["y"] = float(child_pos.get("y", 0.0)) + float(offset_pos.get("y", 0.0)) + child_pos["z"] = float(child_pos.get("z", 0.0)) + float(offset_pos.get("z", 0.0)) + children_map.setdefault(parent_uuid, []).append(child_uuid) + + # 更新父节点 sites 中的 occupied_by + parent_node = nodes_by_uuid.get(parent_uuid) if parent_uuid else None + reparented_children = [nodes_by_uuid.get(c) for c in children_map.get(node_uuid, []) if nodes_by_uuid.get(c)] + if parent_node and slot_label: + child_name = reparented_children[0].get("name") if reparented_children else None + config = parent_node.get("config") + if isinstance(config, dict): + sites = config.get("sites") + if isinstance(sites, list): + for site in sites: + if site.get("label") == slot_label: + site["occupied_by"] = child_name + break + + # 从父节点的子列表中移除此 ResourceHolder + if parent_uuid in children_map: + children_map[parent_uuid] = [c for c in children_map[parent_uuid] if c != node_uuid] + # 移除此 ResourceHolder 节点 + nodes_by_uuid.pop(node_uuid, None) + + flattened_trees.append(list(nodes_by_uuid.values())) + + # 根节点挂到设备 uuid + for tree_nodes in flattened_trees: + for node in tree_nodes: + if not node.get("parent_uuid"): + node["parent_uuid"] = self._ros_node.uuid + + from unilabos.ros.nodes.base_device_node import SerialCommand + request = SerialCommand.Request() + request.command = json.dumps({"data": {"data": flattened_trees}, "action": "update"}) + + async def _do_upload(): + response = await self._ros_node._resource_clients["c2s_update_resource_tree"].call_async(request) + try: + uuid_maps = json.loads(response.response) + self._ros_node.resource_tracker.loop_update_uuid([deck], uuid_maps) + except Exception as exc: + logger.error(f"AGV Deck 更新 uuid 失败: {exc}") + + ROS2DeviceNode.run_async_func(_do_upload, True) + except Exception as exc: + logger.error(f"AGV Deck 扁平化上报失败: {exc}") + import traceback + logger.debug(traceback.format_exc()) + + def _ensure_agv_warehouse_slot_holders(self, agv_wh) -> None: + """ + 确保 AGV WareHouse 内部存在 ResourceHolder 槽位对象。 + + 背景: + - 某些从序列化数据恢复的 WareHouse 只有 sites/child_locations 元数据 + - 此时 agv_wh.children 可能为空,导致运行时同步找不到 AGV-1~AGV-4 + """ + from pylabrobot.resources import ResourceHolder + import uuid as uuid_mod + + child_locations = getattr(agv_wh, "child_locations", None) + if not isinstance(child_locations, dict) or not child_locations: + return + + child_size = getattr(agv_wh, "child_size", {}) or {} + sites = getattr(agv_wh, "sites", None) + if not isinstance(sites, list): + return + + for idx, slot_name in enumerate(child_locations.keys()): + existing = sites[idx] if idx < len(sites) else None + if isinstance(existing, ResourceHolder): + continue + if existing is not None: + continue + + size = child_size.get(slot_name, {}) + slot_holder = ResourceHolder( + name=slot_name, + size_x=float(size.get("width", 127.8)), + size_y=float(size.get("height", 85.5)), + size_z=float(size.get("depth", 40.0)), + ) + slot_holder.unilabos_uuid = str(uuid_mod.uuid4()) + agv_wh.assign_child_resource(slot_holder, location=child_locations[slot_name], spot=idx) + + def _get_agv_target_slot(self, agv_wh, slot_name: str): + """兼容 setup() 新建与反序列化恢复两种场景,返回目标 ResourceHolder。""" + from pylabrobot.resources import ResourceHolder + + for slot in getattr(agv_wh, "children", []): + if isinstance(slot, ResourceHolder) and getattr(slot, "name", "") == slot_name: + return slot + + self._ensure_agv_warehouse_slot_holders(agv_wh) + + for slot in getattr(agv_wh, "children", []): + if isinstance(slot, ResourceHolder) and getattr(slot, "name", "") == slot_name: + return slot + + sites = getattr(agv_wh, "sites", None) + child_locations = getattr(agv_wh, "child_locations", None) + if isinstance(sites, list) and isinstance(child_locations, dict): + for idx, candidate_name in enumerate(child_locations.keys()): + if candidate_name != slot_name or idx >= len(sites): + continue + slot = sites[idx] + if isinstance(slot, ResourceHolder): + return slot + + return None + + def _update_agv_deck_slot( + self, + tray_name: str, + material_type: str = None, + *, + substance_details: list[dict[str, str]] = None, + tray_display_name: str = None, + upload: bool = True, + ) -> None: + """ + 更新 AGV Deck 上指定槽位的物料状态并上报前端。 + + 设计: + - tray_name: "agv_tray_1" ~ "agv_tray_4" + - material_type: 物料类型名称(如 "FLASH_FILTER_OUTER_BOTTLE_TRAY"),为 None 表示清空 + - substance_details: 物料中包含的试剂详情列表,每条含 well/substance/amount 字段 + - tray_display_name: 托盘中文显示名称(如 "30 mL粉桶托盘"),用于前端显示 + - 通过 _TRAY_NAME_TO_SLOT_NAME 映射到 WareHouse 内的 ResourceHolder 名 + - 上报使用扁平化逻辑(与 Synthesis Station 一致,空 ResourceHolder 不显示) + """ + try: + deck = getattr(self, "deck", None) + if deck is None or isinstance(deck, dict): + return + + from pylabrobot.resources import ResourceHolder, Container + import uuid as uuid_mod + + # 映射 agv_tray_N → AGV-N + slot_name = self._TRAY_NAME_TO_SLOT_NAME.get(tray_name) + if not slot_name: + logger.warning(f"未知的 AGV tray 名称: {tray_name}(仅支持 agv_tray_1~4)") + return + + # 在 WareHouse("AGV") 下查找对应 ResourceHolder + agv_wh = deck.warehouses.get("AGV") if hasattr(deck, "warehouses") else None + if agv_wh is None: + # fallback: 在 children 中查找 + for child in deck.children: + if getattr(child, "name", "") == "AGV": + agv_wh = child + break + if agv_wh is None: + logger.warning("AGV Deck 中未找到 WareHouse('AGV')") + return + + target_slot = self._get_agv_target_slot(agv_wh, slot_name) + if target_slot is None: + available_slots = list(getattr(agv_wh, "child_locations", {}).keys()) + logger.warning(f"AGV WareHouse 未找到槽位: {slot_name},可用槽位: {available_slots}") + return + + current_child = target_slot.children[0] if target_slot.children else None + + if material_type is not None: + # 放置物料 + # 优先使用中文显示名称,与工站前端一致(如 "30 mL粉桶托盘@AGV-1") + display_name = tray_display_name or material_type + desired_name = f"{display_name}@{slot_name}" + + # 如果已有同类型物料,跳过 + if current_child is not None: + existing_type = getattr(current_child, "_agv_material_type", None) + if existing_type == material_type: + return + # 类型变了,先卸载 + target_slot.unassign_child_resource(current_child) + + # 创建新物料(使用与手套箱 TB 区同源的载架工厂) + carrier_factory = self._get_carrier_factory() + factory_func = carrier_factory.get(material_type) + if factory_func: + try: + new_carrier = factory_func(name=desired_name, prefill_items=False) + except TypeError: + new_carrier = factory_func(name=desired_name) + else: + new_carrier = Container( + name=desired_name, + size_x=127.8, size_y=85.5, size_z=20.0, + ) + new_carrier.unilabos_uuid = str(uuid_mod.uuid4()) + new_carrier.description = material_type + new_carrier._agv_material_type = material_type + + # 填充子物料(试剂信息),使前端显示与工站一致 + if substance_details: + self._fill_carrier_substance_items( + new_carrier, substance_details, material_type, + ) + + target_slot.assign_child_resource(new_carrier) + logger.info( + f"AGV Deck 槽位 {slot_name} 放置物料: {display_name}" + f" (试剂数: {len(substance_details) if substance_details else 0})" + ) + else: + # 清空物料 + if current_child is not None: + target_slot.unassign_child_resource(current_child) + logger.info(f"AGV Deck 槽位 {slot_name} 物料已移除") + else: + return # 本来就是空的,不需要上报 + + # 扁平化上报 + if upload and hasattr(self, "_ros_node"): + self._upload_agv_deck_flattened() + + except Exception as exc: + logger.error(f"AGV Deck 槽位 {tray_name} 更新失败: {exc}") + import traceback + logger.debug(traceback.format_exc()) + + def _fill_carrier_substance_items( + self, + carrier, + substance_details: list[dict[str, str]], + material_type: str, + ) -> None: + """ + 功能: + 在载架(Carrier)中填充子物料(试剂瓶/粉桶等),使前端 Deck 显示: + ▼ 30 mL粉桶托盘@AGV-1 + 无水碳酸钾@A1 + 碳酸铯@B1 + 参数: + carrier: pylabrobot 载架资源对象。 + substance_details: 试剂详情列表, 每条含 well/substance/amount。 + material_type: 物料类型名称。 + """ + import uuid as uuid_mod + + if not substance_details: + return + + try: + from pylabrobot.resources import Container + + # 获取载架的 sites(ResourceHolder 子节点)列表 + sites = getattr(carrier, "children", []) + sites_by_name = {} + for site in sites: + site_name = getattr(site, "name", "") + if site_name: + sites_by_name[site_name] = site + + # 尝试解析 well → site 索引的映射 + tray_spec = self._get_tray_spec_for_material(material_type) + + for detail in substance_details: + well = detail.get("well", "") + substance = detail.get("substance", "") + amount = detail.get("amount", "") + + if not substance or not well: + continue + + # 构建子物料显示名称,如 "无水碳酸钾@A1" + item_name = f"{substance}@{well}" + if amount: + item_desc = f"{substance} {amount}" + else: + item_desc = substance + + # 尝试找到对应 site 并放置 + slot_index = self._well_to_slot_index_simple(well, tray_spec) + placed = False + + if slot_index is not None and slot_index < len(sites): + site = sites[slot_index] + if not site.children: + try: + item = Container( + name=item_name, + size_x=10.0, size_y=10.0, size_z=10.0, + ) + item.unilabos_uuid = str(uuid_mod.uuid4()) + item.description = item_desc + site.assign_child_resource(item) + placed = True + except Exception as place_exc: + logger.debug(f"放置子物料 {item_name} 到 site[{slot_index}] 失败: {place_exc}") + + if not placed: + logger.debug(f"子物料 {item_name} 无法定位到载架 site,跳过(不影响转运)") + + except Exception as exc: + logger.warning(f"填充载架子物料失败(不影响转运): {exc}") + + @staticmethod + def _get_tray_spec_for_material(material_type: str): + """根据 material_type 名称返回 (col, row) 规格, 用于 well 到 slot 索引转换。""" + try: + from unilabos.devices.eit_synthesis_station.config.constants import TraySpec + spec_map = { + "REAGENT_BOTTLE_TRAY_2ML": TraySpec.REAGENT_BOTTLE_TRAY_2ML, + "REAGENT_BOTTLE_TRAY_8ML": TraySpec.REAGENT_BOTTLE_TRAY_8ML, + "REAGENT_BOTTLE_TRAY_40ML": TraySpec.REAGENT_BOTTLE_TRAY_40ML, + "REAGENT_BOTTLE_TRAY_125ML": TraySpec.REAGENT_BOTTLE_TRAY_125ML, + "POWDER_BUCKET_TRAY_30ML": TraySpec.POWDER_BUCKET_TRAY_30ML, + "REACTION_TUBE_TRAY_2ML": TraySpec.REACTION_TUBE_TRAY_2ML, + "TEST_TUBE_MAGNET_TRAY_2ML": TraySpec.TEST_TUBE_MAGNET_TRAY_2ML, + "FLASH_FILTER_INNER_BOTTLE_TRAY": TraySpec.FLASH_FILTER_INNER_BOTTLE_TRAY, + "FLASH_FILTER_OUTER_BOTTLE_TRAY": TraySpec.FLASH_FILTER_OUTER_BOTTLE_TRAY, + "TIP_TRAY_50UL": TraySpec.TIP_TRAY_50UL, + "TIP_TRAY_1ML": TraySpec.TIP_TRAY_1ML, + "TIP_TRAY_5ML": TraySpec.TIP_TRAY_5ML, + } + return spec_map.get(material_type) + except Exception: + return None + + @staticmethod + def _well_to_slot_index_simple(well: str, tray_spec) -> int: + """ + 简易 well 到 slot_index 转换: A1→0, A2→1, B1→col_count, ... + 与 SynthesisStationController._well_to_slot_index 同源逻辑。 + """ + if not well or tray_spec is None: + return None + well = str(well).strip().upper() + if not well: + return None + row_char = well[0] + col_str = well[1:] + if not row_char.isalpha() or not col_str.isdigit(): + return None + row = ord(row_char) - ord("A") + col = int(col_str) - 1 + col_count = tray_spec[0] + return row * col_count + col + + @property + def status(self) -> str: + nav_status = self.query_nav_task_status() + if not nav_status: + return "UNKNOWN" + return str(nav_status.get("task_status_name", "UNKNOWN")) + + def get_current_station(self) -> str: + station_info = self.query_current_station() + if not station_info: + return "" + return str(station_info.get("station_id", "")) + + def send_nav_task(self, command: str): + """ + 兼容旧 AGV registry 的导航接口。 + + 支持两种输入: + - 直接传站点 ID,例如 "CP6" + - 传 JSON 字符串,例如 {"target":"CP6"} + """ + target = str(command).strip() + if target.startswith("{"): + try: + payload = json.loads(target) + target = str(payload.get("target", "")).strip() + except Exception as ex: + return { + "success": False, + "return_info": json.dumps( + {"suc": False, "msg": f"导航参数解析失败: {ex}"}, + ensure_ascii=False, + ), + } + + if not target: + return { + "success": False, + "return_info": json.dumps( + {"suc": False, "msg": "目标站点不能为空"}, + ensure_ascii=False, + ), + } + + result = self.navigate_to_station(target) + success = bool(result) + return { + "success": success, + "return_info": json.dumps( + { + "suc": success, + "target": target, + "result": result or {}, + }, + ensure_ascii=False, + ), + } + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_agv/controller/auto_charge_monitor.py b/unilabos/devices/eit_agv/controller/auto_charge_monitor.py new file mode 100644 index 00000000..f6568b9d --- /dev/null +++ b/unilabos/devices/eit_agv/controller/auto_charge_monitor.py @@ -0,0 +1,116 @@ +# coding: utf-8 +""" +功能: + AGV自动充电监控独立入口脚本 + 持续监控AGV所在位置, 当AGV在充电站CP6时自动执行电量检查与充电 + 支持命令行参数配置检查间隔和重试间隔 + +用法: + python -m eit_agv.controller.auto_charge_monitor + python -m eit_agv.controller.auto_charge_monitor --interval-hours 2 --retry-minutes 10 +""" + +import argparse +import logging +import os +import sys + +from .agv_controller import AGVController + + +def _setup_logging(log_dir: str) -> None: + """ + 功能: + 配置日志系统, 同时输出到控制台和日志文件 + 参数: + log_dir: 日志文件所在目录 + 返回: + 无 + """ + log_file = os.path.join(log_dir, "auto_charge_monitor.log") + + # 统一格式: 时间 级别 模块 消息 + fmt = logging.Formatter( + fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + + # 文件处理器, 追加模式, UTF-8编码 + file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a") + file_handler.setFormatter(fmt) + file_handler.setLevel(logging.DEBUG) + + # 控制台处理器 + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(fmt) + console_handler.setLevel(logging.INFO) + + root_logger = logging.getLogger() + root_logger.setLevel(logging.DEBUG) + root_logger.addHandler(file_handler) + root_logger.addHandler(console_handler) + + logging.info(f"日志文件路径: {log_file}") + + +def _parse_args() -> argparse.Namespace: + """ + 功能: + 解析命令行参数 + 返回: + argparse.Namespace, 包含interval_hours和retry_minutes + """ + parser = argparse.ArgumentParser( + description="AGV自动充电监控 - 当AGV在CP6充电站时自动检查并执行充电" + ) + parser.add_argument( + "--interval-hours", + type=float, + default=1.0, + metavar="HOURS", + help="AGV在CP6完成充电检查后的等待时间(小时), 默认1.0" + ) + parser.add_argument( + "--retry-minutes", + type=float, + default=5.0, + metavar="MINUTES", + help="AGV不在CP6时的重试查询间隔(分钟), 默认5.0" + ) + return parser.parse_args() + + +def main() -> None: + """ + 功能: + 主入口, 初始化日志和控制器后启动充电监控循环 + 返回: + 无 + """ + # 日志文件存放在 eit_agv/data 目录 + log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "data") + _setup_logging(log_dir) + + logger = logging.getLogger(__name__) + + args = _parse_args() + logger.info( + f"自动充电监控启动 | 检查间隔: {args.interval_hours}小时 | " + f"不在CP6时重试间隔: {args.retry_minutes}分钟" + ) + + try: + controller = AGVController() + controller.auto_charge_loop( + interval_hours=args.interval_hours, + retry_wait_minutes=args.retry_minutes + ) + except KeyboardInterrupt: + logger.info("收到中断信号, 自动充电监控已退出") + except Exception as e: + logger.exception(f"自动充电监控发生未处理异常: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_agv/controller/auto_charge_pp5_cp6_monitor.py b/unilabos/devices/eit_agv/controller/auto_charge_pp5_cp6_monitor.py new file mode 100644 index 00000000..7b034c29 --- /dev/null +++ b/unilabos/devices/eit_agv/controller/auto_charge_pp5_cp6_monitor.py @@ -0,0 +1,113 @@ +# coding: utf-8 +""" +功能: + PP5/CP6自动充电监控独立入口脚本. + 持续监控AGV所在位置, 当AGV位于PP5或CP6时按待命充电规则执行检查. + 支持命令行参数配置检查间隔和重试间隔. + +用法: + python -m eit_agv.controller.auto_charge_pp5_cp6_monitor + python -m eit_agv.controller.auto_charge_pp5_cp6_monitor --interval-hours 2 --retry-minutes 10 +""" + +import argparse +import logging +import os +import sys + +from .agv_controller import AGVController + + +def _setup_logging(log_dir: str) -> None: + """ + 功能: + 配置日志系统, 同时输出到控制台和日志文件. + 参数: + log_dir: 日志文件所在目录. + 返回: + 无. + """ + log_file = os.path.join(log_dir, "auto_charge_pp5_cp6_monitor.log") + + fmt = logging.Formatter( + fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a") + file_handler.setFormatter(fmt) + file_handler.setLevel(logging.DEBUG) + + console_handler = logging.StreamHandler(sys.stdout) + console_handler.setFormatter(fmt) + console_handler.setLevel(logging.INFO) + + root_logger = logging.getLogger() + root_logger.setLevel(logging.DEBUG) + root_logger.addHandler(file_handler) + root_logger.addHandler(console_handler) + + logging.info(f"日志文件路径: {log_file}") + + +def _parse_args() -> argparse.Namespace: + """ + 功能: + 解析命令行参数. + 返回: + argparse.Namespace, 包含interval_hours和retry_minutes. + """ + parser = argparse.ArgumentParser( + description="AGV PP5/CP6自动充电监控, 根据待命点和电量阈值切换PP5与CP6" + ) + parser.add_argument( + "--interval-hours", + type=float, + default=1.0, + metavar="HOURS", + help="检查成功后的等待时间(小时), 默认1.0", + ) + parser.add_argument( + "--retry-minutes", + type=float, + default=5.0, + metavar="MINUTES", + help="检查跳过或出错后的重试间隔(分钟), 默认5.0", + ) + return parser.parse_args() + + +def main() -> None: + """ + 功能: + 主入口, 初始化日志和控制器后启动PP5/CP6充电监控循环. + 参数: + 无. + 返回: + 无. + """ + log_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "data") + _setup_logging(log_dir) + + logger = logging.getLogger(__name__) + args = _parse_args() + logger.info( + f"PP5/CP6自动充电监控启动 | 检查间隔: {args.interval_hours}小时 | " + f"重试间隔: {args.retry_minutes}分钟" + ) + + try: + controller = AGVController() + controller.auto_charge_pp5_cp6_loop( + interval_hours=args.interval_hours, + retry_wait_minutes=args.retry_minutes, + ) + except KeyboardInterrupt: + logger.info("收到中断信号, PP5/CP6自动充电监控已退出") + except Exception as e: + logger.exception(f"PP5/CP6自动充电监控发生未处理异常: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_agv/data/__init__.py b/unilabos/devices/eit_agv/data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_agv/data/shelf_manager.py b/unilabos/devices/eit_agv/data/shelf_manager.py new file mode 100644 index 00000000..eb49ec23 --- /dev/null +++ b/unilabos/devices/eit_agv/data/shelf_manager.py @@ -0,0 +1,247 @@ +# coding:utf-8 +""" +功能: + 货架物料状态管理器, 负责追踪货架12个槽位的物料占用情况. + 使用 JSON 文件持久化, 支持放置/取走/查询空位等操作. +""" + +import copy +import json +import logging +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Optional + +logger = logging.getLogger(__name__) + +# 货架全部槽位定义(3层 x 4列 = 12个槽位) +SHELF_SLOT_NAMES: List[str] = [ + "shelf_tray_1-1", "shelf_tray_1-2", "shelf_tray_1-3", "shelf_tray_1-4", + "shelf_tray_2-1", "shelf_tray_2-2", "shelf_tray_2-3", "shelf_tray_2-4", + "shelf_tray_3-1", "shelf_tray_3-2", "shelf_tray_3-3", "shelf_tray_3-4", +] + + +class ShelfManager: + """ + 功能: + 货架槽位物料状态管理器. + 每个槽位可以为空或存放一种物料, 物料信息包含类型、来源、时间戳等. + 状态持久化到 shelf_status.json 文件. + """ + + def __init__(self, data_dir: Optional[Path] = None): + """ + 功能: + 初始化货架管理器, 加载或创建 shelf_status.json + 参数: + data_dir: 数据存储目录, 默认为本文件所在目录 + """ + if data_dir is None: + data_dir = Path(__file__).parent + self.data_dir = Path(data_dir) + self.status_file = self.data_dir / "shelf_status.json" + + self._ensure_directory() + self._status = self._load_status() + + def _ensure_directory(self) -> None: + """确保数据目录存在""" + self.data_dir.mkdir(parents=True, exist_ok=True) + + def _load_status(self) -> Dict[str, Any]: + """ + 功能: + 从 JSON 文件加载货架状态, 不存在则初始化全空 + 返回: + Dict, 包含 last_updated 和 slots 的状态字典 + """ + if self.status_file.exists(): + try: + with self.status_file.open("r", encoding="utf-8") as f: + data = json.load(f) + # 兼容处理: 确保所有槽位都存在 + slots = data.get("slots", {}) + for slot_name in SHELF_SLOT_NAMES: + if slot_name not in slots: + slots[slot_name] = None + data["slots"] = slots + logger.debug("已从 %s 加载货架状态", self.status_file) + return data + except Exception as e: + logger.error("加载货架状态失败: %s, 将初始化为全空", e) + + # 初始化全空状态 + return self._create_empty_status() + + def _create_empty_status(self) -> Dict[str, Any]: + """ + 功能: + 创建全空的货架状态 + 返回: + Dict, 全部槽位为 null 的状态字典 + """ + return { + "last_updated": datetime.now().isoformat(), + "slots": {name: None for name in SHELF_SLOT_NAMES}, + } + + def _save_status(self) -> None: + """ + 功能: + 将当前状态持久化到 shelf_status.json + """ + self._status["last_updated"] = datetime.now().isoformat() + try: + with self.status_file.open("w", encoding="utf-8") as f: + json.dump(self._status, f, ensure_ascii=False, indent=2) + logger.debug("货架状态已保存到 %s", self.status_file) + except Exception as e: + logger.error("保存货架状态失败: %s", e) + + def place_material( + self, + slot_name: str, + material_type: str, + source: str, + description: str = "", + ) -> bool: + """ + 功能: + 在指定槽位记录放置物料 + 参数: + slot_name: 槽位名称, 例如 "shelf_tray_1-1" + material_type: 物料类型标识, 例如 "FLASH_FILTER_OUTER_BOTTLE_TRAY" + source: 物料来源位置, 例如 "analysis_station_tray_1-2" + description: 物料描述说明 + 返回: + bool, True 表示成功, False 表示失败(槽位不存在或已占用) + """ + if slot_name not in SHELF_SLOT_NAMES: + logger.error("无效的槽位名称: %s", slot_name) + return False + + if self._status["slots"][slot_name] is not None: + logger.warning("槽位 %s 已有物料, 拒绝覆写", slot_name) + return False + + self._status["slots"][slot_name] = { + "material_type": material_type, + "source": source, + "description": description, + "placed_at": datetime.now().isoformat(), + } + self._save_status() + logger.info("已在槽位 %s 记录物料: 类型=%s, 来源=%s", slot_name, material_type, source) + return True + + def remove_material(self, slot_name: str) -> bool: + """ + 功能: + 清除指定槽位的物料记录(表示物料已被取走) + 参数: + slot_name: 槽位名称 + 返回: + bool, True 表示成功, False 表示槽位不存在或本身为空 + """ + if slot_name not in SHELF_SLOT_NAMES: + logger.error("无效的槽位名称: %s", slot_name) + return False + + if self._status["slots"][slot_name] is None: + logger.warning("槽位 %s 已经为空, 无需清除", slot_name) + return False + + old_info = self._status["slots"][slot_name] + self._status["slots"][slot_name] = None + self._save_status() + logger.info("已清除槽位 %s 的物料记录 (原物料类型: %s)", slot_name, old_info.get("material_type", "未知")) + return True + + def find_empty_slots(self, count: int = 1) -> List[str]: + """ + 功能: + 按固定顺序查找空闲槽位 + 参数: + count: 需要的空位数量, 默认为1 + 返回: + List[str], 空闲槽位名称列表, 可能少于 count(空位不足时) + """ + empty_slots = [] + for slot_name in SHELF_SLOT_NAMES: + if self._status["slots"][slot_name] is None: + empty_slots.append(slot_name) + if len(empty_slots) >= count: + break + + if len(empty_slots) < count: + logger.warning( + "空闲槽位不足: 需要 %d 个, 仅找到 %d 个", count, len(empty_slots) + ) + + return empty_slots + + def get_slot_info(self, slot_name: str) -> Optional[Dict[str, Any]]: + """ + 功能: + 获取单个槽位的物料信息 + 参数: + slot_name: 槽位名称 + 返回: + Dict 或 None, 槽位物料信息, 空槽位返回 None + """ + if slot_name not in SHELF_SLOT_NAMES: + logger.error("无效的槽位名称: %s", slot_name) + return None + + info = self._status["slots"][slot_name] + if info is not None: + return copy.deepcopy(info) + return None + + def get_all_status(self) -> Dict[str, Any]: + """ + 功能: + 获取全部货架状态的深拷贝 + 返回: + Dict, 包含 last_updated 和 slots 的完整状态 + """ + return copy.deepcopy(self._status) + + def print_status(self) -> None: + """ + 功能: + 在控制台打印可视化的货架状态表格 + """ + print("\n========== 货架物料状态 ==========") + print(f" 最后更新: {self._status.get('last_updated', '未知')}") + print("-" * 60) + print(f" {'槽位':<20} {'状态':<8} {'物料类型':<16} {'来源'}") + print("-" * 60) + + empty_count = 0 + occupied_count = 0 + + for slot_name in SHELF_SLOT_NAMES: + info = self._status["slots"].get(slot_name) + if info is None: + print(f" {slot_name:<20} {'空闲':<8}") + empty_count += 1 + else: + material_type = info.get("material_type", "未知") + source = info.get("source", "未知") + print(f" {slot_name:<20} {'占用':<8} {material_type:<16} {source}") + occupied_count += 1 + + print("-" * 60) + print(f" 合计: 占用 {occupied_count} / 空闲 {empty_count} / 总计 {len(SHELF_SLOT_NAMES)}") + print("=" * 38) + + def reset_all(self) -> None: + """ + 功能: + 重置全部槽位为空(调试用) + """ + self._status = self._create_empty_status() + self._save_status() + logger.info("已重置全部货架槽位为空") diff --git a/unilabos/devices/eit_agv/data/shelf_status.json b/unilabos/devices/eit_agv/data/shelf_status.json new file mode 100644 index 00000000..6ae8b255 --- /dev/null +++ b/unilabos/devices/eit_agv/data/shelf_status.json @@ -0,0 +1,22 @@ +{ + "last_updated": "2026-03-13T22:49:50.784894", + "slots": { + "shelf_tray_1-1": { + "material_type": "FLASH_FILTER_OUTER_BOTTLE_TRAY", + "source": "analysis_station_tray_1-2", + "description": "分析完成样品(自动转运)", + "placed_at": "2026-03-13T22:49:50.784894" + }, + "shelf_tray_1-2": null, + "shelf_tray_1-3": null, + "shelf_tray_1-4": null, + "shelf_tray_2-1": null, + "shelf_tray_2-2": null, + "shelf_tray_2-3": null, + "shelf_tray_2-4": null, + "shelf_tray_3-1": null, + "shelf_tray_3-2": null, + "shelf_tray_3-3": null, + "shelf_tray_3-4": null + } +} \ No newline at end of file diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/DucoCobot.py b/unilabos/devices/eit_agv/driver/DucoCobot/DucoCobot.py new file mode 100644 index 00000000..7c0dafc6 --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/DucoCobot.py @@ -0,0 +1,2329 @@ +# coding:utf-8 +#!/usr/bin/env python +# from http import client + +import sys +import glob +import threading +import time + +sys.path.append('gen_py') +sys.path.append('lib') +from thrift import Thrift +from thrift.transport import TSocket +from thrift.transport import TTransport +from thrift.protocol import TBinaryProtocol +from .gen_py.robot import RPCRobot +from .gen_py.robot.ttypes import StateRobot, StateProgram, OperationMode, TaskState, Op, RealTimeControlData, PointOp, MoveJogTaskParam, TrajTimeParam +from .gen_py.robot.ttypes import RobotStatus, IOStatus, EAxisParam, ReachabilityParam, EAxissInfo, PathOffsetResult +from ...config.arm_config import ARM_HOST, ARM_PORT, ARM_TIMEOUT + +class DucoCobot: + def __init__(self, ip=None, port=None, timeout=None): + """ + 功能: + 初始化机械臂连接 + 参数: + ip: 机械臂IP地址, 默认使用配置文件中的ARM_HOST + port: 机械臂端口, 默认使用配置文件中的ARM_PORT + timeout: socket超时时间, 单位毫秒, 默认使用配置文件中的ARM_TIMEOUT + """ + # 兼容设备图中的占位空值("", 0), 自动回退到配置文件默认值 + self.ip = ARM_HOST if ip in (None, "") else ip + self.port = ARM_PORT if port in (None, 0) else port + self.timeout = ARM_TIMEOUT if timeout in (None, 0) else timeout + + self.transport = TSocket.TSocket(self.ip, self.port) + # 设置socket超时时间, 避免长时间运动时连接断开 + self.transport.setTimeout(int(self.timeout)) + self.protocol = TBinaryProtocol.TBinaryProtocol(self.transport) + self.client = RPCRobot.Client(self.protocol) + + ''' + * @brief 连接机器人 + * @return -1:打开失败; 0:打开成功 + ''' + def open(self): + try: + self.transport.open() + except TTransport.TTransportException as e: + print("open mesg:",repr(e)) + return -1 + else: + return 0 + ''' + * @brief 断开机器人连接 + * @return -1:打开失败; 0:打开成功 + ''' + def close(self): + try: + self.transport.close() + except Exception as e: + print("close mesg:",repr(e)) + return -1 + else: + return 0 + ''' + * @brief 机器人上电 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def power_on(self, block): + return self.client.power_on(block) + ''' + * @brief 机器人下电 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def power_off(self, block): + return self.client.power_off(block) + ''' + * @brief 机器人上使能 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def enable(self, block): + return self.client.enable(block) + ''' + * @brief 机器人下使能 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def disable(self, block): + return self.client.disable(block) + ''' + * @brief 机器人关机 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def shutdown(self, block): + return self.client.shutdown(block) + ''' + * @brief 停止所有任务 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def stop(self, block): + return self.client.stop(block) + ''' + * @brief 暂停所有任务 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def pause(self, block): + return self.client.pause(block) + ''' + * @brief 恢复所有暂停的任务 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def resume(self, block): + return self.client.resume(block) + ''' + * @brief 运行程序脚本 + * @param name 脚本程序名称 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def run_program(self, name, block): + return self.client.run_program(name, block) + ''' + * @brief 设置工具末端相对于法兰面坐标系的偏移, 设置成功后, + * 后续运动函数中的TCP设置为该TCP或者为空时,使用该TCP参数 + * @param name 工具坐标系的名字 + * @param tool_offset 工具TCP偏移量 [x_off, y_off,z_off,rx,ry,rz], 单位: m, rad + * @param payload 末端负载质量, 质心, [mass,x_cog,y_cog,z_cog], 单位: kg, m + * @param inertia_tensor 末端工具惯量矩阵参数, 参数1-6分别对应矩阵xx、xy、xz、yy、yz、zz元素, 单位: kg*m^2 + * @return 返回当前任务结束时的状态 + ''' + def set_tool_data(self, name, tool_offset, payload, inertia_tensor): + return self.client.set_tool_data(name, tool_offset, payload, inertia_tensor) + ''' + * @brief 获取当前状态下机械臂有效的末端工具的偏移量 + * @return TCP偏移量信息,单位: m, rad + ''' + def get_tcp_offset(self): + return self.client.get_tcp_offset() + ''' + * @brief 设置工件坐标系 + * @param name 工件坐标系名称 + * @param wobj 工件坐标系 + * @return 返回当前任务结束时的状态 + ''' + def set_wobj(self, name, wobj): + return self.client.set_wobj(name, wobj) + ''' + * @brief 基于当前的工件坐标系设置一个偏移量, 后续的move类脚本的参考工件坐标系上都将添加这个偏移量 + * @param wobj 工件坐标系相对于基坐标系的位移, 单位: m, rad + * @param active 是否启用 + * @return 返回当前任务结束时的状态 + ''' + def set_wobj_offset(self, wobj, active = True): + return self.client.set_wobj_offset(wobj, active) + ''' + * @brief 计算机械臂的正运动学, 求解给定TCP在给定wobj下的值 + * @param joints_position 需要计算正解的关节角, 单位: rad + * @param tool 工具坐标系信息,tcp偏移量[x_off,y_off,z_off,rx,ry,rz], 单位:m, rad, 为空使用当前tcp值 + * @param wobj 工件坐标系相对于基坐标系的位移[x, y, z, rx, ry, rz], 单位:m, rad, 为空使用当前wobj + * @return 末端姿态列表[x,y,z,rx,ry,rz] + ''' + def cal_fkine(self, joints_position, tool, wobj): + return self.client.cal_fkine(joints_position, tool, wobj) + ''' + * @brief 计算运动学逆解, 在求解过程中, 会选取靠近当前机械臂关节位置的解 + * @param p 需要计算的末端位姿在设置工件坐标系的值,包含当前有效的工具偏移量, 单位:m,rad + * @param q_near 用于计算逆运动学的参考关节位置,为空使用当前关节值 + * @param tool 工具坐标系信息,tcp偏移量[x_off,y_off,z_off,rx,ry,rz], 单位:m, rad, 为空使用当前tcp值 + * @param wobj 工件坐标系相对于基坐标系的位移[x, y, z, rx, ry, rz], 单位:m, rad, 为空使用当前wobj + * @return 关节位置列表[q1,q2,q3,q4,q5,q6] + ''' + def cal_ikine(self, p, q_near, tool, wobj): + return self.client.cal_ikine(p, q_near, tool, wobj) + ''' + * @brief 设置通用IO输出的信号类型 + * @param num 控制柜上的IO输出口序号, 范围从1-16 + * @param type 输出的信号类型, 0: 高低电平, 1: 脉冲 + * @param freq 脉冲频率(Hz) + * @param duty_cycle 脉冲占空比(%) + * @return + ''' + def set_digital_output_mode(self, num, type, freq, duty_cycle): + return self.client.set_digital_output_mode(num, type, freq, duty_cycle) + ''' + * @brief 该函数可控制控制柜上的IO输出口的高低电平 + * @param num 控制柜上的IO输出口序号, 范围从1-16 + * @param value true为高电平, false为低电平 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_standard_digital_out(self, num, value, block): + return self.client.set_standard_digital_out(num, value, block) + ''' + * @brief set_tool_digital_out + * @param num 机械臂末端的IO输出口序号, 范围从1-2 + * @param value true为高电平, false为低电平 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_tool_digital_out(self, num, value, block): + return self.client.set_tool_digital_out(num, value, block) + ''' + * @brief 读取控制柜上的用户IO输入口的高低电平, 返回true为高电平, false为低电平 + * @param num 控制柜上的IO输入口序号, 范围从1-16 + * @return true为高电平, false为低电平 + ''' + def get_standard_digital_in(self, num): + return self.client.get_standard_digital_in(num) + ''' + * @brief 获取控制柜上通用IO输出口的高低电平, 返回true为高电平, false为低电平 + * @param num 控制柜上的IO输出口序号, 范围从1-16 + * @return true为高电平, false为低电平 + ''' + def get_standard_digital_out(self, num): + return self.client.get_standard_digital_out(num) + ''' + * @brief 读取机械臂末端的IO输入口的高低电平, 返回true为高电平, false为低电平 + * @param num 机械臂末端的IO输出口序号, 范围从1-2 + * @return true为高电平, false为低电平 + ''' + def get_tool_digital_in(self, num): + return self.client.get_tool_digital_in(num) + ''' + * @brief 读取机械臂末端的IO输入口的高低电平, 返回true为高电平, false为低电平 + * @param num 机械臂末端的IO输出口序号, 范围从1-2 + * @return true为高电平, false为低电平 + ''' + def get_tool_digital_out(self, num): + return self.client.get_tool_digital_out(num) + + def get_config_digital_in(self, num): + return self.client.get_config_digital_in(num) + ''' + * @brief 读取控制柜上的模拟电压输入 + * @param num 控制柜上的模拟电压通道序号, 范围从1-4 + * @return 对应通道的模拟电压值 + ''' + def get_standard_analog_voltage_in(self, num): + return self.client.get_standard_analog_voltage_in(num) + ''' + * @brief 读取机械臂末端的模拟电压输入 + * @param num 机械臂末端的模拟电压通道序号, 范围从1-2 + * @return 对应通道的模拟电压值 + ''' + def get_tool_analog_voltage_in(self, num): + return self.client.get_tool_analog_voltage_in(num) + ''' + * @brief 读取控制柜上的模拟电流输入 + * @param num 控制柜上的模拟电流通道序号, 范围从1-4 + * @return 对应通道的模拟电流值 + ''' + def get_standard_analog_current_in(self, num): + return self.client.get_standard_analog_current_in(num) + ''' + * @brief 读取功能输入寄存器的值 + * @param num 内部寄存器序号, 范围从1-16 + * @return bool寄存器的值 + ''' + def get_function_reg_in(self, num): + return self.client.get_function_reg_in(num) + ''' + * @brief 读取功能输出寄存器的值 + * @param num 内部寄存器序号, 范围从1-16 + * @return bool寄存器的值 + ''' + def get_function_reg_out(self, num): + return self.client.get_function_reg_out(num) + ''' + * @brief 读取控制柜功能输入IO高低电平, 返回true为高电平, false为低电平 + * @param num 控制柜功能IO输入口序号, 范围从1-8 + * @return true为高电平, false为低电平 + ''' + def get_function_digital_in(self, num): + return self.client.get_function_digital_in(num) + ''' + * @brief 读取控制柜功能输出IO高低电平, 返回true为高电平, false为低电平 + * @param num 控制柜功能IO输出口序号, 范围从1-8 + * @return true为高电平, false为低电平 + ''' + def get_function_digital_out(self, num): + return self.client.get_function_digital_out(num) + ''' + * @brief 485端口读取长度为len的字节数据 + * @param len 读取的长度 + * @return 读取到的数据, 未读到数据返回空列表 + ''' + def read_raw_data_485(self, len): + return self.client.read_raw_data_485(len) + ''' + * @brief 匹配头head后读取到长度为len的一帧数据 + * @param head 需要匹配的头数据 + * @param len 需要读取的长度 + * @return 读取到的数据, 未读到数据返回空列表 + ''' + def read_raw_data_485_h(self, head, len): + return self.client.read_raw_data_485_h(head, len) + ''' + * @brief 匹配头head和尾tail读取到一帧匹配的数据 + * @param head 需要匹配的头数据 + * @param tail 需要匹配的尾数据 + * @return 读取到的数据, 未读到数据返回空列表 + ''' + def read_raw_data_485_ht(self, head, tail): + return self.client.read_raw_data_485_ht(head, tail) + ''' + * @brief 485写原生数据, 将表value中的数据写入485端口 + * @param data 需要写入的数据列表 + * @return true:成功, false:失败 + ''' + def write_raw_data_485(self, data): + return self.client.write_raw_data_485(data) + ''' + * @brief 485写原生数据, 将列表value中的数据加上head写入485端口 + * @param data 需要写入的数据列表 + * @param head 需要添加的头 + * @return true:成功, false:失败 + ''' + def write_raw_data_485_h(self, data, head): + return self.client.write_raw_data_485_h(data, head) + ''' + * @brief 485写原生数据, 将列表value中的数据加上头head和尾tail写入485端口 + * @param data 写入的数据列表 + * @param head 需要添加的头 + * @param tail 需要添加的尾 + * @return true:成功, false:失败 + ''' + def write_raw_data_485_ht(self, data, head, tail): + return self.client.write_raw_data_485_ht(data, head, tail) + ''' + * @brief 末端485端口读取长度为len的字节数据 + * @param len 需要读取的长度 + * @return 读取到的数据, 未读到数据返回空列表 + ''' + def tool_read_raw_data_485(self, len): + return self.client.tool_read_raw_data_485(len) + ''' + * @brief 末端485匹配头head后读取到长度为len的一帧数据 + * @param head 需要匹配的头数据 + * @param len 需要读取的长度 + * @return 读取到的数据, 未读到数据返回空列表 + ''' + def tool_read_raw_data_485_h(self, head, len): + return self.client.tool_read_raw_data_485_h(head, len) + ''' + * @brief 末端485匹配头head和尾tail读取到一帧匹配的数据 + * @param head 需要匹配的头数据 + * @param tail 需要匹配的尾数据 + * @return 读取到的数据, 未读到数据返回空列表 + ''' + def tool_read_raw_data_485_ht(self, head, tail): + return self.client.tool_read_raw_data_485_ht(head, tail) + ''' + * @brief 末端485写原生数据, 将data写入485端口 + * @param data 写入的数据列表 + * @return true:成功, false:失败 + ''' + def tool_write_raw_data_485(self, data): + return self.client.tool_write_raw_data_485(data) + ''' + * @brief 末端485写原生数据, 将data中的数据加上head写入485端口 + * @param data 写入的数据列表 + * @param head 添加的头 + * @return true:成功, false:失败 + ''' + def tool_write_raw_data_485_h(self, data, head): + return self.client.tool_write_raw_data_485_h(data, head) + ''' + * @brief 末端485写原生数据, 将value中的数据加上头head和尾tail写入485端口 + * @param data 写入的数据列表 + * @param head 添加的头 + * @param tail 添加的尾 + * @return true:成功, false:失败 + ''' + def tool_write_raw_data_485_ht(self, data, head, tail): + return self.client.tool_write_raw_data_485_ht(data, head, tail) + ''' + * @brief 读取一帧can的字节数据 + * @return 读取到的数据, 未读到数据返回空列表, 读到数据时, 列表的第一个数据为发送端的can帧id + ''' + def read_raw_data_can(self): + return self.client.read_raw_data_can() + ''' + * @brief can写帧为id, 数据为data的原生数据 + * @param id 数据帧的id + * @param data 发送的数据列表 + * @return true:成功, false:失败 + ''' + def write_raw_data_can(self, id, data): + return self.client.write_raw_data_can(id, data) + ''' + * @brief 读取bool寄存器的值 + * @param num 寄存器序号, num范围为1-64 + * @return true or false + ''' + def read_bool_reg(self, num): + return self.client.read_bool_reg(num) + ''' + * @brief 读取word寄存器的值 + * @param num 寄存器序号, num范围为1-32 + * @return 寄存器的值 + ''' + def read_word_reg(self, num): + return self.client.read_word_reg(num) + ''' + * @brief 读取float寄存器的值 + * @param num 寄存器序号, num范围为1-32 + * @return 寄存器的值 + ''' + def read_float_reg(self, num): + return self.client.read_float_reg(num) + ''' + * @brief 修改bool寄存器的值 + * @param num 寄存器序号, num范围为1-64 + * @param value true or false + * @return 返回当前任务结束时的状态 + ''' + def write_bool_reg(self, num, value): + return self.client.write_bool_reg(num, value) + ''' + * @brief 修改word寄存器的值 + * @param num 寄存器序号, num范围为1-32 + * @param value 寄存器的值 + * @return 返回当前任务结束时的状态 + ''' + def write_word_reg(self, num, value): + return self.client.write_word_reg(num, value) + ''' + * @brief 修改float寄存器的值 + * @param num 寄存器序号, num范围为1-32 + * @param value 寄存器的值 + * @return 返回当前任务结束时的状态 + ''' + def write_float_reg(self, num, value): + return self.client.write_float_reg(num, value) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动到目标关节角状态 + * @param joints_list 1-6关节的目标关节角度, 单位: rad + * @param v 关节角速度, 单位: 系统设定速度的百分比%, 取值范围(0,100] + * @param a 关节角加速度, 单位: 系统设定加速度的百分比%, 取值范围(0,100] + * @param r 融合半径, 单位: 系统设定最大融合半径的百分比%, 默认值为 0, 表示无融合, 取值范围[0,50) + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movej(self, joints_list, v, a, r, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movej(joints_list, v, a, r, block, op, def_acc) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动到目标关节角状态 + * @param joints_list 1-6关节的目标关节角度, 单位: rad + * @param v 关节角速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param a 关节角加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movej2(self, joints_list, v, a, r, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movej2(joints_list, v, a, r, block, op, def_acc) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动到末端目标位置 + * @param p 对应末端的位姿, 位置单位: m, 姿态以Rx、Ry、Rz表示, 单位: rad + * @param v 关节角速度, 单位: 系统设定速度的百分比%, 取值范围(0,100] + * @param a 关节加速度, 单位: 系统设定加速度的百分比%, 取值范围(0,100] + * @param r 融合半径, 单位: 系统设定最大融合半径的百分比%, 默认值为 0, 表示无融合, 取值范围[0,50) + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movej_pose(self, p, v, a, r, q_near, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movej_pose(p, v, a, r, q_near, tool, wobj, block, op, def_acc) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动到末端目标位置 + * @param p 对应末端的位姿, 位置单位: m, 姿态以Rx、Ry、Rz表示, 单位: rad + * @param v 关节角速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param a 关节加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movej_pose2(self, p, v, a, r, q_near, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + op.time_or_dist_1 + return self.client.movej_pose2(p, v, a, r, q_near, tool, wobj, block, op, def_acc) + ''' + * @brief 控制机械臂末端从当前状态按照直线路径移动到目标状态 + * @param p 对应末端的位姿, 位置单位: m, 姿态以Rx、Ry、Rz表示, 单位: rad + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movel(self, p, v, a, r, q_near, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movel(p, v, a, r, q_near, tool, wobj, block, op, def_acc) + ''' + * @brief 控制机械臂做圆弧运动, 起始点为当前位姿点, 途径p1点, 终点为p2点 + * @param p1 圆弧运动中间点 + * @param p2 圆弧运动结束点 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param mode 姿态控制模式 0:姿态与终点保持一致;1:姿态与起点保持一致;2:姿态受圆心约束 + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @param arc_rad 圆弧弧度, 为0时运动到p2结束点, 按照原始圆弧规划, 其余情况按照数据圆弧角规划(此模式姿态仅支持起点一致和受圆心约束), 单位: rad. 可缺省, 默认为0 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movec(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op = None, def_acc = False, arc_rad = 0): + if(op is None): + op = Op() + return self.client.movec(p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc, arc_rad) + ''' + * @brief 控制机械臂做圆周运动, 起始点为当前位姿点, 途径p1点和p2点 + * @param p1 圆周运动经过点 + * @param p2 圆周运动经过点 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度,范围[0.00001, ∞), 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param mode 姿态控制模式 1:姿态与终点保持一致; 2:姿态受圆心约束 + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def move_circle(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.move_circle(p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc) + ''' + * @brief 控制机械臂沿工具坐标系直线移动一个增量 + * @param pose_offset 工具坐标系下的位姿偏移量 + * @param v 直线移动的速度, 范围[0.00001, 5], 单位: m/s, 当x、y、z均为0时, 线速度按比例换算成角速度 + * @param a 加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def tcp_move(self, pose_offset, v, a, r, tool, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.tcp_move(pose_offset, v, a, r, tool, block, op, def_acc) + ''' + * @brief 控制机器人沿工具坐标系直线移动一个增量, 增量为p1与p2点之间的差, 运动的目标点为:当前点*p1^-1*p2 + * @param p1 工具坐标系下的位姿偏移量计算点1 + * @param p2 工具坐标系下的位姿偏移量计算点2 + * @param v 直线移动的速度, 范围[0.00001, 5], 单位: m/s, 当x、y、z均为0时, 线速度按比例换算成角速度 + * @param a 加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def tcp_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.tcp_move_2p(p1, p2, v, a, r, tool, wobj, block, op, def_acc) + ''' + * @brief 控制机械臂沿工件坐标系直线移动一个增量 + * @param pose_offset 工件坐标系下的位姿偏移量 + * @param v 直线移动的速度, 范围[0.00001, 5], 单位: m/s, 当x、y、z均为0时, 线速度按比例换算成角速度 + * @param a 加速度, 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def wobj_move(self, pose_offset, v, a, r, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.wobj_move(pose_offset, v, a, r, wobj, block, op, def_acc) + ''' + * @brief 控制机器人沿工件坐标系直线移动一个增量, 增量为p1与p2点之间的差, 运动的目标点为:当前点*p1^-1*p2 + * @param p1 工件坐标系下的位姿偏移量计算点1 + * @param p2 工件坐标系下的位姿偏移量计算点2 + * @param v 直线移动的速度, 范围[0.00001, 5], 单位: m/s, 当x、y、z均为0时, 线速度按比例换算成角速度 + * @param a 加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param r 融合半径, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param tool 设置使用的工具坐标系的名称, 为空时默认为当前使用的工具坐标系 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def wobj_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.wobj_move_2p(p1, p2, v, a, r, tool, wobj, block, op, def_acc) + ''' + * @brief 样条运动函数, 控制机器人按照空间样条进行运动 + * @param pose_list 在设置工件坐标系下的末端位姿列表,最多不超过50个点 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def spline(self, pose_list, v, a, tool, wobj, block, op = None, r = 0, def_acc = False): + if(op is None): + op = Op() + return self.client.spline(pose_list, v, a, tool, wobj, block, op, r, def_acc) + ''' + * @brief 控制机械臂每个关节按照给定的速度一直运动, 函数执行后会直接运行后续指令. + * 运行speedj函数后, 机械臂会持续运动并忽略后续运动指令, 直到接收到speed_stop()函数后停止 + * @param joints_list 每个关节的速度, 单位: rad/s + * @param a 主导轴的关节加速度, 单位: rad/s^2 + * @param time 运行时间, 到达时间后会停止运动,单位: ms.默认-1表示一直运行 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def speedj(self, joints_list, a, time, block): + return self.client.speedj(joints_list, a, time, block) + ''' + * @brief 控制机械臂末端按照给定的速度一直运动, 函数执行后会直接运行后续指令. + * 运行speedl函数后, 机械臂会持续运动并忽略后续运动指令, 直到接收到speed_stop()函数后停止 + * @param pose_list 末端速度向量, 范围[0.00001, 5], 线速度单位: m/s,角速度单位: rad/s + * @param a 末端的线性加速度, 范围[0.00001, ∞), 单位: rad/s^2 + * @param time 运行时间, 到达时间会停止运动, 单位: ms.默认-1表示一直运行 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def speedl(self, pose_list, a, time, block): + return self.client.speedl(pose_list, a, time, block) + ''' + * @brief 停止speedj及speedl函数的运动 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def speed_stop(self, block): + return self.client.speed_stop(block) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动到目标关节角状态,运动过程中不考虑笛卡尔空间路径 + * @param joints_list 目标关节角度, 单位: rad + * @param v 最大关节角速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param a 最大关节加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回, 可缺省, 默认为非阻塞 + * @param kp 比例参数, 默认值200, 可缺省, 建议使用默认参数 + * @param kd 微分参数, 默认值25, 可缺省, 建议使用默认参数 + * @param smooth_vel 速度平滑参数, 默认值10, 可缺省, 范围[1-10], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数已保证速度的平滑度, 否则可根据实际需要降低参数提高精度 + * @param smooth_acc 加速度平滑参数, 默认值10, 可缺省, 范围[0-1], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数以保证加速度的平滑度, 否则可根据实际需要降低参数提高跟踪精度 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def servoj(self, joints_list, v, a, block,kp=200, kd=25, smooth_vel=10, smooth_acc=1): + return self.client.servoj(joints_list, v, a, block, kp, kd, smooth_vel, smooth_acc) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动到目标笛卡尔状态,通过关节空间运动 + * @param pose_list 目标工件坐标系下的末端位姿, 单位: m, rad + * @param v 关节速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param a 关节加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回, 可缺省, 默认为非阻塞 + * @param kp 比例参数, 默认值200, 可缺省, 建议使用默认参数 + * @param kd 微分参数, 默认值25, 可缺省, 建议使用默认参数 + * @param smooth_vel 速度平滑参数, 默认值10, 可缺省, 范围[1-10], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数已保证速度的平滑度, 否则可根据实际需要降低参数提高精度 + * @param smooth_acc 加速度平滑参数, 默认值10, 可缺省, 范围[0-1], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数以保证加速度的平滑度, 否则可根据实际需要降低参数提高跟踪精度 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def servoj_pose(self, pose_list, v, a, q_near, tool, wobj, block, kp=200, kd=25, smooth_vel=10, smooth_acc=1): + return self.client.servoj_pose(pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc) + ''' + * @brief 控制机械臂从当前状态, 按照关节运动的方式移动一个增量,通过关节空间运动 + * @param pose_offset 目标工件坐标系下的末端位姿, 单位: m, rad + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回, 默认值false, 可缺省 + * @param kp 比例参数, 默认值200, 可缺省, 建议使用默认参数 + * @param kd 微分参数, 默认值25, 可缺省, 建议使用默认参数 + * @param smooth_vel 速度平滑参数, 默认值10, 可缺省, 范围[1-10], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数已保证速度的平滑度, 否则可根据实际需要降低参数提高精度 + * @param smooth_acc 加速度平滑参数, 默认值10, 可缺省, 范围[0-1], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数以保证加速度的平滑度, 否则可根据实际需要降低参数提高跟踪精度 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def servo_tcp(self, pose_offset, v, a, tool, block, kp=200, kd=25, smooth_vel=10, smooth_acc=1): + return self.client.servo_tcp(pose_offset, v, a, tool, block, kp, kd, smooth_vel, smooth_acc) + ''' + * @brief 控制机械臂末端从当前状态按照直线路径移动到目标状态 + * @param pose_list 对应末端的位姿, 位置单位: m, 姿态以Rx、Ry、Rz表示, 单位: rad + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param q_near 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回, 可缺省, 默认为非阻塞 + * @param kp 比例参数, 默认值200, 可缺省, 建议使用默认参数 + * @param kd 微分参数, 默认值25, 可缺省, 建议使用默认参数 + * @param smooth_vel 速度平滑参数, 默认值10, 可缺省, 范围[1-10], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数已保证速度的平滑度, 否则可根据实际需要降低参数提高精度 + * @param smooth_acc 加速度平滑参数, 默认值10, 可缺省, 范围[0-1], 当快速连续发送密集离散点位且无法保证点位间隔均匀性时, 推荐使用较大参数以保证加速度的平滑度, 否则可根据实际需要降低参数提高跟踪精度 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def servol(self, pose_list, v, a, q_near, tool, wobj, block, kp=200, kd=25, smooth_vel=10, smooth_acc=1): + return self.client.servol(pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc) + ''' + * @brief 控制机器人进入牵引示教模式 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def teach_mode(self, block): + return self.client.teach_mode(block) + ''' + * @brief 控制机器人退出牵引示教模式 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def end_teach_mode(self, block): + return self.client.end_teach_mode(block) + + def modbus_add_signal(self, ip, slave_number, signal_address, signal_type, signal_name): + return self.client.modbus_add_signal(ip, slave_number, signal_address, signal_type, signal_name) + + def modbus_delete_signal(self, signal_name): + return self.client.modbus_delete_signal(signal_name) + ''' + * @brief 读取modbus节点的数据, 返回值为double类型 + * @param signal_name modbus节点名 + * @return 节点返回值 + ''' + def modbus_read(self, signal_name): + return self.client.modbus_read(signal_name) + ''' + * @brief 对modbus节点进行写操作 + * @param signal_name modbus节点名 + * @param value 写入的数值, 寄存器节点取值为0-65535内的整数, 线圈节点取值为0或1 + * @return 返回当前任务结束时的状态 + ''' + def modbus_write(self, signal_name, value): + return self.client.modbus_write(signal_name, value) + ''' + * @brief 修改modbus节点的刷新频率, 默认频率为10Hz + * @param signal_name modbus节点名 + * @param frequence 频率值, 取值范围:1~100Hz + ''' + def modbus_set_frequency(self, signal_name, frequence): + return self.client.modbus_set_frequency(signal_name, frequence) + ''' + * @brief 读取机器人最新的错误列表 + * @return 错误列表 + ''' + def get_last_error(self): + return self.client.get_last_error() + ''' + * @brief 根据id查询当前的任务状态 + * @param id 任务的id + * @return 任务的当前执行状态 + ''' + def get_noneblock_taskstate(self, id): + return self.client.get_noneblock_taskstate(id) + ''' + * @brief 插入log日志, 记录运行问题 + * @param message 日志描述 + ''' + def log_info(self, message): + self.client.log_info(message) + ''' + * @brief 在运行过程中产生弹窗, 并暂停当前所有任务 + * @param message 弹窗描述 + ''' + def log_error(self, message): + self.client.log_error(message) + ''' + * @brief 切换机器人到仿真或者真机模式 + * @param sim true:仿真, false:真机 + * @param block + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def simulation(self, sim, block): + return self.client.simulation(sim, block) + ''' + * @brief 获取当前是否是仿真模式 + * @return true:仿真, false:真机 + ''' + def get_simulation_state(self): + return self.client.get_simulation_state() + ''' + * @brief 设置机器人全局速度 + * @param val 机器人全局速度, 范围[1,100] + * @return 任务结束时状态 + ''' + def speed(self, val): + return self.client.speed(val) + ''' + * @brief 获取当前机器人状态 + * @return 机器人状态信息列表, data[0]表示机器人状态, data [1]表示程序状态, + * data [2]表示安全控制器状态, data [3]表示操作模式 + ''' + def get_robot_state(self): + return self.client.get_robot_state() + ''' + * @brief 法兰笛卡尔指令位置, 参考base坐标系 + * @param _return + ''' + def get_flange_pose_cmd(self): + return self.client.get_flange_pose_cmd() + ''' + * @brief g法兰笛卡尔指令速度, 参考base坐标系 + * @param _return + ''' + def get_flange_speed_cmd(self): + return self.client.get_flange_speed_cmd() + ''' + * @brief 法兰笛卡尔指令加速度, 参考base坐标系 + * @param _return + ''' + def get_flange_acceleration_cmd(self): + return self.client.get_flange_acceleration_cmd() + ''' + * @brief 获取当前状态下机械臂末端法兰在基坐标系下的位姿 + * @return 末端法兰位置 + ''' + def get_flange_pose(self): + return self.client.get_flange_pose() + ''' + * @brief 获取当前状态下机械臂末端法兰在基坐标系下的速度 + * @return 末端法兰速度列表, 单位: m/s,rad/s + ''' + def get_flange_speed(self): + return self.client.get_flange_speed() + ''' + * @brief 获取当前状态下机械臂末端法兰在基坐标系下的加速度 + * @return 末端法兰加速度列表, 单位: m/s^2, rad/s^2 + ''' + def get_flange_acceleration(self): + return self.client.get_flange_acceleration() + ''' + * @brief 当前生效TCP笛卡尔指令位置, 参考base坐标系 + * @param _return 当前生效TCP笛卡尔指令位置 + ''' + def get_tcp_pose_command(self): + return self.client.get_tcp_pose_command() + ''' + * @brief 当前生效TCP笛卡尔指令速度, 参考base坐标系 + * @param _return 当前生效TCP笛卡尔指令速度 + ''' + def get_tcp_speed_command(self): + return self.client.get_tcp_speed_command() + ''' + * @brief 当前生效TCP笛卡尔指令加速度, 参考base坐标系 + * @param _return 当前生效TCP笛卡尔指令加速度 + ''' + def get_tcp_acceleration_command(self): + return self.client.get_tcp_acceleration_command() + ''' + * @brief 获取当前状态下机械臂工具末端点在基坐标系下的位姿 + * @return 末端位姿 + ''' + def get_tcp_pose(self): + return self.client.get_tcp_pose() + ''' + * @brief 获取当前状态下机械臂工具末端点的速度 + * @return 末端速度列表, 单位: m/s,rad/s + ''' + def get_tcp_speed(self): + return self.client.get_tcp_speed() + ''' + * @brief 获取当前状态下机械臂工具末端点的加速度 + * @return 末端加速度列表, 单位: m/s^2, rad/s^2 + ''' + def get_tcp_acceleration(self): + return self.client.get_tcp_acceleration() + ''' + * @brief 获取当前末端的力矩信息 + * @return 末端力矩信息, [Fx,Fy,Fz,Mx,My,Mz],单位: N、N.m + ''' + def get_tcp_force(self): + return self.client.get_tcp_force() + ''' + * @brief 获取当前状态下机械臂各关节的角度 + * @return 1-6轴关节角度列表, 单位: rad + ''' + def get_actual_joints_position(self): + return self.client.get_actual_joints_position() + ''' + * @brief 获取当前状态下机械臂各关节的规划角度 + * @return 1-6轴目标关节角度列表, 单位: rad + ''' + def get_target_joints_position(self): + return self.client.get_target_joints_position() + ''' + * @brief 获取当前状态下机械臂各关节角速度 + * @return 1-6轴关节速度列表, 单位: rad/s + ''' + def get_actual_joints_speed(self): + return self.client.get_actual_joints_speed() + ''' + * @brief 获取当前状态下机械臂各关节规划角速度 + * @return 1-6轴目标关节速度列表, 单位: rad/s + ''' + def get_target_joints_speed(self): + return self.client.get_target_joints_speed() + ''' + * @brief 获取当前状态下机械臂各关节角加速度 + * @return 1-6轴关节加速度列表, 单位: rad/s^2 + ''' + def get_actual_joints_acceleration(self): + return self.client.get_actual_joints_acceleration() + ''' + * @brief 取当前状态下机械臂各关节角规划加速度 + * @return 1-6轴关节加速度列表, 单位: rad/s^2 + ''' + def get_target_joints_acceleration(self): + return self.client.get_target_joints_acceleration() + ''' + * @brief 获取当前状态下机械臂各关节力矩 + * @return 1-6轴关节力矩列表, 单位: N.m + ''' + def get_actual_joints_torque(self): + return self.client.get_actual_joints_torque() + ''' + * @brief 获取当前状态下机械臂各关节目标力矩 + * @return 1-6轴关节加速度列表, 单位: rad/s^2 + ''' + def get_target_joints_torque(self): + return self.client.get_target_joints_torque() + ''' + * @brief 停止轨迹记录 + * @return 任务结束时状态 + ''' + def stop_record_track(self): + return self.client.stop_record_track() + ''' + * @brief 开启轨迹记录,当超过允许记录的轨迹长度(针对基于位置记录)或允许记录的时长时(针对基于时间记录), + * 会自动停止文件记录,并且暂停当前运行的程序. + * 文件会记录机器人的各关节弧度值和选定工具、工件坐标系下的笛卡尔空间位姿 + * @param name 轨迹名称 + * @param mode 轨迹类型, mode=0基于位置记录(与上一记录点所有关节偏移总量到达5°时记录新点); + * mode=1基于时间记录(与上一记录点间隔250ms记录新点) + * @param tool 工具坐标系名称 + * @param wobj 工件坐标系名称 + * @return 当前任务的id + ''' + def start_record_track(self, name, mode, tool, wobj): + interval = 5.0 + if mode == 1: + interval = 0.5 + + return self.client.start_record_track(name, mode, tool, wobj, interval) + ''' + * @brief 设置碰撞检测等级 + * @param value 0:关闭碰撞检测, 1-5:对应设置碰撞检测等级1到等级5 + * @return 任务结束时状态 + ''' + def collision_detect(self, value): + return self.client.collision_detect(value) + ''' + * @brief 对记录的轨迹基于关节空间(或基于笛卡尔空间)复现 + * @param name 轨迹名称 + * @param value 轨迹速度, (系统设定速度的百分比%), 取值范围(0,100] + * @param mode 复现方式, 0:基于关节空间, 1:基于笛卡尔空间 + * @return 任务结束时状态 + ''' + def replay(self, name, value, mode): + return self.client.replay(name, value, mode) + ''' + * @brief 设置抓取负载.可以在程序运行过程中设置机器人当前的负载(质量、质心) + * @param value 末端工具抓取负载质量, 质心, {mass,x_cog,y_cog,z_cog}, + * 相对于工具坐标系, 质量范围[0, 35], 单位: kg, m + * @return 任务结束时状态 + ''' + def set_load_data(self, value): + return self.client.set_load_data(value) + ''' + * @brief 控制机械臂开启末端力控.开启末端力控后所有运动函数除正常运动外, + * 会额外基于已配置的末端力控参数进行末端力控运动 + * @return 返回值代表当前任务的id信息 + ''' + def fc_start(self): + return self.client.fc_start() + ''' + * @brief 控制机械臂退出末端力控 + * @return 当前任务的id信息 + ''' + def fc_stop(self): + return self.client.fc_stop() + ''' + * @brief 修改并配置机器人末端力控参数 + * @param direction 6个笛卡尔空间方向末端力控开关, 开为true, 关为false + * @param ref_ft 6个笛卡尔空间方向末端力控参考力, 范围[-1000, 1000], X/Y/Z方向单位: N, + RX/RY/RZ方向单位: Nm, 方向符号参考末端力控参考坐标系方向 + * @param damp 6个笛卡尔空间方向末端力控阻尼, 范围[-10000, 10000], + * X/Y/Z方向单位: N/(m/s), RX/RY/RZ方向单位: Nm/(°/s) + * @param max_vel 6个笛卡尔空间方向末端力控最大调整速度, 范围[-5, 5], X/Y/Z方向单位: m/s, + * 范围[-2*PI, 2*PI], RX/RY/RZ方向单位: rad/s + * @param dead_zone 6个笛卡尔空间方向末端与环境接触力死区, 范围[-1000, 1000], + * X/Y/Z方向单位: N, RX/RY/RZ方向单位: Nm + * @param toolname 设置使用的末端力控工具的名称, 默认为当前使用的工具 + * @param wobjname 设置使用的末端力控工件坐标系的名称, 默认为当前使用的工件坐标系 + * @param value 末端力控参考坐标系选择标志位, 0为参考工具坐标系, 1位参考工件坐标系 + * @return 当前任务的id信息 + ''' + def fc_config(self, direction, ref_ft, damp, max_vel, dead_zone, toolname, wobjname, value): + return self.client.fc_config(direction, ref_ft, damp, max_vel, dead_zone, toolname, wobjname, value) + ''' + * @brief 控制机械臂仅产生末端力控运动 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回, 默认为false + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def fc_move(self, block = False): + return self.client.fc_move(block) + ''' + * @brief 控制机械臂在末端力控过程中进行力安全监控 + * @param direction 6个笛卡尔空间方向末端力安全监控开关, 开为true, 关为false + * @param ref_ft 6个笛卡尔空间方向末端力安全监控参考力, X/Y/Z方向单位: N, RX/RY/RZ方向单位: Nm, + * 方向符号参考末端力安全监控参考坐标系方向 + * @param toolname 设置使用的末端力安全监控工具的名称 + * @param wobjname 设置使用的末端力安全监控工件坐标系的名称 + * @param type 末端力安全监控参考坐标系选择标志位, 0为参考工具坐标系, 1位参考工件坐标系 + * @param force_property 监控力属性, 0为末端负载力及外力, 1为末端外力(不含负载),可缺省, 默认为0 + * @return 当前任务的id信息 + ''' + def fc_guard_act(self, direction, ref_ft, toolname, wobjname, type, force_property = 0): + return self.client.fc_guard_act(direction, ref_ft, toolname, wobjname, type, force_property) + ''' + * @brief 控制机械臂在末端力控过程中禁用力安全监控 + * @return 当前任务的id信息 + ''' + def fc_guard_deact(self): + return self.client.fc_guard_deact() + ''' + * @brief 控制机械臂末端力传感器读数设置为指定值 + * @param direction 6个末端力传感器输出力设置标志位, 需要设置为true, 不需要设置为false + * @param ref_ft 6个末端力传感器输出力设置目标值, X/Y/Z方向单位: N, RX/RY/RZ方向单位: Nm + * @return 当前任务的id信息 + ''' + def fc_force_set_value(self, direction, ref_ft): + return self.client.fc_force_set_value(direction, ref_ft) + ''' + * @brief 控制机械臂在执行fc_start()函数后的末端力控过程中满足指定位置判断条件时自动停止当前运动函数并调过后续运动函数, + * 直到fc_stop()函数被执行停止末端力控 + * @param middle 位置判断条件绝对值, X/Y/Z方向单位: m, RX/RY/RZ方向单位: rad + * @param range 位置判断条件偏移范围大小, X/Y/Z方向单位: m, RX/RY/RZ方向单位: rad + * @param absolute 绝对/增量条件判断标志位, true为绝对位置判断, false为增量位置判断 + * @param duration 条件满足触发保持时间, 单位: ms + * @param timeout 条件满足触发超时时间, 单位: ms + * @return 当前任务的id信息 + ''' + def fc_wait_pos(self, middle, range, absolute, duration, timeout): + return self.client.fc_wait_pos(middle, range, absolute, duration, timeout) + ''' + * @brief 控制机械臂在执行fc_start()函数后的末端力控过程中满足指定速度判断条件时自动停止当前运动函数并跳过后续运动函数, + * 直到fc_stop()函数被执行停止末端力控 + * @param middle 速度判断条件绝对值, X/Y/Z方向范围[-5, 5], 单位: m/s, RX/RY/RZ方向范围[-2*PI, 2*PI], 单位: rad/s + * @param range 速度判断条件偏移范围大小, X/Y/Z方向单位: m/s, RX/RY/RZ方向单位: rad/s + * @param absolute 绝对/增量条件判断标志位, true为绝对速度判断, false为增量速度判断 + * @param duration 条件满足触发保持时间, 单位: ms + * @param timeout 条件满足触发超时时间, 单位: ms + * @return 当前任务的id信息 + ''' + def fc_wait_vel(self, middle, range, absolute, duration, timeout): + return self.client.fc_wait_vel(middle, range, absolute, duration, timeout) + ''' + * @brief 控制机械臂在执行fc_start()函数后的末端力控过程中满足指定力判断条件时自动停止当前运动函数并跳过后续运动函数, + * 直到fc_stop()函数被执行停止末端力控 + * @param middle 力判断条件绝对值, 范围[-1000, 1000], X/Y/Z方向单位: N, RX/RY/RZ方向单位: Nm + * @param range 力判断条件偏移范围大小, X/Y/Z方向单位: N, RX/RY/RZ方向单位: Nm + * @param absolute 绝对/增量条件判断标志位, true为绝对力判断, false为增量力判断 + * @param duration 条件满足触发保持时间, 单位: ms + * @param timeout 条件满足触发超时时间, 单位: ms + * @return 当前任务的id信息 + ''' + def fc_wait_ft(self, middle, range, absolute, duration, timeout): + return self.client.fc_wait_ft(middle, range, absolute, duration, timeout) + ''' + * @brief 控制机械臂在执行fc_start()函数后的末端力控过程中位置条件判断、速度条件判断与力条件判断间的逻辑关系.不配置时默认三个条件判断都禁用 + * @param value 三维整形列表, 0代表不启用, 1代表与逻辑, 2代表或逻辑.例如开启位置条件判断, 禁用速度条件判断, 开启力条件判断, 并且位置与力的关系为或, 则输入[1,0,2] + * @return 当前任务的id信息 + ''' + def fc_wait_logic(self, value): + return self.client.fc_wait_logic(value) + ''' + * @brief 获取当前机器人末端传感器的反馈读数 + * @return 6自由度末端力读数, X/Y/Z方向单位: N, RX/RY/RZ方向单位: Nm + ''' + def fc_get_ft(self): + return self.client.fc_get_ft() + ''' + * @brief 获取当前机器人末端力控功能启用状态 + * @return 机器人末端力控启用返回true, 未启用返回false + ''' + def fc_mode_is_active(self): + return self.client.fc_mode_is_active() + ''' + * @brief 控制机械臂开启速度优化功能.开启该功能后, 在满足系统约束前提下, + * 机械臂以尽可能高的速度跟踪路径 + * @return 当前任务结束时的状态 + ''' + def enable_speed_optimization(self): + return self.client.enable_speed_optimization() + ''' + * @brief 控制机械臂退出速度优化 + * @return 当前任务结束时的状态 + ''' + def disable_speed_optimization(self): + return self.client.disable_speed_optimization() + ''' + * @brief 将一组points点位信息输入到机器人控制器中的轨迹池 + * @param track 一组points点位信息.每个point以6个double类型数据构成 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def trackEnqueue(self, track, block): + return self.client.trackEnqueue(track, block) + ''' + * @brief 将机器人控制器中的轨迹池清空 + * @return 当前任务结束时的状态 + ''' + def trackClearQueue(self): + return self.client.trackClearQueue() + ''' + * @brief 获取机器人控制器中的当前轨迹池大小 + * @return 当前轨迹池大小 + ''' + def getQueueSize(self): + return self.client.getQueueSize() + ''' + * @brief 执行时, 机器人的各关节将顺序到达轨迹池中的点位值直到轨迹池中无新的点位. + * 执行过程中, 主导关节(关节位置变化最大的关节)将以speed与acc规划运动, 其他关节按比例缩放. + * 注:如果已经开始执行停止规划, 将不再重新获取轨迹池中的数据, 直到完成停止. + * 停止后如果轨迹池中有新的点位, 将重新执行跟随.为保证运动连续性, 建议至少保证轨迹池中有10个数据 + * @param speed 最大关节速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param acc 最大关节加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def trackJointMotion(self, speed, acc, block): + return self.client.trackJointMotion(speed, acc, block) + ''' + * @brief 执行时, 机器人的工具末端tool将顺序到达轨迹池中的点位值直到轨迹池中无新的点位. + * 执行过程中, 工具末端tool将以speed与acc在工件坐标系wobj下规划运动. + * 注:如果已经开始执行停止规划, 将不再重新获取轨迹池中的数据, 直到完成停止. + 停止后如果轨迹池中有新的点位, 将重新执行跟随.为保证运动连续性, 建议至少保证轨迹池中有10个数据 + * @param speed 最大末端速度, 范围[0.00001, 5], 单位: m/s + * @param acc 最大末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param tool 设置使用的工件坐标系的名称, 为空字符串时默认为当前使用的工件坐标系 + * @param wobj 设置使用的工具的名称,为空字符串时默认为当前使用的工具 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def trackCartMotion(self, speed, acc, block, tool, wobj): + return self.client.trackCartMotion(speed, acc, block, tool, wobj, 0.005) + ''' + * @brief 确保机器人远程连接断开时, 机器人自动产生一条stop指令以停止当前运动.使用该函数需要单独创建一个线程周期性调用 + * @param time 心跳延时时间, 单位: ms + ''' + def rpc_heartbeat(self,time=1000): + return self.client.rpc_heartbeat(time) + ''' + * @brief 通过参数或者结束点两种设置方式, 在笛卡尔空间做螺旋轨迹运动 + * @param p1 螺旋线中心点位姿 + * @param p2 螺旋线的目标点位姿, 参数设置模式时不参考此参数 + * @param rev 总旋转圈数, rev < 0, 表示顺时针旋转;rev > 0, 表示逆时针旋转 + * @param len 轴向移动距离, 正负号遵循右手定则, 结束点设置模式时不参考此参数, 单位: m + * @param r 目标点半径, 结束点设置模式时不参考此参数, 单位: m + * @param mode 螺旋线示教模式, 0:参数设置, 1:结束点设置 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param q_near 目标点位置对应的关节角度, 用于确定逆运动学选解, 单位: rad + * @param tool 设置使用的工具的名称, 为空字符串时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空字符串默认为当前使用的工件坐标系 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明,可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def move_spiral(self, p1, p2, rev, len, r, mode, v, a, q_near, tool, wobj, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.move_spiral(p1, p2, rev, len, r, mode, v, a, q_near, tool, wobj, block, op, def_acc) + ''' + * @brief 控制机械臂开启加速度优化功能.开启该功能后, 系统会根据机器人动力学模型、电功率模型计算得到最优加速度大小, + * 在满足速度约束前提下, 机械臂以尽可能高的加速度进行规划.当速度优化同时打开后, 该函数不起作用 + * @return 当前任务结束时的状态 + ''' + def enable_acc_optimization(self): + return self.client.enable_acc_optimization() + ''' + * @brief 控制机械臂退出加速度优化 + * @return 当前任务结束时的状态 + ''' + def disable_acc_optimization(self): + return self.client.disable_acc_optimization() + ''' + * @brief 设置控制柜上的模拟电压输出 + * @param num 控制柜上的模拟电压通道序号, 范围从1-4 + * @param value 设置的模拟电压值 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_standard_analog_voltage_out(self, num, value, block): + return self.client.set_standard_analog_voltage_out(num, value, block) + ''' + * @brief 设置控制柜上的模拟电流输出 + * @param num 控制柜上的模拟电流通道序号, 范围从1-4 + * @param value 设置的模拟电流值 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_standard_analog_current_out(self, num, value, block): + return self.client.set_standard_analog_current_out(num, value, block) + ''' + * @brief 设置485的波特率 + * @param value 波特率 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_baudrate_485(self, value, block): + return self.client.set_baudrate_485(value, block) + ''' + * @brief 设置CAN的波特率 + * @param value 波特率 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_baudrate_can(self, value, block): + return self.client.set_baudrate_can(value, block) + ''' + * @brief set_analog_output_mode + * @param num + * @param mode + * @param block + * @return + ''' + def set_analog_output_mode(self, num, mode, block): + return self.client.set_analog_output_mode(num, mode, block) + ''' + * @brief 判断机器人是否在运动 + * @return True:机器人在运动, False:机器人没有运动 + ''' + def robotmoving(self): + return self.client.robotmoving() + ''' + * @brief 对多线圈进行写操作 + * @param slave_num modbus节点号 + * @param name modbus节点名 + * @param len 需要写入数据的线圈长度 + * @param byte_list 需要写入的数据 + * @return 任务结束时状态 + ''' + def modbus_write_multiple_coils(self, slave_num, name, len, byte_list): + return self.client.modbus_write_multiple_coils(slave_num, name, len, byte_list) + ''' + * @brief 对多寄存器进行写操作 + * @param slave_num modbus节点号 + * @param name modbus节点名 + * @param len 需要写入数据的寄存器长度 + * @param word_list 需要写入的数据 + * @return 任务结束时状态 + ''' + def modbus_write_multiple_regs(self, slave_num, name, len, word_list): + return self.client.modbus_write_multiple_regs(slave_num, name, len, word_list) + ''' + * @brief 获取当前工程的路径 + * @param project_path 当前工程路径 + ''' + def get_current_project(self): + return self.client.get_current_project() + ''' + * @brief 获取指定路径下的文件列表 + * @param fileslist 文件列表和类型;0:文件夹;1:文件 + * @param path 当前工程路径 + ''' + def get_files_list(self, path): + return self.client.get_files_list(path) + ''' + * @brief 获取当前RPC库的版本号 + * @return 当前RPC库的版本号 + ''' + def get_version(self): + return "4.4.0" + ''' + * @brief 获取机器人当前的位姿等信息 + * @param status 机器人位姿等信息 + ''' + def getRobotStatus(self): + return self.client.getRobotStatus() + ''' + * @brief 获取机器人当前IO和寄存器信息 + * @param status 当前IO和寄存器信息 + ''' + def getRobotIOStatus(self): + return self.client.getRobotIOStatus() + ''' + * @brief 任意工具坐标系参考任意工件坐标系笛卡尔指令位置 + * @param _return 笛卡尔指令位置 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 工件坐标系名称, 为空字符串默认为当前使用的坐标系 + ''' + def get_tcp_pose_command_coord(self, tool, wobj): + return self.client.get_tcp_pose_command_coord(tool, wobj) + ''' + * @brief 任意工具坐标系参考任意工件坐标系笛卡尔指令速度 + * @param _return 笛卡尔指令速度 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 工件坐标系名称, 为空字符串默认为当前使用的坐标系 + ''' + def get_tcp_speed_command_coord(self, tool, wobj): + return self.client.get_tcp_speed_command_coord(tool, wobj) + ''' + * @brief 任意工具坐标系参考任意工件坐标系笛卡尔指令加速度 + * @param _return 笛卡尔指令加速度 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 工件坐标系名称, 为空字符串默认为当前使用的坐标系 + ''' + def get_tcp_acceleration_command_coord(self, tool, wobj): + return self.client.get_tcp_acceleration_command_coord(tool, wobj) + + def restart(self, block): + return self.client.restart(block) + + ''' + * @brief 获取末端法兰在工具坐标系和工件坐标系下的位姿 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 工件坐标系名称, 为空字符串默认为当前使用的坐标系 + * @return 末端法兰的位姿 + ''' + def get_tcp_pose_coord(self, tool, wobj): + return self.client.get_tcp_pose_coord(tool, wobj) + ''' + * @brief 任意工具坐标系参考任意工件坐标系笛卡尔实际速度 + * @param _return 笛卡尔实际速度 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 工件坐标系名称, 为空字符串默认为当前使用的坐标系 + ''' + def get_tcp_speed_coord(self, tool, wobj): + return self.client.get_tcp_speed_coord(tool, wobj) + ''' + * @brief 任意工具坐标系参考任意工件坐标系笛卡尔实际加速度 + * @param _return 笛卡尔实际加速度 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 工件坐标系名称, 为空字符串默认为当前使用的坐标系 + ''' + def get_tcp_acceleration_coord(self, tool, wobj): + return self.client.get_tcp_acceleration_coord(tool, wobj) + ''' + * @brief 获取机械臂工具末端在工具坐标系下的力矩信息 + * @param tool 工具坐标系名称, 默认为当前使用的坐标系 + * @return 末端力矩信息, [Fx,Fy,Fz,Mx,My,Mz],单位: N、N.m + ''' + def get_tcp_force_tool(self, tool): + return self.client.get_tcp_force_tool(tool) + ''' + * @brief 修改伺服参数 + * @param axis_num 关节索引号 + * @param id 参数的ID号 + * @param value 要设置的值 + * @param qfmt 要设置的qfmt值 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def set_servo_config(self, axis_num, id, value, qfmt): + return self.client.set_servo_config(axis_num, id, value, qfmt) + ''' + * @brief 将伺服参数应用到实际控制 + * @param axis_num 关节索引号 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def apply_servo_config(self, axis_num, block): + return self.client.apply_servo_config(axis_num, block) + ''' + * @brief 获取电机极对数 + * @return 电机极对数 + ''' + def get_motor_pole_pair_number(self): + return self.client.get_motor_pole_pair_number() + ''' + * @brief 获取机器人轴电机定子插槽编号 + * @return 轴电机定子插槽编号 + ''' + def get_motor_stator_slots(self): + return self.client.get_motor_stator_slots() + ''' + * @brief 获取机器人轴减速器比率 + * @return 轴减速器比率 + ''' + def get_axis_ratio(self): + return self.client.get_axis_ratio() + ''' + * @brief 重置碰撞检测警告 + * @return 任务结束时状态 + ''' + def collision_detection_reset(self): + return self.client.collision_detection_reset() + ''' + * @brief 控制机械臂在关节或者笛卡尔空间做点动 + * @param param Jog运动的相关参数, 参考MoveJogTaskParams + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def move_jog(self, param, block): + return self.client.move_jog(param, block) + ''' + * @brief 结束机械臂的关节或者笛卡尔Jog + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def stop_manual_move(self, block): + return self.client.stop_manual_move(block) + ''' + * @brief 获取机器人控制器的软件版本号 + * @return 机器人控制器的软件版本号 + ''' + def get_robot_version(self): + return self.client.get_robot_version() + ''' + * @brief 启用或禁用示教器的物理按键 + * @param enable true:启动示教器物理按键, false:禁用示教器物理按键 + * @return 任务结束时状态 + ''' + def set_teach_pendant(self, enable): + return self.client.set_teach_pendant(enable) + ''' + * @brief 获取示教速度的百分比 + * @return 示教速度的百分比 + ''' + def get_teach_speed(self): + return self.client.get_teach_speed() + ''' + * @brief 获取全局速度的百分比 + * @return 全局速度的百分比 + ''' + def get_global_speed(self): + return self.client.get_global_speed() + ''' + * @brief 设置示教速度的百分比 + * @param v 示教速度的百分比, 范围[1,100] + * @return 任务结束时状态 + ''' + def set_teach_speed(self, v): + return self.client.set_teach_speed(v) + ''' + * @brief 设置复合运动的相关参数 + * @param type 复合运动类型.1:平面三角形轨迹, 2:平面正旋轨迹, + * 3:平面圆形轨迹, 4:平面梯形轨迹, 5:平面8字形轨迹, 6:自定义轨迹, 7:月牙形轨迹, 8:钟摆形轨迹 + * @param ref_plane 参考平面, 0:工具XOY, 1:工具XOZ + * @param fq 频率, 单位: Hz + * @param amp 振幅, 单位: m; 当运动类型是钟摆时, 该参数是摆动角, 单位:rad + * @param el_offset 仰角偏移, 单位: m + * @param az_offset 方向角偏移, 单位: m + * @param up_height 中心隆起高度, 单位: m + * @param time 左右停留时间 + * @param path_dwell 主路径同步停留, 可缺省, 默认参数是false + * @param pendulum_height 钟摆高度, 单位: m, 可缺省, 默认参数是0 + * @param op_list 二维的OP参数列表, 可缺省, 默认参数是空vector + * @return 任务结束时状态 + ''' + def combine_motion_config(self, type, ref_plane, fq, amp, el_offset, az_offset, up_height, time, path_dwell = False, pendulum_height = 0, op_list=[]): + return self.client.combine_motion_config(type, ref_plane, fq, amp, el_offset, az_offset, up_height, time, path_dwell, pendulum_height, op_list) + ''' + * @brief 开启复合运动 + * @return 任务结束时状态 + ''' + def enable_combine_motion(self): + return self.client.enable_combine_motion() + ''' + * @brief 结束复合运动 + * @return 任务结束时状态 + ''' + def disable_combine_motion(self): + return self.client.disable_combine_motion() + ''' + * @brief 开启机械臂奇异点规避功能 + * @return 任务结束时状态 + ''' + def enable_singularity_control(self): + return self.client.enable_singularity_control() + ''' + * @brief 关闭机械臂奇异点规避功能 + * @return 任务结束时状态 + ''' + def disable_singularity_control(self): + return self.client.disable_singularity_control() + ''' + * @brief 启动外部轴方案 + * @param scheme_name 外部轴方案名称 + * @return 任务结束时的状态 + ''' + def enable_eaxis_scheme(self, scheme_name): + return self.client.enable_eaxis_scheme(scheme_name) + ''' + * @brief 结束外部轴方案 + * @param scheme_name 外部轴方案名称 + * @return 任务结束时的状态 + ''' + def disable_eaxis_scheme(self, scheme_name): + return self.client.disable_eaxis_scheme(scheme_name) + ''' + * @brief 控制外部轴移动 + * @param scheme_name 目标外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), + * 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效),可缺省参数 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def move_eaxis(self, scheme_name, epose, v, block, op = None): + if(op is None): + op = Op() + return self.client.move_eaxis(scheme_name, epose, v, block, op) + ''' + * @brief 控制外部轴和机器人执行关节运动 + * @param joints_list 目标关节位置, 单位: rad + * @param v 关节角速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param a 关节加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param rad 融合半径, 单位: m + * @param scheme_name 目标外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), + * 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param eaxis_v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效), 可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movej2_eaxis(self, joints_list, v, a, rad, scheme_name, epose, eaxis_v, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movej2_eaxis(joints_list, v, a, rad, scheme_name, epose, eaxis_v, block, op, def_acc) + ''' + * @brief 控制外部轴和机器人从当前状态, 按照关节运动的方式移动到末端目标位置 + * @param p 对应末端的位姿, 位置单位: m, 姿态以Rx、Ry、Rz表示, 范围[-2*PI, 2*PI], 单位: rad + * @param v 关节角速度, 范围[0.01*PI/180, 1.25*PI], 单位: rad/s + * @param a 关节加速度, 范围[0.01*PI/180, 12.5*PI], 单位: rad/s^2 + * @param rad 融合半径, 单位: m + * @param qnear 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param scheme_name 目标外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param eaxis_v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效),可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movej2_pose_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movej2_pose_eaxis(p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc) + ''' + * @brief 控制外部轴和机器人从当前状态按照直线路径移动到目标状态 + * @param p 对应末端的位姿, 位置单位: m, 姿态以Rx、Ry、Rz表示, 范围[-2*PI, 2*PI], 单位: rad + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param rad 融合半径, 单位: m + * @param qnear 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param scheme_name 目标外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param eaxis_v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效),可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movel_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.movel_eaxis(p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc) + ''' + * @brief 控制外部轴和机器人做圆弧运动, 起始点为当前位姿点, 途径p1点, 终点为p2点 + * @param p1 圆弧运动中间点位姿 + * @param p2 圆弧运动结束点位姿 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param rad 融合半径, 单位: m + * @param qnear 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param scheme_name 目标外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param eaxis_v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效),可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @param mode 姿态控制模式 0:姿态与终点保持一致;1:姿态与起点保持一致;2:姿态受圆心约束, 可缺省参数, 默认是0 + * @param arc_rad 圆弧弧度, 为0时运动到p2结束点, 按照原始圆弧规划, 其余情况按照数据圆弧角规划(此模式姿态仅支持起点一致和受圆心约束), 单位: rad. 可缺省, 默认为0 + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def movec_eaxis(self, p1, p2, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op = None, def_acc = False, mode = 0, arc_rad = 0): + if(op is None): + op = Op() + return self.client.movec_eaxis(p1, p2, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc, mode, arc_rad) + ''' + * @brief 控制机械臂做圆周运动, 起始点为当前位姿点, 途径p1点和p2点 + * @param p1 圆周运动经过点 + * @param p2 圆周运动经过点 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param rad 融合半径, 单位: m + * @param mode 姿态控制模式 0:姿态与终点保持一致;1:姿态与起点保持一致;2:姿态受圆心约束 + * @param qnear 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param scheme_name 目标外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param eaxis_v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效),可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def move_circle_eaxis(self, p1, p2, v, a, rad, mode, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.move_circle_eaxis(p1, p2, v, a, rad, mode, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc) + + ''' + * @brief 可达性检查 + * @param base 基坐标在世界坐标系中的位置 + * @param wobj 工件坐标系在世界坐标系中的位置 + * @param tool 工具坐标系在法兰坐标系中的描述 + * @param ref_pos 机器人关节参考角度 + * @param check_points 需要确认可达性检查的点位列表 + * @return 可达性确认结果 + ''' + def reach_check(self, base, tool, wobj, ref_pos, check_points): + return self.client.reach_check(base, tool, wobj, ref_pos, check_points) + ''' + * @brief 控制外部轴和机器人执行点动 + * @param name 目标外部轴的轴序号 + * @param direction 运动方向, -1:负方向, 1:正方向 + * @param vel 速度百分比 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def move_jog_eaxis(self, name, direction, vel, block): + return self.client.move_jog_eaxis(name, direction, vel, block) + ''' + * @brief 获取外部轴的当前信息 + * @param info 当前位置, 状态, 速度, 加速度 + ''' + def get_eaxis_info(self): + return self.client.get_eaxis_info() + ''' + * @brief 设置牵引时的参数 + * @param space 牵引类型 + * @param joint_scale 关节柔顺度 + * @param cart_scale 笛卡尔柔顺度 + * @param coord_type 类型 + * @param direction 牵引方向激活 + * @param global_scale 全局牵引柔顺度 + * @return 任务结束时的状态 + ''' + def set_hand_teach_parameter(self, space, joint_scale, cart_scale, coord_type, direction, global_scale): + return self.client.set_hand_teach_parameter(space, joint_scale, cart_scale, coord_type, direction, global_scale) + ''' + * @brief 示教器按键的jog类型 + * @param type 1:关节, 2:笛卡尔 + * @return 任务结束时的状态 + ''' + def set_pendant_type(self, type): + return self.client.set_pendant_type(type) + ''' + * @brief 开启末端震动抑制功能 + * @return 任务结束时状态 + ''' + def enable_vibration_control(self): + return self.client.enable_vibration_control() + ''' + * @brief 关闭末端震动抑制功能 + * @return 任务结束时状态 + ''' + def disable_vibration_control(self): + return self.client.disable_vibration_control() + ''' + * @brief 设置融合预读取配置 + * @param per 百分比(%) + * @param num 预读取运动脚本数量 + * @return 任务结束时的状态 + ''' + def set_blend_ahead(self, per, num=1): + return self.client.set_blend_ahead(per,num) + ''' + * @brief 开启实时控制模式 (不支持) + * @param mode 实时控制模式, 1:关节位置, 2:关节速度, 3:空间位置, 4:空间速度 + * @param filter_bandwidth 实时控制指令滤波器带宽, 单位Hz, 默认100Hz + * @param com_lost_time 实时控制通讯数据丢失监控保护时间, 单位s, 默认0.02s + * @return 任务结束时的状态 + ''' + def start_realtime_mode(self, mode, fileter_bandwidth, com_lost_time): + return self.client.start_realtime_mode(mode, fileter_bandwidth, com_lost_time) + ''' + * @brief 结束实时控制模式 (不支持) + * @return 任务结束时的状态 + ''' + def end_realtime_mode(self): + return self.client.end_realtime_mode() + ''' + * @brief 实时数据入队 (不支持) + * @param realtime_data 实时数据 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 任务结束时的状态 + ''' + def realtime_data_enqueue(self, realtime_data, block): + return self.client.realtime_data_enqueue(realtime_data, block) + ''' + * @brief 清空实时数据队列 (不支持) + * @return 任务结束时的状态 + ''' + def clear_realtime_data_queue(self): + return self.client.clear_realtime_data_queue() + ''' + * @brief 获取当前实时队列池数据的数量 (不支持) + * @return 当前实时队列池数据的数量 + ''' + def get_realtime_data_queue_size(self): + return self.client.get_realtime_data_queue_size() + ''' + * @brief 样条运动函数, 控制机器人按照空间样条进行运动, 在运动过程中触发对应点位的OP操作 + * @param pose_list 在设置工件坐标系下的末端位姿和OP列表, 最多不超过50个点 + * @param v 末端速度, 范围[0.00001, 5], 单位: m/s + * @param a 末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param tool 设置使用的工具的名称, 为空时默认为当前使用的工具 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param block 是否阻塞, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 可缺省参数 + * @param r 融合半径, 可缺省参数, 单位: m, 默认值为 0, 表示无融合.当数值大于0时表示与下一条运动融合 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def spline_op(self, pose_list, v, a, tool, wobj, block, op = None, r = 0, def_acc = False): + if(op is None): + op = Op() + return self.client.spline_op(pose_list, v, a, tool, wobj, block, op, r, def_acc) + ''' + * @brief 将一组points点位和该点位下的OP信息输入到机器人控制器中的轨迹池, 在运动过程中触发对应点位的OP操作 + * @param track 点位信息和该点位下的OP列表. + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def trackEnqueueOp(self, track, block): + return self.client.trackEnqueueOp(track, block) + ''' + * @brief 手自动模式切换 + * @param mode 0:手动模式, 1:自动模式. + * @return 阻塞执行代表任务结束时状态 + ''' + def switch_mode(self, mode): + return self.client.switch_mode(mode) + ''' + * @brief 获取外接编码器的CNT值 + * @return 外接编码器的CNT值 + ''' + def read_encoder_count(self): + return self.client.read_encoder_count() + ''' + * @brief 获取机器人零位 + * @return 机器人零位 + ''' + def get_pos_bias(self): + return self.client.get_pos_bias() + ''' + * @brief 设置boolean类型系统变量 + * @param name 变量名称 + * @param value 变量的值 + * @return 任务结束时的状态 + ''' + def set_system_value_bool(self, name, value): + return self.client.set_system_value_bool(name, value) + ''' + * @brief 设置number类型系统变量 + * @param name 变量名称 + * @param value 变量的值 + * @return 任务结束时的状态 + ''' + def set_system_value_double(self, name, value): + return self.client.set_system_value_double(name, value) + ''' + * @brief 设置string类型系统变量 + * @param name 变量名称 + * @param value 变量的值 + * @return 任务结束时的状态 + ''' + def set_system_value_str(self, name, value): + return self.client.set_system_value_str(name, value) + ''' + * @brief 设置pose_list/joint_list类型系统变量 + * @param name 变量名称 + * @param value 变量的值 + * @return 任务结束时的状态 + ''' + def set_system_value_list(self, name, value): + return self.client.set_system_value_str(name, value) + ''' + * @brief 获取boolean类型系统变量 + * @return boolean类型系统变量名称 + ''' + def get_system_value_bool(self, name): + return self.client.get_system_value_bool(name) + ''' + * @brief 获取number类型系统变量 + * @return number类型系统变量名称 + ''' + def get_system_value_double(self, name): + return self.client.get_system_value_double(name) + ''' + * @brief 获取string类型系统变量 + * @return string类型系统变量名称 + ''' + def get_system_value_str(self, name): + return self.client.get_system_value_str(name) + ''' + * @brief 获取pose_list/joint_list类型系统变量 + * @return pose_list/joint_list类型系统变量名称 + ''' + def get_system_value_lists(self, name): + return self.client.get_system_value_lists(name) + ''' + * @brief 当前机器人型号原始DH参数 + * @return 原始DH参数, 参数顺序a, alpha, d, theta, 单位m/rad + ''' + def get_origin_DH(self): + return self.client.get_origin_DH() + ''' + * @brief 当前机器人型号标定补偿后DH参数 + * @return 补偿后DH参数, 参数顺序a, alpha, d, theta, 单位m/rad + ''' + def get_calib_DH(self): + return self.client.get_calib_DH() + ''' + * @brief 获取机器人系列号, 型号, ext, SN + * @param type 机器人系列号, 型号, ext, SN + ''' + def get_robot_type(self): + return self.client.get_robot_type() + ''' + * @brief 获取关节外力矩 + * @param torque 关节外力矩 + ''' + def get_ext_torque(self): + return self.client.get_ext_torque() + ''' + * @brief 修改SJxx-xx-x.json文件的Fric值和零位 + * @param params Fric值和零位 + ''' + def set_dynamic_calibration_params(self, params): + return self.client.set_dynamic_calibration_params(params) + ''' + * @brief 获取SJxx-xx-x.json文件的Fric值和零位 + * @param params Fric值和零位 + ''' + def get_dynamic_calibration_params(self): + return self.client.get_dynamic_calibration_params() + ''' + * @brief 同步机器人参数到末端 + * @param passwd 密码 + ''' + def upload_robot_param_to_toolboard(self, passwd): + return self.client.upload_robot_param_to_toolboard(passwd) + ''' + * @brief 修改机器人DH参数 + * @param params 机器人DH参数 a alpha d beta + ''' + def set_kinematic_calibration_params(self, params): + return self.client.set_kinematic_calibration_params(params) + ''' + * @brief 设置运动学标定识别码 + * @param passwd 密码 + * @param version 版本号 + ''' + def set_kinematic_calibration_info(self, passwd,version): + return self.client.set_kinematic_calibration_info(passwd,version) + ''' + * @brief 设置动力学标定识别码 + * @param passwd 密码 + * @param version 版本号 + ''' + def set_dynamic_calibration_info(self, passwd,version): + return self.client.set_dynamic_calibration_info(passwd,version) + ''' + * @brief 设置震动标定识别码 + * @param passwd 密码 + * @param version 版本号 + ''' + def set_vibration_calibration_info(self, passwd,version): + return self.client.set_vibration_calibration_info(passwd,version) + ''' + * @brief 获取机器人轴减速器比率 + * @param _return 轴减速器比率 + ''' + def get_axis_motor_rated_current(self): + return self.client.get_axis_motor_rated_current() + ''' + * @brief 获取机器人轴减速器比率 + * @param _return 轴减速器比率 + ''' + def get_axis_motor_kt(self): + return self.client.get_axis_motor_kt() + ''' + * @brief 中止当前正在执行的运动任务. 如果还提前预读取了下一条或多条运动指令进行融合, 此时预读取的指令同样会被中止 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def abort(self,block): + return self.client.abort(block) + ''' + * @brief 获取震动参数 + * @param params 震动参数 + ''' + def get_vibration_calibration_params(self): + return self.client.get_vibration_calibration_params() + ''' + * @brief 保存运动学参数到文件 + * @param passwd 密码 + ''' + def save_kinematic_calibration_params(self, passwd): + return self.client.save_kinematic_calibration_params(passwd) + ''' + * @brief 保存动力学参数到文件 + * @param passwd 密码 + ''' + def save_dynamic_calibration_params(self, passwd): + return self.client.save_dynamic_calibration_params(passwd) + ''' + * @brief 清空保存信息并关闭弹窗 + ''' + def clear_error_message(self): + return self.client.clear_error_message() + ''' + * @brief 计算目标运动类型从指定位置以某一几何路径运动到目标位置在指定最大速度/最大加速度约束下所需要的时间 + * @param param 参考TrajTimeParam + * @return 小于0: 参数不匹配, 等于0: 点位异常, 大于0: 用时 + ''' + def cal_traj_time(self, param): + return self.client.cal_traj_time(param) + ''' + * @brief 获取主板硬件时钟, 精确到ms + * @return 主板硬件时钟, 单位: ms + ''' + def get_hardware_clock(self): + return self.client.get_hardware_clock() + ''' + * @brief 获取工具坐标系值 + * @param _return 工具坐标系值(长度:10) + * @param name 为空: 返回当前坐标系的值, 不为空: 返回指定工具坐标系的值 + ''' + def get_tool_data(self, name = ""): + return self.client.get_tool_data(name) + ''' + * @brief 获取当前设置工具的负载质量及质心位置 + * @param _return 质量单位: kg,质心位置单位: m,[mass,x_cog,y_cog,z_cog] + * @param name 为空: 返回当前的值, 不为空: 返回指定的值 + ''' + def get_tool_load(self, name = ""): + return self.client.get_tool_load(name) + ''' + * @brief 获取当前设置的工件坐标系的值 + * @param _return [x, y, z, rx, ry, rz]工件坐标系相对于基坐标系的位移, 单位: m, rad + ''' + def get_wobj(self, name = ""): + return self.client.get_wobj(name) + ''' + * @brief 获取机器人安装位姿 + * @param _return 安装位姿 + ''' + def get_base(self): + return self.client.get_base() + ''' + * @brief 获取Home位置 + * @param _return Home位置关节角 + ''' + def get_home_pose(self): + return self.client.get_home_pose() + ''' + * @brief 设置工具末端相对于法兰面坐标系的偏移及质量、质心, 设置成功后, 后续运动函数中的TCP设置为该TCP或者为空时,使用该TCP参数,此函数会将工程文件中的值同步修改 + * @param name 工具坐标系的名称 + * @param tool 工具TCP偏移量 {x_off, y_off, z_off, rx, ry, rz}, 单位(m, rad) + * @param payload 末端工具质量, 质心, {mass, x_cog, y_cog, z_cog}, 相对于法兰坐标系, 单位(kg, m) + * @param inertia_tensor 末端工具惯量矩阵参数, 参数1-6分别对应矩阵xx、xy、xz、yy、yz、zz元素, 单位kg*m^2 + * @return 任务结束时的状态 + ''' + def set_tool_data_workcell(self, name, tool, payload, inertia_tensor): + return self.client.set_tool_data_workcell(name, tool, payload, inertia_tensor) + ''' + * @brief 根据名称修改要配置的工件坐标系, 此函数会将工程文件中的值同步修改 + * @param name 工件坐标系的名称 + * @param wobj {x, y, z, rx, ry, rz}, 单位(m, rad) + * @return 任务结束时的状态 + ''' + def set_wobj_workcell(self, name, wobj): + return self.client.set_wobj_workcell(name, wobj) + ''' + * @brief 同步机器人参数到末端 + * @param passwd 密码 + ''' + def upload_robot_param_to_toolboard(self, passwd): + return self.client.upload_robot_param_to_toolboard(passwd) + ''' + * @brief 修改机器人DH参数 + * @param params 机器人DH参数 a alpha d beta + ''' + def set_kinematic_calibration_params(self, params): + return self.client.set_kinematic_calibration_params(params) + ''' + * @brief 设置运动学标定识别码 + * @param passwd 密码 + ''' + def set_kinematic_calibration_info(self, passwd): + return self.client.set_kinematic_calibration_info(passwd) + ''' + * @brief 设置动力学标定识别码 + * @param passwd 密码 + ''' + def set_dynamic_calibration_info(self, passwd): + return self.client.set_dynamic_calibration_info(passwd) + ''' + * @brief 获取机器人轴减速器比率 + * @param _return 轴减速器比率 + ''' + def get_axis_motor_rated_current(self): + return self.client.get_axis_motor_rated_current() + ''' + * @brief 获取机器人轴减速器比率 + * @param _return 轴减速器比率 + ''' + def get_axis_motor_kt(self): + return self.client.get_axis_motor_kt() + ''' + * @brief 原地摆 + * @param tool 工具坐标系名称, 为空字符串默认为当前使用的坐标系 + * @param wobj 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param axis 复合运动参考轴向: 0为参考平面轴向1, 1为参考平面轴向2. 例如XOY 0代表X轴 1代表Y轴 + * @param num 延迟时间参数 单位ms 当且仅当eaxis_scheme_name为空时生效 + * @param scheme_name 外部轴方案名称 + * @param epose 目标外部轴方案所对应自由度位置(三维), + * 记录位置自由度及单位根据外部轴方案所设置自由度及外部轴方案类型改变, 单位: rad, m + * @param eaxis_v 外部轴最大规划速度, 根据对应外部轴方案类型改变, 单位: rad/s, m/s + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @param op 详见上方Op特殊类型说明(距离触发无效),可缺省参数 + * @param def_acc 是否使用系统默认加速度, false表示使用自定义的加速度值, true表示使用系统自动规划的加速度值, 可缺省, 默认为false + * @return 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. + * 当配置为非阻塞执行, 返回值代表当前任务的id, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + ''' + def move_fixed(self, tool, wobj, axis, num, scheme_name, epose, eaxis_v, block, op = None, def_acc = False): + if(op is None): + op = Op() + return self.client.move_fixed(tool, wobj, axis, num, scheme_name, epose, eaxis_v, block, op, def_acc) + ''' + * @brief 将一组points点位及对应速度(m/s)、融合时间(ms)、停留时间(ms)和该点位下的OP信息输入到机器人控制器中的轨迹池 + * @param track 点位信息和该点位下的OP列表 (PointOp 类型). + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def track_enqueue_op_vel(self, track, block): + return self.client.track_enqueue_op_vel(track, block) + ''' + * @brief 执行时, 机器人的工具末端tool将顺序到达轨迹池中的点位值直到轨迹池中无新的点位. + * 执行过程中, 工具末端tool将以speed与acc在工件坐标系wobj下规划运动. + * 注:如果已经开始执行停止规划, 将不再重新获取轨迹池中的数据, 直到完成停止. + 停止后如果轨迹池中有新的点位, 将重新执行跟随.为保证运动连续性, 建议至少保证轨迹池中有10个数据 + * @param acc 最大末端加速度, 范围[0.00001, ∞), 单位: m/s^2 + * @param tool 设置使用的工件坐标系的名称, 为空字符串时默认为当前使用的工件坐标系 + * @param wobj 设置使用的工具的名称,为空字符串时默认为当前使用的工具 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def track_cart_vel_motion(self, acc, tool, wobj, block): + return self.client.track_cart_vel_motion(acc, tool, wobj, block) + ''' + * @brief 按照输入的偏移量和和偏移方向 计算出偏移后的路径点序列 + * @param path_type 路径类型, 0:直线, 1:圆弧 2:直线、直线, 3:直线、圆弧, 4:直线、圆弧、直线, 5:圆弧、直线、圆弧, 6:参考输入路径类型队列(path_type) + * @param path_point 轨迹路点序列 + * @param tool 使用的工具坐标系的名称, 为空时默认为当前使用的工具坐标系 + * @param wobj 使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + * @param path_offset 路径在路径坐标系起点处的偏移 + * @param path_retract 路径在路径坐标系终点处的缩进 + * @param tool_rotation 工具自身参考tool的旋转量 + * @param path_rotation 工具参考路径坐标系起点处路径切线方向的旋转量 + * @param weld_rotation 相对于焊道坐标系的RY、RZ, 可缺省 + * @param tool_z_offset 工具z偏移量, 可缺省 + * @param path_type 路径类型序列, 可缺省 + * @param plane_normals 法向量, 可缺省 + * @param average_height 根据法向量高度平均, 可缺省 + * @return 偏移后的路径点序列 + ''' + def path_offset_cal(self, path_type, path_point, tool, wobj, path_offset, path_retract, tool_rotation, path_rotation, weld_rotation = {}, + tool_z_offset = 0, path_type_list = {}, plane_normals = {}, average_height = False): + return self.client.path_offset_cal(path_type, path_point, tool, wobj, path_offset, path_retract, tool_rotation, path_rotation, weld_rotation, + tool_z_offset, path_type_list, plane_normals, average_height) + ''' + * @brief 设置机械臂的安装方式 + * @param installation 安装方式。 两个元素[ry, rz],ry:机器人绕基座倾斜弧度, rz:机器人绕基座旋转弧度 + * @return 返回值代表当前任务结束时的状态 + ''' + def set_installation(self, installation): + return self.client.set_installation(installation) + ''' + * @brief 获取机械臂的安装方式 + * @return 当前安装方式。 两个元素[ry, rz],ry:机器人绕基座倾斜弧度, rz:机器人绕基座旋转弧度 + ''' + def get_installation(self): + return self.client.get_installation() + ''' + * @brief 开启激光跟踪功能 + * @return 非阻塞执行代表任务的ID + ''' + def enable_laser_track(self): + return self.client.enable_laser_track() + ''' + * @brief 设置激光跟踪功能相关参数 + * @param plane 补偿量在工具坐标系中所在平面, 0: X-Y平面, 1: X-Z平面, 2: Y-Z平面 + * @param freq 数据更新参考频率 + * @param lead_len 获取的偏差量在主路径上的提前距离, 单位m, (暂时不需要) + * @param active 补偿量方向开关, 参考对应补偿平面, 变量顺序为X-Y-Z中两个平面所在方向顺序, 0: 关闭, 1: 打开 + * @param kp 补偿方向上的控制kp系数 + * @param kd 补偿方向上的控制kd系数 + * @param offset 补偿方向上最大补偿偏移量, 单位m + * @param offset_vel 补偿方向上最大补偿速度, 单位m/s + * @param filter_coe 补偿方向上补偿量滤波系数 + * @param path_length_correction 路径长度修正 + * @param track_type true: 表示使用记录的数据 + * @return 任务结束时的状态 + ''' + def set_laser_track_params(self, plane, freq, lead_len, active, kp, kd, offset, offset_vel, filter_coe, path_length_correction, track_type): + return self.client.set_laser_track_params(plane, freq, lead_len, active, kp, kd, offset, offset_vel, filter_coe, path_length_correction, track_type) + ''' + * @brief 关闭激光跟踪功能 + * @return 任务结束时的状态 + ''' + def disable_laser_track(self): + return self.client.disable_laser_track() + ''' + * @brief 清空记录的偏移数据 + * @return 任务结束时的状态 + ''' + def reset_laser_track(self): + return self.client.reset_laser_track() + ''' + * @brief 添加基于世界坐标系偏移的点位 + * @param pos 点位 + * @return 任务结束时的状态 + ''' + def write_laser_search_pos(self, pos): + return self.client.write_laser_search_pos(pos) + ''' + * @brief 电弧跟踪系统参数设置 + * @param coeff_K 循环队列系数 单位:次, K≥2 数据采集长度系数,K个摆动周期内不进行跟踪 + * @param lag_time 滞后时间 单位:ms, t≥0, 电流反馈数据滞后时间,焊机不同,滞后时间不同 + * @param start_time 延迟跟踪时间 单位:ms, t≥0, 避免起弧开始阶段电流不稳,影响跟踪效果 + * @param sensi_coeff 敏感系数, 一行两列, 10≥x≥0.1, 调节相应的纠偏能力的参数,范围 0.1-10 之间。推荐敏感系数设置:0.3-1.0 + * @param diff_constant 电流差常量 x>0, 用于跟踪算法的常数,焊丝直径 1.2mm 时为12 + * @param diff_value 电流差设定值 单位:A i≥0, 用于电流差在设定值范围内不进行纠偏 + * @param track_type false:电流跟踪, true:电压跟踪 + * @param cal_lag_time 启用计算滞后时间 + * @param arc_direction 圆弧焊接方向,false:焊接和圆平面垂直,true:焊接和圆平面平行 + * @param ref_cycle_times 基准周期 6≥k≥1 默认值为1 + * @return + ''' + def set_arc_system_params(self, coeff_K, lag_time, start_time, sensi_coeff, diff_constant, diff_value, track_type, cal_lag_time, + arc_direction, ref_cycle_times = 1): + return self.client.set_arc_system_params(coeff_K, lag_time, start_time, sensi_coeff, diff_constant, diff_value, track_type, cal_lag_time, + arc_direction, ref_cycle_times) + ''' + * @brief 电弧跟踪参数设置 + * @param freq 数据更新参考频率 + * @param active 补偿量方向开关, 一行两列, 参考对应补偿平面, 变量顺序为X-Y-Z中两个平面所在方向顺序 + * @param kp 补偿方向上的控制kp系数, 一行两列 + * @param kd 补偿方向上的控制kd系数, 一行两列 + * @param max_displacement 补偿方向上最大补偿偏移量, 一行两列, 单位m + * @param max_displacement_vel 补偿方向上最大补偿速度, 一行两列, 单位m/s + * @param base_current 基准电流 + * @param track_type 跟踪方式, false:偏移量跟踪 true:电弧跟踪 + * @param max_displacement_cycle 单周期补偿方向上最大补偿偏移量,一行两列, 单位m + * @param constant_bias 左右差补偿量 (加在左峰上) + * @return + ''' + def set_arc_track_params(self, freq, active, kp, kd, max_displacement, max_displacement_vel, base_current, track_type, max_displacement_cycle, constant_bias): + return self.client.set_arc_track_params(freq, active, kp, kd, max_displacement, max_displacement_vel, base_current, track_type, max_displacement_cycle, constant_bias) + ''' + * @brief 启用电弧跟踪 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return + ''' + def enable_arc_track(self, block): + return self.client.enable_arc_track(block) + ''' + * @brief 关闭电弧跟踪 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return + ''' + def disable_arc_track(self, block): + return self.client.disable_arc_track(block) + ''' + * @brief 获取滞后时间 + * @return + ''' + def get_arc_track_lag_time(self): + return self.client.get_arc_track_lag_time() + ''' + * @brief 重置电弧跟踪的数据 + * @return + ''' + def reset_arc_track(self): + return self.client.reset_arc_track() + ''' + * @brief 添加当前电弧跟踪的数据 + * @param data 电流或电压 + * @return + ''' + def write_arc_current_data(self, data): + return self.client.write_arc_current_data(data) + ''' + * @brief 清除电弧跟踪的数据 + * @return + ''' + def clear_arc_current_data(self): + return self.client.clear_arc_current_data() + ''' + * @brief 开启负载辨识 + * @param block 指令是否阻塞型指令, 如果为false表示非阻塞指令, 指令会立即返回 + * @return 阻塞执行代表任务结束时状态, 非阻塞执行代表任务的ID + ''' + def start_load_identification(self, block): + return self.client.start_load_identification(block) + ''' + * @brief 获取指定外部轴方案的基坐标系 + * @param eaxis_scheme_name 目标外部轴方案名称列表 + * @return 目标外部轴方案的基坐标系 + ''' + def get_eaxis_coord(self, eaxis_scheme_name): + return self.client.get_eaxis_coord(eaxis_scheme_name) + ''' + * @brief 设置外部轴方案的基坐标系 + * @param eaxis_scheme_name 目标外部轴方案名称列表 + * @param coord_list 目标基坐标系列表 + * @return 任务执行结果 + ''' + def set_eaxis_coord(self, eaxis_scheme_name, coord_list): + return self.client.set_eaxis_coord(eaxis_scheme_name, coord_list) + ''' + * @brief 设置工具末端相对于基坐标系的偏移, 设置成功后, 后续运动函数中的TCP设置为该TCP或者为空时,使用该TCP参数 + * @param name 工具坐标系的名字 + * @param tool_offset 工具TCP偏移量 [x_off, y_off,z_off,rx,ry,rz], 单位: m, rad + * @param payload 末端负载质量, 质心, [mass,x_cog,y_cog,z_cog], 单位: kg, m + * @param inertia_tensor 末端工具惯量矩阵参数, 参数1-6分别对应矩阵xx、xy、xz、yy、yz、zz元素, 单位: kg*m^2 + * @return 返回当前任务结束时的状态 + ''' + def set_remote_tool_data(self, name, tool_offset, payload, inertia_tensor): + return self.client.set_remote_tool_data(name, tool_offset, payload, inertia_tensor) + ''' + * @brief 设置工具末端相对于基坐标系的偏移及质量、质心, 设置成功后, 后续运动函数中的TCP设置为该TCP或者为空时,使用该TCP参数,此函数会将工程文件中的值同步修改 + * @param name 工具坐标系的名称 + * @param tool_offset 工具TCP偏移量 {x_off, y_off,z_off,rx,ry,rz}, 单位(m, rad) + * @param payload 末端工具质量, 质心, {mass,x_cog,y_cog,z_cog}, 相对于法兰坐标系, 单位(kg, m) + * @param inertia_tensor 末端工具惯量矩阵参数, 参数1-6分别对应矩阵xx、xy、xz、yy、yz、zz元素, 单位kg*m^2 + * @return 任务结束时的状态 + ''' + def set_remote_tool_data_workcell(self, name, tool_offset, payload, inertia_tensor): + return self.client.set_remote_tool_data_workcell(name, tool_offset, payload, inertia_tensor) diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/__init__.py b/unilabos/devices/eit_agv/driver/DucoCobot/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/demo.py b/unilabos/devices/eit_agv/driver/DucoCobot/demo.py new file mode 100644 index 00000000..fba5b15d --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/demo.py @@ -0,0 +1,131 @@ +from array import array +import sys +import time +import threading + +sys.path.append('gen_py') +sys.path.append('lib') +from DucoCobot import DucoCobot +from thrift import Thrift +from thrift.transport import TSocket +from thrift.transport import TTransport +from thrift.protocol import TBinaryProtocol +from gen_py.robot.ttypes import StateRobot, StateProgram, OperationMode,TaskState,Op,MoveJogTaskParam,PointOp + + +ip='127.0.0.1' +stopheartthread = False + + +def hearthread_fun(): + duco_heartbeat = DucoCobot(ip, 7003) + duco_heartbeat.open() + while not stopheartthread: + duco_heartbeat.rpc_heartbeat() + time.sleep(1) + duco_heartbeat.close() + + +def thread_fun(): + duco_cobot = DucoCobot(ip, 7003) + # Connect! + duco_cobot.open() + while not stopheartthread: + tcp_pose = [] + tcp_pose = duco_cobot.get_robot_state() + print("state: ", tcp_pose) + time.sleep(1) + duco_cobot.close() + +def main(): + thd_A = threading.Thread(target=thread_fun) + thd_A.start() + thd_B = threading.Thread(target=hearthread_fun) + thd_B.start() + + duco_cobot = DucoCobot(ip,7003) + op = Op() + op.time_or_dist_1 = 0 + op.trig_io_1 = 1 + op.trig_value_1 = False + op.trig_time_1 = 0.0 + op.trig_dist_1 = 0.0 + op.trig_event_1 = '' + op.time_or_dist_2 = 0 + op.trig_io_2 = 1 + op.trig_value_2 = False + op.trig_time_2 = 0.0 + op.trig_dist_2 = 0.0 + op.trig_event_2 = '' + + list1=Op(3,1,True,3000,0.0,"",3,2,True,3000,0.0,"") + list2=Op(3,3,True, 3000, 10, "", 3, 4, True, 3000, 0.0, "") + # Connect! + rlt = duco_cobot.open() + print("open:", rlt) + rlt = duco_cobot.power_on(True) + print("power_on:", rlt) + rlt = duco_cobot.enable(True) + print("enable:", rlt) + # rlt = duco_cobot.set_tool_data("default",[0,0,0,0,0,0],[1,0,0,0],[0,0,0,0,0,0]) + # print("set tool:", rlt) + rlt = duco_cobot.movej2([0,0,1.57,0,-1.57,0],1.0,1.0,0,True) + print("movej2", rlt) + point1 = PointOp([0.692000, 0.164000, 0.660000, -3.141592, 0.000000, -1.570796], list1) + point2 = PointOp([0.692000, 0.164000, 0.529717, -3.141592, 0.000000, -1.570796], list2) + # point3 = PointOp([0.692000, 0.164000, 0.380282, -3.141592, 0.000000, -1.570796], op) + # point4 = PointOp([0.791035, 0.164000, 0.380282, -3.141592, 0.000000, -1.570796], op) + # point5 = PointOp([0.866886, 0.164000, 0.380282, -3.141592, 0.000000, -1.570796], op) + pointList = [point1, point2] + op.time_or_dist_1=0 + op.time_or_dist_2=0 + # rlt = duco_cobot.tcp_move([0.35,0.09,0.2,1.7,0.45,-0.13],0.5,0.5,0,"default",True) + # print("tcp move:",rlt) + # rlt=duco_cobot.movej2([-1,-0.25,2,-3.4,1.57,2.24],1,2,0,True) + # print("movej2",rlt) + + # print("spline") + # rlt=duco_cobot.spline_op(pointList, 1, 2, "", "", True,op,0,False) + # print("spline",rlt) + # Pp = [[0.692000, 0.164000, 0.660000, -3.141592, 0.000000, -1.570796], [3, 1, True, 0, 0, "", 3, 1, False, 0, 0, ""]] + + rlt = duco_cobot.trackClearQueue() + print("clear queue: ", rlt) + rlt = duco_cobot.getQueueSize() + print("queue size: ", rlt) + rlt = duco_cobot.trackEnqueueOp(pointList,True) + print("enqueue: ", rlt) + rlt = duco_cobot.getQueueSize() + print("queue size: ", rlt) + rlt=duco_cobot.trackCartMotion(0.1, 0.2, True, "", "") + print("cart",rlt) + + # # Connect! + # rlt = duco_cobot.open() + # print("open:", rlt) + # rlt = duco_cobot.power_on(True) + # print("power_on:", rlt) + # rlt = duco_cobot.enable(True) + # print("enable:", rlt) + # rlt = duco_cobot.set_tool_data("default",[0,0,0,0,0,0],[1,0,0,0],[0,0,0,0,0,0]) + # print("set tool:", rlt) + # rlt = duco_cobot.movej2([0,0,1.57,0,-1.57,0],1.0,1.0,0,True) + # print("movej2", rlt) + # pp1 = PointOp() + # pp1.pos = [0.692000,0.164000,0.329717,-3.141592,0.000000,-1.570796] + # pp1.op = op + # pp_list = [pp1] + + # duco_cobot.spline_op(pp_list,1,1,"","",True,op,0,True) + + # input() + stopheartthread = True + time.sleep(1) + rlt = duco_cobot.close() + print("close:", rlt) + +if __name__ == '__main__': + try: + main() + except Thrift.TException as tx: + print('%s' % tx.message) diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/__init__.py b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/RPCRobot-remote b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/RPCRobot-remote new file mode 100644 index 00000000..81584a40 --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/RPCRobot-remote @@ -0,0 +1,2035 @@ +#!/usr/bin/env python +# +# Autogenerated by Thrift Compiler (0.13.0) +# +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING +# +# options string: py +# + +import sys +import pprint +if sys.version_info[0] > 2: + from urllib.parse import urlparse +else: + from urlparse import urlparse +from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient +from thrift.protocol.TBinaryProtocol import TBinaryProtocol + +from robot import RPCRobot +from robot.ttypes import * + +if len(sys.argv) <= 1 or sys.argv[1] == '--help': + print('') + print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]') + print('') + print('Functions:') + print(' i32 power_on(bool block)') + print(' i32 power_off(bool block)') + print(' i32 enable(bool block)') + print(' i32 disable(bool block)') + print(' i32 shutdown(bool block)') + print(' i32 stop(bool block)') + print(' i32 pause(bool block)') + print(' i32 resume(bool block)') + print(' i32 run_program(string name, bool block)') + print(' i32 set_tool_data(string name, tool_offset, payload, inertia_tensor)') + print(' get_tcp_offset()') + print(' i32 set_wobj(string name, wobj)') + print(' i32 set_wobj_offset( wobj, bool active)') + print(' cal_fkine( joints_position, tool, wobj)') + print(' cal_ikine( p, q_near, tool, wobj)') + print(' i32 set_digital_output_mode(i16 num, i16 type, i32 freq, i32 duty_cycle)') + print(' i32 set_standard_digital_out(i16 num, bool value, bool block)') + print(' i32 set_tool_digital_out(i16 num, bool value, bool block)') + print(' bool get_standard_digital_in(i16 num)') + print(' bool get_standard_digital_out(i16 num)') + print(' bool get_tool_digital_in(i16 num)') + print(' bool get_tool_digital_out(i16 num)') + print(' bool get_config_digital_in(i16 num)') + print(' double get_standard_analog_voltage_in(i16 num)') + print(' double get_tool_analog_voltage_in(i16 num)') + print(' double get_standard_analog_current_in(i16 num)') + print(' i32 set_standard_analog_voltage_out(i16 num, double value, bool block)') + print(' i32 set_standard_analog_current_out(i16 num, double value, bool block)') + print(' read_data_485()') + print(' read_raw_data_485(i32 len)') + print(' read_raw_data_485_ht( head, tail)') + print(' read_raw_data_485_h( head, i32 len)') + print(' bool write_data_485( data)') + print(' bool write_raw_data_485( data)') + print(' bool write_raw_data_485_h( data, head)') + print(' bool write_raw_data_485_ht( data, head, tail)') + print(' tool_read_data_485()') + print(' tool_read_raw_data_485(i32 len)') + print(' tool_read_raw_data_485_h( head, i32 len)') + print(' tool_read_raw_data_485_ht( head, tail)') + print(' bool tool_write_data_485( data)') + print(' bool tool_write_raw_data_485( data)') + print(' bool tool_write_raw_data_485_h( data, head)') + print(' bool tool_write_raw_data_485_ht( data, head, tail)') + print(' read_data_can()') + print(' read_raw_data_can()') + print(' bool write_data_can(i32 id, data)') + print(' bool write_raw_data_can(i32 id, data)') + print(' bool get_function_digital_in(i16 num)') + print(' bool get_function_digital_out(i16 num)') + print(' bool read_bool_reg(i16 num)') + print(' i32 read_word_reg(i16 num)') + print(' double read_float_reg(i16 num)') + print(' i32 write_bool_reg(i16 num, bool value)') + print(' i32 write_word_reg(i16 num, i32 value)') + print(' i32 write_float_reg(i16 num, double value)') + print(' bool get_function_reg_in(i16 num)') + print(' bool get_function_reg_out(i16 num)') + print(' i32 movej( joints_list, double v, double a, double r, bool block, Op op, bool def_acc)') + print(' i32 movej_pose( p, double v, double a, double r, q_near, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 movej2( joints_list, double v, double a, double r, bool block, Op op, bool def_acc)') + print(' i32 movej_pose2( p, double v, double a, double r, q_near, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 movel( p, double v, double a, double r, q_near, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 movec( p1, p2, double v, double a, double r, i32 mode, q_near, string tool, string wobj, bool block, Op op, bool def_acc, double arc_rad)') + print(' i32 move_circle( p1, p2, double v, double a, double r, i32 mode, q_near, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 tcp_move( pose_offset, double v, double a, double r, string tool, bool block, Op op, bool def_acc)') + print(' i32 tcp_move_2p( p1, p2, double v, double a, double r, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 wobj_move( pose_offset, double v, double a, double r, string wobj, bool block, Op op, bool def_acc)') + print(' i32 wobj_move_2p( p1, p2, double v, double a, double r, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 spline( pose_list, double v, double a, string tool, string wobj, bool block, Op op, double r, bool def_acc)') + print(' i32 spline_op( pose_list, double v, double a, string tool, string wobj, bool block, Op op, double r, bool def_acc)') + print(' i32 speedj( joints_list, double a, i32 time, bool block)') + print(' i32 speedl( pose_list, double a, i32 time, bool block)') + print(' i32 speed_stop(bool block)') + print(' i32 servoj( joints_list, double v, double a, bool block, double kp, double kd, double smooth_vel, double smooth_acc)') + print(' i32 servoj_pose( pose_list, double v, double a, q_near, string tool, string wobj, bool block, double kp, double kd, double smooth_vel, double smooth_acc)') + print(' i32 servo_tcp( pose_offset, double v, double a, string tool, bool block, double kp, double kd, double smooth_vel, double smooth_acc)') + print(' i32 servol( pose_list, double v, double a, q_near, string tool, string wobj, bool block, double kp, double kd, double smooth_vel, double smooth_acc)') + print(' i32 teach_mode(bool block)') + print(' i32 end_teach_mode(bool block)') + print(' i32 modbus_add_signal(string ip, i32 slave_number, i32 signal_address, i32 signal_type, string signal_name)') + print(' i32 modbus_delete_signal(string signal_name)') + print(' i32 modbus_read(string signal_name)') + print(' i32 modbus_write(string signal_name, i32 value)') + print(' void modbus_set_frequency(string signal_name, i32 frequence)') + print(' get_last_error()') + print(' i32 get_noneblock_taskstate(i32 id)') + print(' void log_info(string message)') + print(' void log_error(string message)') + print(' i32 simulation(bool sim, bool block)') + print(' i32 speed(double val)') + print(' get_robot_state()') + print(' get_flange_pose()') + print(' get_flange_speed()') + print(' get_flange_acceleration()') + print(' get_tcp_pose()') + print(' get_tcp_speed()') + print(' get_tcp_acceleration()') + print(' get_tcp_force()') + print(' get_actual_joints_position()') + print(' get_target_joints_position()') + print(' get_actual_joints_speed()') + print(' get_target_joints_speed()') + print(' get_actual_joints_acceleration()') + print(' get_target_joints_acceleration()') + print(' get_actual_joints_torque()') + print(' get_target_joints_torque()') + print(' i32 stop_record_track()') + print(' i32 start_record_track(string name, i32 mode, string tool, string wobj, double interval)') + print(' i32 collision_detect(i32 value)') + print(' i32 replay(string name, i32 value, i32 mode)') + print(' i32 set_load_data( value)') + print(' i32 fc_start()') + print(' i32 fc_stop()') + print(' i32 fc_config( direction, ref_ft, damp, max_vel, number_list, string tool, string wobj, i32 value)') + print(' i32 fc_move(bool block)') + print(' i32 fc_guard_act( direction, ref_ft, string tool, string wobj, i32 type, i32 force_property)') + print(' i32 fc_guard_deact()') + print(' i32 fc_force_set_value( direction, ref_ft)') + print(' i32 fc_wait_pos( middle, range, bool absolute, i32 duration, i32 timeout)') + print(' i32 fc_wait_vel( middle, range, bool absolute, i32 duration, i32 timeout)') + print(' i32 fc_wait_ft( middle, range, bool absolute, i32 duration, i32 timeout)') + print(' i32 fc_wait_logic( value)') + print(' fc_get_ft()') + print(' bool fc_mode_is_active()') + print(' i32 start_realtime_mode(i32 mode, double fileter_bandwidth, double com_lost_time)') + print(' i32 end_realtime_mode()') + print(' i32 realtime_data_enqueue( realtime_data, bool block)') + print(' i32 clear_realtime_data_queue()') + print(' i32 get_realtime_data_queue_size()') + print(' i32 enable_speed_optimization()') + print(' i32 disable_speed_optimization()') + print(' void change_recipe()') + print(' i32 set_system_value_bool(string name, bool value)') + print(' i32 set_system_value_double(string name, double value)') + print(' i32 set_system_value_str(string name, string value)') + print(' i32 set_system_value_list(string name, value)') + print(' bool get_system_value_bool(string name)') + print(' double get_system_value_double(string name)') + print(' string get_system_value_str(string name)') + print(' get_system_value_list(string name)') + print(' i32 trackEnqueue( track, bool block)') + print(' i32 trackEnqueueOp( track, bool block)') + print(' i32 trackClearQueue()') + print(' i32 getQueueSize()') + print(' i32 trackJointMotion(double speed, double acc, bool block)') + print(' i32 trackCartMotion(double speed, double acc, bool block, string tool, string wobj, double radius)') + print(' void rpc_heartbeat(i32 time)') + print(' i32 move_spiral( p1, p2, double rev, double len, double r, i32 mode, double v, double a, q_near, string tool, string wobj, bool block, Op op, bool def_acc)') + print(' i32 enable_acc_optimization()') + print(' i32 disable_acc_optimization()') + print(' i32 set_baudrate_485(i32 value, bool block)') + print(' i32 set_baudrate_can(i32 value, bool block)') + print(' i32 set_analog_output_mode(i16 num, i32 mode, bool block)') + print(' bool robotmoving()') + print(' i32 modbus_write_multiple_coils(i32 slave_num, string name, i32 len, byte_list)') + print(' i32 modbus_write_multiple_regs(i32 slave_num, string name, i32 len, word_list)') + print(' string get_current_project()') + print(' get_files_list(string path)') + print(' RobotStatus getRobotStatus()') + print(' IOStatus getRobotIOStatus()') + print(' get_tcp_pose_coord(string tool, string wobj)') + print(' get_tcp_force_tool(string tool)') + print(' i32 restart(bool block)') + print(' i32 set_servo_config(i32 axis_num, i32 id, i32 value, i32 qfmt, bool block)') + print(' i32 apply_servo_config(i32 axis_num, bool block)') + print(' get_motor_pole_pair_number()') + print(' get_motor_stator_slots()') + print(' get_axis_ratio()') + print(' i32 collision_detection_reset()') + print(' i32 set_servo_file_params(i32 axis_num, i32 id, string name, double value, double qfmt)') + print(' i32 combine_motion_config(i32 type, i32 ref_plane, double fq, double amp, double el_offset, double az_offset, double up_height, time, bool path_dwell, double pendulum_height, op_list)') + print(' i32 set_eaxis_param(i32 num, EAxisParam param, bool block)') + print(' i32 add_eaxis_scheme(i32 num, bool block)') + print(' i32 delete_eaxis_scheme(i32 num, bool block)') + print(' i32 enable_eaxis_scheme(string scheme_name)') + print(' i32 disable_eaxis_scheme(string scheme_name)') + print(' i32 set_eaxiss_scheme_param(i32 num, EAxisSchemeParam param, bool block)') + print(' i32 move_jog(MoveJogTaskParam param, bool block)') + print(' i32 stop_manual_move(bool block)') + print(' string get_robot_version()') + print(' i32 set_teach_pendant(bool enable)') + print(' i32 get_teach_speed()') + print(' i32 get_global_speed()') + print(' i32 set_teach_speed(i32 v)') + print(' i32 enable_combine_motion()') + print(' i32 disable_combine_motion()') + print(' i32 enable_singularity_control()') + print(' i32 disable_singularity_control()') + print(' i32 enable_vibration_control()') + print(' i32 disable_vibration_control()') + print(' i32 move_eaxis( scheme_name, epose, v, bool block, Op op)') + print(' i32 movej2_eaxis( joints_list, double v, double a, double rad, scheme_name, epose, eaxis_v, bool block, Op op, bool def_acc)') + print(' i32 movej2_pose_eaxis( p, double v, double a, double rad, qnear, string tool, string wobj, scheme_name, epose, eaxis_v, bool block, Op op, bool def_acc)') + print(' i32 movel_eaxis( p, double v, double a, double rad, qnear, string tool, string wobj, scheme_name, epose, eaxis_v, bool block, Op op, bool def_acc)') + print(' i32 movec_eaxis( p1, p2, double v, double a, double rad, qnear, string tool, string wobj, scheme_name, epose, eaxis_v, bool block, Op op, bool def_acc, i32 mode, double arc_rad)') + print(' i32 move_circle_eaxis( p1, p2, double v, double a, double rad, i32 mode, qnear, string tool, string wobj, scheme_name, epose, eaxis_v, bool block, Op op, bool def_acc)') + print(' ReachabilityParam reach_check( base, wobj, tool, ref_pos, check_points)') + print(' i32 move_jog_eaxis(string name, i32 direction, double vel, bool block)') + print(' get_eaxis_info()') + print(' i32 set_hand_teach_parameter(i32 space, joint_scale, cart_scale, i32 coord_type, direction, i32 global_scale)') + print(' i32 set_pendant_type(i32 type)') + print(' i32 set_blend_ahead(i32 per, i32 num)') + print(' i32 switch_mode(i32 mode)') + print(' i64 read_encoder_count()') + print(' i32 set_kinematic_calibration_params( params)') + print(' get_pos_bias()') + print(' get_system_value_lists(string name)') + print(' get_origin_DH()') + print(' get_calib_DH()') + print(' get_robot_type()') + print(' get_ext_torque()') + print(' i32 set_dynamic_calibration_params( params)') + print(' get_dynamic_calibration_params()') + print(' i32 upload_robot_param_to_toolboard(string passwd)') + print(' i32 set_kinematic_calibration_info(string passwd, string info)') + print(' i32 set_dynamic_calibration_info(string passwd, string info)') + print(' i32 set_vibration_calibration_info(string passwd, string info)') + print(' get_axis_motor_rated_current()') + print(' get_axis_motor_kt()') + print(' i32 abort(bool block)') + print(' get_vibration_calibration_params()') + print(' i32 save_kinematic_calibration_params(string passwd)') + print(' i32 save_dynamic_calibration_params(string passwd)') + print(' get_flange_pose_cmd()') + print(' get_flange_speed_cmd()') + print(' get_flange_acceleration_cmd()') + print(' get_tcp_pose_command()') + print(' get_tcp_speed_command()') + print(' get_tcp_acceleration_command()') + print(' get_tcp_pose_command_coord(string tool, string wobj)') + print(' get_tcp_speed_command_coord(string tool, string wobj)') + print(' get_tcp_acceleration_command_coord(string tool, string wobj)') + print(' get_tcp_speed_coord(string tool, string wobj)') + print(' get_tcp_acceleration_coord(string tool, string wobj)') + print(' void clear_error_message()') + print(' i64 cal_traj_time(TrajTimeParam param)') + print(' i64 get_hardware_clock()') + print(' get_tool_data(string name)') + print(' get_tool_load(string name)') + print(' get_wobj(string name)') + print(' get_base()') + print(' get_home_pose()') + print(' i32 set_tool_data_workcell(string name, tool, payload, inertia_tensor)') + print(' i32 set_wobj_workcell(string name, wobj)') + print(' bool get_simulation_state()') + print(' i32 save_stiffness_calibration_params(string passwd)') + print(' get_stiffness_calibration_params()') + print(' i32 set_stiffness_calibration_params( params)') + print(' get_joint_motion_params()') + print(' i32 move_fixed(string tool, string wobj, i32 axis, i32 timer, scheme_name, epose, eaxis_v, bool block, Op op, bool def_acc)') + print(' i32 track_enqueue_op_vel( track, bool block)') + print(' i32 track_cart_vel_motion(double acc, string tool, string wobj, bool block)') + print(' PathOffsetResult path_offset_cal(i32 path_type, path_point, string tool, string wobj, path_offset, path_retract, tool_rotation, double path_rotation, weld_rotation, i32 tool_z_offset, path_type_list, plane_normals, bool average_height)') + print(' i32 set_installation( installation)') + print(' get_installation()') + print(' i32 enable_laser_track()') + print(' i32 set_laser_track_params(i32 plane, i32 freq, i32 lead_len, active, kp, kd, offset, offset_vel, filter_coe, bool path_length_correction, bool track_type)') + print(' i32 disable_laser_track()') + print(' i32 reset_laser_track()') + print(' i32 write_laser_search_pos( p)') + print(' i32 set_arc_system_params(double coeff_K, i32 lag_time, i32 start_time, sensi_coeff, double diff_constant, double diff_value, bool voltage_track, bool cal_lag_time, bool arc_direction, i32 ref_cycle_times)') + print(' i32 set_arc_track_params(i32 freq, active, kp, kd, max_displacement, max_displacement_vel, double base_current, bool track_type, max_displacement_cycle, double constant_bias)') + print(' i32 enable_arc_track(bool block)') + print(' i32 disable_arc_track(bool block)') + print(' get_arc_track_lag_time()') + print(' i32 reset_arc_track()') + print(' i32 write_arc_current_data(double current)') + print(' i32 clear_arc_current_data()') + print(' i32 start_load_identification(bool block)') + print(' get_eaxis_coord( eaxis_scheme_name)') + print(' i32 set_eaxis_coord( eaxis_scheme_name, coord_list)') + print(' i32 set_remote_tool_data(string name, tool_offset, payload, inertia_tensor)') + print(' i32 set_remote_tool_data_workcell(string name, tool_offset, payload, inertia_tensor)') + print(' get_eaxis_scheme_info()') + print('') + sys.exit(0) + +pp = pprint.PrettyPrinter(indent=2) +host = 'localhost' +port = 9090 +uri = '' +framed = False +ssl = False +validate = True +ca_certs = None +keyfile = None +certfile = None +http = False +argi = 1 + +if sys.argv[argi] == '-h': + parts = sys.argv[argi + 1].split(':') + host = parts[0] + if len(parts) > 1: + port = int(parts[1]) + argi += 2 + +if sys.argv[argi] == '-u': + url = urlparse(sys.argv[argi + 1]) + parts = url[1].split(':') + host = parts[0] + if len(parts) > 1: + port = int(parts[1]) + else: + port = 80 + uri = url[2] + if url[4]: + uri += '?%s' % url[4] + http = True + argi += 2 + +if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed': + framed = True + argi += 1 + +if sys.argv[argi] == '-s' or sys.argv[argi] == '-ssl': + ssl = True + argi += 1 + +if sys.argv[argi] == '-novalidate': + validate = False + argi += 1 + +if sys.argv[argi] == '-ca_certs': + ca_certs = sys.argv[argi+1] + argi += 2 + +if sys.argv[argi] == '-keyfile': + keyfile = sys.argv[argi+1] + argi += 2 + +if sys.argv[argi] == '-certfile': + certfile = sys.argv[argi+1] + argi += 2 + +cmd = sys.argv[argi] +args = sys.argv[argi + 1:] + +if http: + transport = THttpClient.THttpClient(host, port, uri) +else: + if ssl: + socket = TSSLSocket.TSSLSocket(host, port, validate=validate, ca_certs=ca_certs, keyfile=keyfile, certfile=certfile) + else: + socket = TSocket.TSocket(host, port) + if framed: + transport = TTransport.TFramedTransport(socket) + else: + transport = TTransport.TBufferedTransport(socket) +protocol = TBinaryProtocol(transport) +client = RPCRobot.Client(protocol) +transport.open() + +if cmd == 'power_on': + if len(args) != 1: + print('power_on requires 1 args') + sys.exit(1) + pp.pprint(client.power_on(eval(args[0]),)) + +elif cmd == 'power_off': + if len(args) != 1: + print('power_off requires 1 args') + sys.exit(1) + pp.pprint(client.power_off(eval(args[0]),)) + +elif cmd == 'enable': + if len(args) != 1: + print('enable requires 1 args') + sys.exit(1) + pp.pprint(client.enable(eval(args[0]),)) + +elif cmd == 'disable': + if len(args) != 1: + print('disable requires 1 args') + sys.exit(1) + pp.pprint(client.disable(eval(args[0]),)) + +elif cmd == 'shutdown': + if len(args) != 1: + print('shutdown requires 1 args') + sys.exit(1) + pp.pprint(client.shutdown(eval(args[0]),)) + +elif cmd == 'stop': + if len(args) != 1: + print('stop requires 1 args') + sys.exit(1) + pp.pprint(client.stop(eval(args[0]),)) + +elif cmd == 'pause': + if len(args) != 1: + print('pause requires 1 args') + sys.exit(1) + pp.pprint(client.pause(eval(args[0]),)) + +elif cmd == 'resume': + if len(args) != 1: + print('resume requires 1 args') + sys.exit(1) + pp.pprint(client.resume(eval(args[0]),)) + +elif cmd == 'run_program': + if len(args) != 2: + print('run_program requires 2 args') + sys.exit(1) + pp.pprint(client.run_program(args[0], eval(args[1]),)) + +elif cmd == 'set_tool_data': + if len(args) != 4: + print('set_tool_data requires 4 args') + sys.exit(1) + pp.pprint(client.set_tool_data(args[0], eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'get_tcp_offset': + if len(args) != 0: + print('get_tcp_offset requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_offset()) + +elif cmd == 'set_wobj': + if len(args) != 2: + print('set_wobj requires 2 args') + sys.exit(1) + pp.pprint(client.set_wobj(args[0], eval(args[1]),)) + +elif cmd == 'set_wobj_offset': + if len(args) != 2: + print('set_wobj_offset requires 2 args') + sys.exit(1) + pp.pprint(client.set_wobj_offset(eval(args[0]), eval(args[1]),)) + +elif cmd == 'cal_fkine': + if len(args) != 3: + print('cal_fkine requires 3 args') + sys.exit(1) + pp.pprint(client.cal_fkine(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'cal_ikine': + if len(args) != 4: + print('cal_ikine requires 4 args') + sys.exit(1) + pp.pprint(client.cal_ikine(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'set_digital_output_mode': + if len(args) != 4: + print('set_digital_output_mode requires 4 args') + sys.exit(1) + pp.pprint(client.set_digital_output_mode(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'set_standard_digital_out': + if len(args) != 3: + print('set_standard_digital_out requires 3 args') + sys.exit(1) + pp.pprint(client.set_standard_digital_out(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'set_tool_digital_out': + if len(args) != 3: + print('set_tool_digital_out requires 3 args') + sys.exit(1) + pp.pprint(client.set_tool_digital_out(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'get_standard_digital_in': + if len(args) != 1: + print('get_standard_digital_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_standard_digital_in(eval(args[0]),)) + +elif cmd == 'get_standard_digital_out': + if len(args) != 1: + print('get_standard_digital_out requires 1 args') + sys.exit(1) + pp.pprint(client.get_standard_digital_out(eval(args[0]),)) + +elif cmd == 'get_tool_digital_in': + if len(args) != 1: + print('get_tool_digital_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_tool_digital_in(eval(args[0]),)) + +elif cmd == 'get_tool_digital_out': + if len(args) != 1: + print('get_tool_digital_out requires 1 args') + sys.exit(1) + pp.pprint(client.get_tool_digital_out(eval(args[0]),)) + +elif cmd == 'get_config_digital_in': + if len(args) != 1: + print('get_config_digital_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_config_digital_in(eval(args[0]),)) + +elif cmd == 'get_standard_analog_voltage_in': + if len(args) != 1: + print('get_standard_analog_voltage_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_standard_analog_voltage_in(eval(args[0]),)) + +elif cmd == 'get_tool_analog_voltage_in': + if len(args) != 1: + print('get_tool_analog_voltage_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_tool_analog_voltage_in(eval(args[0]),)) + +elif cmd == 'get_standard_analog_current_in': + if len(args) != 1: + print('get_standard_analog_current_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_standard_analog_current_in(eval(args[0]),)) + +elif cmd == 'set_standard_analog_voltage_out': + if len(args) != 3: + print('set_standard_analog_voltage_out requires 3 args') + sys.exit(1) + pp.pprint(client.set_standard_analog_voltage_out(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'set_standard_analog_current_out': + if len(args) != 3: + print('set_standard_analog_current_out requires 3 args') + sys.exit(1) + pp.pprint(client.set_standard_analog_current_out(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'read_data_485': + if len(args) != 0: + print('read_data_485 requires 0 args') + sys.exit(1) + pp.pprint(client.read_data_485()) + +elif cmd == 'read_raw_data_485': + if len(args) != 1: + print('read_raw_data_485 requires 1 args') + sys.exit(1) + pp.pprint(client.read_raw_data_485(eval(args[0]),)) + +elif cmd == 'read_raw_data_485_ht': + if len(args) != 2: + print('read_raw_data_485_ht requires 2 args') + sys.exit(1) + pp.pprint(client.read_raw_data_485_ht(eval(args[0]), eval(args[1]),)) + +elif cmd == 'read_raw_data_485_h': + if len(args) != 2: + print('read_raw_data_485_h requires 2 args') + sys.exit(1) + pp.pprint(client.read_raw_data_485_h(eval(args[0]), eval(args[1]),)) + +elif cmd == 'write_data_485': + if len(args) != 1: + print('write_data_485 requires 1 args') + sys.exit(1) + pp.pprint(client.write_data_485(eval(args[0]),)) + +elif cmd == 'write_raw_data_485': + if len(args) != 1: + print('write_raw_data_485 requires 1 args') + sys.exit(1) + pp.pprint(client.write_raw_data_485(eval(args[0]),)) + +elif cmd == 'write_raw_data_485_h': + if len(args) != 2: + print('write_raw_data_485_h requires 2 args') + sys.exit(1) + pp.pprint(client.write_raw_data_485_h(eval(args[0]), eval(args[1]),)) + +elif cmd == 'write_raw_data_485_ht': + if len(args) != 3: + print('write_raw_data_485_ht requires 3 args') + sys.exit(1) + pp.pprint(client.write_raw_data_485_ht(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'tool_read_data_485': + if len(args) != 0: + print('tool_read_data_485 requires 0 args') + sys.exit(1) + pp.pprint(client.tool_read_data_485()) + +elif cmd == 'tool_read_raw_data_485': + if len(args) != 1: + print('tool_read_raw_data_485 requires 1 args') + sys.exit(1) + pp.pprint(client.tool_read_raw_data_485(eval(args[0]),)) + +elif cmd == 'tool_read_raw_data_485_h': + if len(args) != 2: + print('tool_read_raw_data_485_h requires 2 args') + sys.exit(1) + pp.pprint(client.tool_read_raw_data_485_h(eval(args[0]), eval(args[1]),)) + +elif cmd == 'tool_read_raw_data_485_ht': + if len(args) != 2: + print('tool_read_raw_data_485_ht requires 2 args') + sys.exit(1) + pp.pprint(client.tool_read_raw_data_485_ht(eval(args[0]), eval(args[1]),)) + +elif cmd == 'tool_write_data_485': + if len(args) != 1: + print('tool_write_data_485 requires 1 args') + sys.exit(1) + pp.pprint(client.tool_write_data_485(eval(args[0]),)) + +elif cmd == 'tool_write_raw_data_485': + if len(args) != 1: + print('tool_write_raw_data_485 requires 1 args') + sys.exit(1) + pp.pprint(client.tool_write_raw_data_485(eval(args[0]),)) + +elif cmd == 'tool_write_raw_data_485_h': + if len(args) != 2: + print('tool_write_raw_data_485_h requires 2 args') + sys.exit(1) + pp.pprint(client.tool_write_raw_data_485_h(eval(args[0]), eval(args[1]),)) + +elif cmd == 'tool_write_raw_data_485_ht': + if len(args) != 3: + print('tool_write_raw_data_485_ht requires 3 args') + sys.exit(1) + pp.pprint(client.tool_write_raw_data_485_ht(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'read_data_can': + if len(args) != 0: + print('read_data_can requires 0 args') + sys.exit(1) + pp.pprint(client.read_data_can()) + +elif cmd == 'read_raw_data_can': + if len(args) != 0: + print('read_raw_data_can requires 0 args') + sys.exit(1) + pp.pprint(client.read_raw_data_can()) + +elif cmd == 'write_data_can': + if len(args) != 2: + print('write_data_can requires 2 args') + sys.exit(1) + pp.pprint(client.write_data_can(eval(args[0]), eval(args[1]),)) + +elif cmd == 'write_raw_data_can': + if len(args) != 2: + print('write_raw_data_can requires 2 args') + sys.exit(1) + pp.pprint(client.write_raw_data_can(eval(args[0]), eval(args[1]),)) + +elif cmd == 'get_function_digital_in': + if len(args) != 1: + print('get_function_digital_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_function_digital_in(eval(args[0]),)) + +elif cmd == 'get_function_digital_out': + if len(args) != 1: + print('get_function_digital_out requires 1 args') + sys.exit(1) + pp.pprint(client.get_function_digital_out(eval(args[0]),)) + +elif cmd == 'read_bool_reg': + if len(args) != 1: + print('read_bool_reg requires 1 args') + sys.exit(1) + pp.pprint(client.read_bool_reg(eval(args[0]),)) + +elif cmd == 'read_word_reg': + if len(args) != 1: + print('read_word_reg requires 1 args') + sys.exit(1) + pp.pprint(client.read_word_reg(eval(args[0]),)) + +elif cmd == 'read_float_reg': + if len(args) != 1: + print('read_float_reg requires 1 args') + sys.exit(1) + pp.pprint(client.read_float_reg(eval(args[0]),)) + +elif cmd == 'write_bool_reg': + if len(args) != 2: + print('write_bool_reg requires 2 args') + sys.exit(1) + pp.pprint(client.write_bool_reg(eval(args[0]), eval(args[1]),)) + +elif cmd == 'write_word_reg': + if len(args) != 2: + print('write_word_reg requires 2 args') + sys.exit(1) + pp.pprint(client.write_word_reg(eval(args[0]), eval(args[1]),)) + +elif cmd == 'write_float_reg': + if len(args) != 2: + print('write_float_reg requires 2 args') + sys.exit(1) + pp.pprint(client.write_float_reg(eval(args[0]), eval(args[1]),)) + +elif cmd == 'get_function_reg_in': + if len(args) != 1: + print('get_function_reg_in requires 1 args') + sys.exit(1) + pp.pprint(client.get_function_reg_in(eval(args[0]),)) + +elif cmd == 'get_function_reg_out': + if len(args) != 1: + print('get_function_reg_out requires 1 args') + sys.exit(1) + pp.pprint(client.get_function_reg_out(eval(args[0]),)) + +elif cmd == 'movej': + if len(args) != 7: + print('movej requires 7 args') + sys.exit(1) + pp.pprint(client.movej(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]),)) + +elif cmd == 'movej_pose': + if len(args) != 10: + print('movej_pose requires 10 args') + sys.exit(1) + pp.pprint(client.movej_pose(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'movej2': + if len(args) != 7: + print('movej2 requires 7 args') + sys.exit(1) + pp.pprint(client.movej2(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]),)) + +elif cmd == 'movej_pose2': + if len(args) != 10: + print('movej_pose2 requires 10 args') + sys.exit(1) + pp.pprint(client.movej_pose2(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'movel': + if len(args) != 10: + print('movel requires 10 args') + sys.exit(1) + pp.pprint(client.movel(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'movec': + if len(args) != 13: + print('movec requires 13 args') + sys.exit(1) + pp.pprint(client.movec(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), args[7], args[8], eval(args[9]), eval(args[10]), eval(args[11]), eval(args[12]),)) + +elif cmd == 'move_circle': + if len(args) != 12: + print('move_circle requires 12 args') + sys.exit(1) + pp.pprint(client.move_circle(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), args[7], args[8], eval(args[9]), eval(args[10]), eval(args[11]),)) + +elif cmd == 'tcp_move': + if len(args) != 8: + print('tcp_move requires 8 args') + sys.exit(1) + pp.pprint(client.tcp_move(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), args[4], eval(args[5]), eval(args[6]), eval(args[7]),)) + +elif cmd == 'tcp_move_2p': + if len(args) != 10: + print('tcp_move_2p requires 10 args') + sys.exit(1) + pp.pprint(client.tcp_move_2p(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'wobj_move': + if len(args) != 8: + print('wobj_move requires 8 args') + sys.exit(1) + pp.pprint(client.wobj_move(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), args[4], eval(args[5]), eval(args[6]), eval(args[7]),)) + +elif cmd == 'wobj_move_2p': + if len(args) != 10: + print('wobj_move_2p requires 10 args') + sys.exit(1) + pp.pprint(client.wobj_move_2p(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'spline': + if len(args) != 9: + print('spline requires 9 args') + sys.exit(1) + pp.pprint(client.spline(eval(args[0]), eval(args[1]), eval(args[2]), args[3], args[4], eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]),)) + +elif cmd == 'spline_op': + if len(args) != 9: + print('spline_op requires 9 args') + sys.exit(1) + pp.pprint(client.spline_op(eval(args[0]), eval(args[1]), eval(args[2]), args[3], args[4], eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]),)) + +elif cmd == 'speedj': + if len(args) != 4: + print('speedj requires 4 args') + sys.exit(1) + pp.pprint(client.speedj(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'speedl': + if len(args) != 4: + print('speedl requires 4 args') + sys.exit(1) + pp.pprint(client.speedl(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'speed_stop': + if len(args) != 1: + print('speed_stop requires 1 args') + sys.exit(1) + pp.pprint(client.speed_stop(eval(args[0]),)) + +elif cmd == 'servoj': + if len(args) != 8: + print('servoj requires 8 args') + sys.exit(1) + pp.pprint(client.servoj(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]),)) + +elif cmd == 'servoj_pose': + if len(args) != 11: + print('servoj_pose requires 11 args') + sys.exit(1) + pp.pprint(client.servoj_pose(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), args[4], args[5], eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]),)) + +elif cmd == 'servo_tcp': + if len(args) != 9: + print('servo_tcp requires 9 args') + sys.exit(1) + pp.pprint(client.servo_tcp(eval(args[0]), eval(args[1]), eval(args[2]), args[3], eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]),)) + +elif cmd == 'servol': + if len(args) != 11: + print('servol requires 11 args') + sys.exit(1) + pp.pprint(client.servol(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), args[4], args[5], eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]),)) + +elif cmd == 'teach_mode': + if len(args) != 1: + print('teach_mode requires 1 args') + sys.exit(1) + pp.pprint(client.teach_mode(eval(args[0]),)) + +elif cmd == 'end_teach_mode': + if len(args) != 1: + print('end_teach_mode requires 1 args') + sys.exit(1) + pp.pprint(client.end_teach_mode(eval(args[0]),)) + +elif cmd == 'modbus_add_signal': + if len(args) != 5: + print('modbus_add_signal requires 5 args') + sys.exit(1) + pp.pprint(client.modbus_add_signal(args[0], eval(args[1]), eval(args[2]), eval(args[3]), args[4],)) + +elif cmd == 'modbus_delete_signal': + if len(args) != 1: + print('modbus_delete_signal requires 1 args') + sys.exit(1) + pp.pprint(client.modbus_delete_signal(args[0],)) + +elif cmd == 'modbus_read': + if len(args) != 1: + print('modbus_read requires 1 args') + sys.exit(1) + pp.pprint(client.modbus_read(args[0],)) + +elif cmd == 'modbus_write': + if len(args) != 2: + print('modbus_write requires 2 args') + sys.exit(1) + pp.pprint(client.modbus_write(args[0], eval(args[1]),)) + +elif cmd == 'modbus_set_frequency': + if len(args) != 2: + print('modbus_set_frequency requires 2 args') + sys.exit(1) + pp.pprint(client.modbus_set_frequency(args[0], eval(args[1]),)) + +elif cmd == 'get_last_error': + if len(args) != 0: + print('get_last_error requires 0 args') + sys.exit(1) + pp.pprint(client.get_last_error()) + +elif cmd == 'get_noneblock_taskstate': + if len(args) != 1: + print('get_noneblock_taskstate requires 1 args') + sys.exit(1) + pp.pprint(client.get_noneblock_taskstate(eval(args[0]),)) + +elif cmd == 'log_info': + if len(args) != 1: + print('log_info requires 1 args') + sys.exit(1) + pp.pprint(client.log_info(args[0],)) + +elif cmd == 'log_error': + if len(args) != 1: + print('log_error requires 1 args') + sys.exit(1) + pp.pprint(client.log_error(args[0],)) + +elif cmd == 'simulation': + if len(args) != 2: + print('simulation requires 2 args') + sys.exit(1) + pp.pprint(client.simulation(eval(args[0]), eval(args[1]),)) + +elif cmd == 'speed': + if len(args) != 1: + print('speed requires 1 args') + sys.exit(1) + pp.pprint(client.speed(eval(args[0]),)) + +elif cmd == 'get_robot_state': + if len(args) != 0: + print('get_robot_state requires 0 args') + sys.exit(1) + pp.pprint(client.get_robot_state()) + +elif cmd == 'get_flange_pose': + if len(args) != 0: + print('get_flange_pose requires 0 args') + sys.exit(1) + pp.pprint(client.get_flange_pose()) + +elif cmd == 'get_flange_speed': + if len(args) != 0: + print('get_flange_speed requires 0 args') + sys.exit(1) + pp.pprint(client.get_flange_speed()) + +elif cmd == 'get_flange_acceleration': + if len(args) != 0: + print('get_flange_acceleration requires 0 args') + sys.exit(1) + pp.pprint(client.get_flange_acceleration()) + +elif cmd == 'get_tcp_pose': + if len(args) != 0: + print('get_tcp_pose requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_pose()) + +elif cmd == 'get_tcp_speed': + if len(args) != 0: + print('get_tcp_speed requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_speed()) + +elif cmd == 'get_tcp_acceleration': + if len(args) != 0: + print('get_tcp_acceleration requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_acceleration()) + +elif cmd == 'get_tcp_force': + if len(args) != 0: + print('get_tcp_force requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_force()) + +elif cmd == 'get_actual_joints_position': + if len(args) != 0: + print('get_actual_joints_position requires 0 args') + sys.exit(1) + pp.pprint(client.get_actual_joints_position()) + +elif cmd == 'get_target_joints_position': + if len(args) != 0: + print('get_target_joints_position requires 0 args') + sys.exit(1) + pp.pprint(client.get_target_joints_position()) + +elif cmd == 'get_actual_joints_speed': + if len(args) != 0: + print('get_actual_joints_speed requires 0 args') + sys.exit(1) + pp.pprint(client.get_actual_joints_speed()) + +elif cmd == 'get_target_joints_speed': + if len(args) != 0: + print('get_target_joints_speed requires 0 args') + sys.exit(1) + pp.pprint(client.get_target_joints_speed()) + +elif cmd == 'get_actual_joints_acceleration': + if len(args) != 0: + print('get_actual_joints_acceleration requires 0 args') + sys.exit(1) + pp.pprint(client.get_actual_joints_acceleration()) + +elif cmd == 'get_target_joints_acceleration': + if len(args) != 0: + print('get_target_joints_acceleration requires 0 args') + sys.exit(1) + pp.pprint(client.get_target_joints_acceleration()) + +elif cmd == 'get_actual_joints_torque': + if len(args) != 0: + print('get_actual_joints_torque requires 0 args') + sys.exit(1) + pp.pprint(client.get_actual_joints_torque()) + +elif cmd == 'get_target_joints_torque': + if len(args) != 0: + print('get_target_joints_torque requires 0 args') + sys.exit(1) + pp.pprint(client.get_target_joints_torque()) + +elif cmd == 'stop_record_track': + if len(args) != 0: + print('stop_record_track requires 0 args') + sys.exit(1) + pp.pprint(client.stop_record_track()) + +elif cmd == 'start_record_track': + if len(args) != 5: + print('start_record_track requires 5 args') + sys.exit(1) + pp.pprint(client.start_record_track(args[0], eval(args[1]), args[2], args[3], eval(args[4]),)) + +elif cmd == 'collision_detect': + if len(args) != 1: + print('collision_detect requires 1 args') + sys.exit(1) + pp.pprint(client.collision_detect(eval(args[0]),)) + +elif cmd == 'replay': + if len(args) != 3: + print('replay requires 3 args') + sys.exit(1) + pp.pprint(client.replay(args[0], eval(args[1]), eval(args[2]),)) + +elif cmd == 'set_load_data': + if len(args) != 1: + print('set_load_data requires 1 args') + sys.exit(1) + pp.pprint(client.set_load_data(eval(args[0]),)) + +elif cmd == 'fc_start': + if len(args) != 0: + print('fc_start requires 0 args') + sys.exit(1) + pp.pprint(client.fc_start()) + +elif cmd == 'fc_stop': + if len(args) != 0: + print('fc_stop requires 0 args') + sys.exit(1) + pp.pprint(client.fc_stop()) + +elif cmd == 'fc_config': + if len(args) != 8: + print('fc_config requires 8 args') + sys.exit(1) + pp.pprint(client.fc_config(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]),)) + +elif cmd == 'fc_move': + if len(args) != 1: + print('fc_move requires 1 args') + sys.exit(1) + pp.pprint(client.fc_move(eval(args[0]),)) + +elif cmd == 'fc_guard_act': + if len(args) != 6: + print('fc_guard_act requires 6 args') + sys.exit(1) + pp.pprint(client.fc_guard_act(eval(args[0]), eval(args[1]), args[2], args[3], eval(args[4]), eval(args[5]),)) + +elif cmd == 'fc_guard_deact': + if len(args) != 0: + print('fc_guard_deact requires 0 args') + sys.exit(1) + pp.pprint(client.fc_guard_deact()) + +elif cmd == 'fc_force_set_value': + if len(args) != 2: + print('fc_force_set_value requires 2 args') + sys.exit(1) + pp.pprint(client.fc_force_set_value(eval(args[0]), eval(args[1]),)) + +elif cmd == 'fc_wait_pos': + if len(args) != 5: + print('fc_wait_pos requires 5 args') + sys.exit(1) + pp.pprint(client.fc_wait_pos(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]),)) + +elif cmd == 'fc_wait_vel': + if len(args) != 5: + print('fc_wait_vel requires 5 args') + sys.exit(1) + pp.pprint(client.fc_wait_vel(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]),)) + +elif cmd == 'fc_wait_ft': + if len(args) != 5: + print('fc_wait_ft requires 5 args') + sys.exit(1) + pp.pprint(client.fc_wait_ft(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]),)) + +elif cmd == 'fc_wait_logic': + if len(args) != 1: + print('fc_wait_logic requires 1 args') + sys.exit(1) + pp.pprint(client.fc_wait_logic(eval(args[0]),)) + +elif cmd == 'fc_get_ft': + if len(args) != 0: + print('fc_get_ft requires 0 args') + sys.exit(1) + pp.pprint(client.fc_get_ft()) + +elif cmd == 'fc_mode_is_active': + if len(args) != 0: + print('fc_mode_is_active requires 0 args') + sys.exit(1) + pp.pprint(client.fc_mode_is_active()) + +elif cmd == 'start_realtime_mode': + if len(args) != 3: + print('start_realtime_mode requires 3 args') + sys.exit(1) + pp.pprint(client.start_realtime_mode(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'end_realtime_mode': + if len(args) != 0: + print('end_realtime_mode requires 0 args') + sys.exit(1) + pp.pprint(client.end_realtime_mode()) + +elif cmd == 'realtime_data_enqueue': + if len(args) != 2: + print('realtime_data_enqueue requires 2 args') + sys.exit(1) + pp.pprint(client.realtime_data_enqueue(eval(args[0]), eval(args[1]),)) + +elif cmd == 'clear_realtime_data_queue': + if len(args) != 0: + print('clear_realtime_data_queue requires 0 args') + sys.exit(1) + pp.pprint(client.clear_realtime_data_queue()) + +elif cmd == 'get_realtime_data_queue_size': + if len(args) != 0: + print('get_realtime_data_queue_size requires 0 args') + sys.exit(1) + pp.pprint(client.get_realtime_data_queue_size()) + +elif cmd == 'enable_speed_optimization': + if len(args) != 0: + print('enable_speed_optimization requires 0 args') + sys.exit(1) + pp.pprint(client.enable_speed_optimization()) + +elif cmd == 'disable_speed_optimization': + if len(args) != 0: + print('disable_speed_optimization requires 0 args') + sys.exit(1) + pp.pprint(client.disable_speed_optimization()) + +elif cmd == 'change_recipe': + if len(args) != 0: + print('change_recipe requires 0 args') + sys.exit(1) + pp.pprint(client.change_recipe()) + +elif cmd == 'set_system_value_bool': + if len(args) != 2: + print('set_system_value_bool requires 2 args') + sys.exit(1) + pp.pprint(client.set_system_value_bool(args[0], eval(args[1]),)) + +elif cmd == 'set_system_value_double': + if len(args) != 2: + print('set_system_value_double requires 2 args') + sys.exit(1) + pp.pprint(client.set_system_value_double(args[0], eval(args[1]),)) + +elif cmd == 'set_system_value_str': + if len(args) != 2: + print('set_system_value_str requires 2 args') + sys.exit(1) + pp.pprint(client.set_system_value_str(args[0], args[1],)) + +elif cmd == 'set_system_value_list': + if len(args) != 2: + print('set_system_value_list requires 2 args') + sys.exit(1) + pp.pprint(client.set_system_value_list(args[0], eval(args[1]),)) + +elif cmd == 'get_system_value_bool': + if len(args) != 1: + print('get_system_value_bool requires 1 args') + sys.exit(1) + pp.pprint(client.get_system_value_bool(args[0],)) + +elif cmd == 'get_system_value_double': + if len(args) != 1: + print('get_system_value_double requires 1 args') + sys.exit(1) + pp.pprint(client.get_system_value_double(args[0],)) + +elif cmd == 'get_system_value_str': + if len(args) != 1: + print('get_system_value_str requires 1 args') + sys.exit(1) + pp.pprint(client.get_system_value_str(args[0],)) + +elif cmd == 'get_system_value_list': + if len(args) != 1: + print('get_system_value_list requires 1 args') + sys.exit(1) + pp.pprint(client.get_system_value_list(args[0],)) + +elif cmd == 'trackEnqueue': + if len(args) != 2: + print('trackEnqueue requires 2 args') + sys.exit(1) + pp.pprint(client.trackEnqueue(eval(args[0]), eval(args[1]),)) + +elif cmd == 'trackEnqueueOp': + if len(args) != 2: + print('trackEnqueueOp requires 2 args') + sys.exit(1) + pp.pprint(client.trackEnqueueOp(eval(args[0]), eval(args[1]),)) + +elif cmd == 'trackClearQueue': + if len(args) != 0: + print('trackClearQueue requires 0 args') + sys.exit(1) + pp.pprint(client.trackClearQueue()) + +elif cmd == 'getQueueSize': + if len(args) != 0: + print('getQueueSize requires 0 args') + sys.exit(1) + pp.pprint(client.getQueueSize()) + +elif cmd == 'trackJointMotion': + if len(args) != 3: + print('trackJointMotion requires 3 args') + sys.exit(1) + pp.pprint(client.trackJointMotion(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'trackCartMotion': + if len(args) != 6: + print('trackCartMotion requires 6 args') + sys.exit(1) + pp.pprint(client.trackCartMotion(eval(args[0]), eval(args[1]), eval(args[2]), args[3], args[4], eval(args[5]),)) + +elif cmd == 'rpc_heartbeat': + if len(args) != 1: + print('rpc_heartbeat requires 1 args') + sys.exit(1) + pp.pprint(client.rpc_heartbeat(eval(args[0]),)) + +elif cmd == 'move_spiral': + if len(args) != 14: + print('move_spiral requires 14 args') + sys.exit(1) + pp.pprint(client.move_spiral(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), args[9], args[10], eval(args[11]), eval(args[12]), eval(args[13]),)) + +elif cmd == 'enable_acc_optimization': + if len(args) != 0: + print('enable_acc_optimization requires 0 args') + sys.exit(1) + pp.pprint(client.enable_acc_optimization()) + +elif cmd == 'disable_acc_optimization': + if len(args) != 0: + print('disable_acc_optimization requires 0 args') + sys.exit(1) + pp.pprint(client.disable_acc_optimization()) + +elif cmd == 'set_baudrate_485': + if len(args) != 2: + print('set_baudrate_485 requires 2 args') + sys.exit(1) + pp.pprint(client.set_baudrate_485(eval(args[0]), eval(args[1]),)) + +elif cmd == 'set_baudrate_can': + if len(args) != 2: + print('set_baudrate_can requires 2 args') + sys.exit(1) + pp.pprint(client.set_baudrate_can(eval(args[0]), eval(args[1]),)) + +elif cmd == 'set_analog_output_mode': + if len(args) != 3: + print('set_analog_output_mode requires 3 args') + sys.exit(1) + pp.pprint(client.set_analog_output_mode(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'robotmoving': + if len(args) != 0: + print('robotmoving requires 0 args') + sys.exit(1) + pp.pprint(client.robotmoving()) + +elif cmd == 'modbus_write_multiple_coils': + if len(args) != 4: + print('modbus_write_multiple_coils requires 4 args') + sys.exit(1) + pp.pprint(client.modbus_write_multiple_coils(eval(args[0]), args[1], eval(args[2]), eval(args[3]),)) + +elif cmd == 'modbus_write_multiple_regs': + if len(args) != 4: + print('modbus_write_multiple_regs requires 4 args') + sys.exit(1) + pp.pprint(client.modbus_write_multiple_regs(eval(args[0]), args[1], eval(args[2]), eval(args[3]),)) + +elif cmd == 'get_current_project': + if len(args) != 0: + print('get_current_project requires 0 args') + sys.exit(1) + pp.pprint(client.get_current_project()) + +elif cmd == 'get_files_list': + if len(args) != 1: + print('get_files_list requires 1 args') + sys.exit(1) + pp.pprint(client.get_files_list(args[0],)) + +elif cmd == 'getRobotStatus': + if len(args) != 0: + print('getRobotStatus requires 0 args') + sys.exit(1) + pp.pprint(client.getRobotStatus()) + +elif cmd == 'getRobotIOStatus': + if len(args) != 0: + print('getRobotIOStatus requires 0 args') + sys.exit(1) + pp.pprint(client.getRobotIOStatus()) + +elif cmd == 'get_tcp_pose_coord': + if len(args) != 2: + print('get_tcp_pose_coord requires 2 args') + sys.exit(1) + pp.pprint(client.get_tcp_pose_coord(args[0], args[1],)) + +elif cmd == 'get_tcp_force_tool': + if len(args) != 1: + print('get_tcp_force_tool requires 1 args') + sys.exit(1) + pp.pprint(client.get_tcp_force_tool(args[0],)) + +elif cmd == 'restart': + if len(args) != 1: + print('restart requires 1 args') + sys.exit(1) + pp.pprint(client.restart(eval(args[0]),)) + +elif cmd == 'set_servo_config': + if len(args) != 5: + print('set_servo_config requires 5 args') + sys.exit(1) + pp.pprint(client.set_servo_config(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]),)) + +elif cmd == 'apply_servo_config': + if len(args) != 2: + print('apply_servo_config requires 2 args') + sys.exit(1) + pp.pprint(client.apply_servo_config(eval(args[0]), eval(args[1]),)) + +elif cmd == 'get_motor_pole_pair_number': + if len(args) != 0: + print('get_motor_pole_pair_number requires 0 args') + sys.exit(1) + pp.pprint(client.get_motor_pole_pair_number()) + +elif cmd == 'get_motor_stator_slots': + if len(args) != 0: + print('get_motor_stator_slots requires 0 args') + sys.exit(1) + pp.pprint(client.get_motor_stator_slots()) + +elif cmd == 'get_axis_ratio': + if len(args) != 0: + print('get_axis_ratio requires 0 args') + sys.exit(1) + pp.pprint(client.get_axis_ratio()) + +elif cmd == 'collision_detection_reset': + if len(args) != 0: + print('collision_detection_reset requires 0 args') + sys.exit(1) + pp.pprint(client.collision_detection_reset()) + +elif cmd == 'set_servo_file_params': + if len(args) != 5: + print('set_servo_file_params requires 5 args') + sys.exit(1) + pp.pprint(client.set_servo_file_params(eval(args[0]), eval(args[1]), args[2], eval(args[3]), eval(args[4]),)) + +elif cmd == 'combine_motion_config': + if len(args) != 11: + print('combine_motion_config requires 11 args') + sys.exit(1) + pp.pprint(client.combine_motion_config(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]),)) + +elif cmd == 'set_eaxis_param': + if len(args) != 3: + print('set_eaxis_param requires 3 args') + sys.exit(1) + pp.pprint(client.set_eaxis_param(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'add_eaxis_scheme': + if len(args) != 2: + print('add_eaxis_scheme requires 2 args') + sys.exit(1) + pp.pprint(client.add_eaxis_scheme(eval(args[0]), eval(args[1]),)) + +elif cmd == 'delete_eaxis_scheme': + if len(args) != 2: + print('delete_eaxis_scheme requires 2 args') + sys.exit(1) + pp.pprint(client.delete_eaxis_scheme(eval(args[0]), eval(args[1]),)) + +elif cmd == 'enable_eaxis_scheme': + if len(args) != 1: + print('enable_eaxis_scheme requires 1 args') + sys.exit(1) + pp.pprint(client.enable_eaxis_scheme(args[0],)) + +elif cmd == 'disable_eaxis_scheme': + if len(args) != 1: + print('disable_eaxis_scheme requires 1 args') + sys.exit(1) + pp.pprint(client.disable_eaxis_scheme(args[0],)) + +elif cmd == 'set_eaxiss_scheme_param': + if len(args) != 3: + print('set_eaxiss_scheme_param requires 3 args') + sys.exit(1) + pp.pprint(client.set_eaxiss_scheme_param(eval(args[0]), eval(args[1]), eval(args[2]),)) + +elif cmd == 'move_jog': + if len(args) != 2: + print('move_jog requires 2 args') + sys.exit(1) + pp.pprint(client.move_jog(eval(args[0]), eval(args[1]),)) + +elif cmd == 'stop_manual_move': + if len(args) != 1: + print('stop_manual_move requires 1 args') + sys.exit(1) + pp.pprint(client.stop_manual_move(eval(args[0]),)) + +elif cmd == 'get_robot_version': + if len(args) != 0: + print('get_robot_version requires 0 args') + sys.exit(1) + pp.pprint(client.get_robot_version()) + +elif cmd == 'set_teach_pendant': + if len(args) != 1: + print('set_teach_pendant requires 1 args') + sys.exit(1) + pp.pprint(client.set_teach_pendant(eval(args[0]),)) + +elif cmd == 'get_teach_speed': + if len(args) != 0: + print('get_teach_speed requires 0 args') + sys.exit(1) + pp.pprint(client.get_teach_speed()) + +elif cmd == 'get_global_speed': + if len(args) != 0: + print('get_global_speed requires 0 args') + sys.exit(1) + pp.pprint(client.get_global_speed()) + +elif cmd == 'set_teach_speed': + if len(args) != 1: + print('set_teach_speed requires 1 args') + sys.exit(1) + pp.pprint(client.set_teach_speed(eval(args[0]),)) + +elif cmd == 'enable_combine_motion': + if len(args) != 0: + print('enable_combine_motion requires 0 args') + sys.exit(1) + pp.pprint(client.enable_combine_motion()) + +elif cmd == 'disable_combine_motion': + if len(args) != 0: + print('disable_combine_motion requires 0 args') + sys.exit(1) + pp.pprint(client.disable_combine_motion()) + +elif cmd == 'enable_singularity_control': + if len(args) != 0: + print('enable_singularity_control requires 0 args') + sys.exit(1) + pp.pprint(client.enable_singularity_control()) + +elif cmd == 'disable_singularity_control': + if len(args) != 0: + print('disable_singularity_control requires 0 args') + sys.exit(1) + pp.pprint(client.disable_singularity_control()) + +elif cmd == 'enable_vibration_control': + if len(args) != 0: + print('enable_vibration_control requires 0 args') + sys.exit(1) + pp.pprint(client.enable_vibration_control()) + +elif cmd == 'disable_vibration_control': + if len(args) != 0: + print('disable_vibration_control requires 0 args') + sys.exit(1) + pp.pprint(client.disable_vibration_control()) + +elif cmd == 'move_eaxis': + if len(args) != 5: + print('move_eaxis requires 5 args') + sys.exit(1) + pp.pprint(client.move_eaxis(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]),)) + +elif cmd == 'movej2_eaxis': + if len(args) != 10: + print('movej2_eaxis requires 10 args') + sys.exit(1) + pp.pprint(client.movej2_eaxis(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'movej2_pose_eaxis': + if len(args) != 13: + print('movej2_pose_eaxis requires 13 args') + sys.exit(1) + pp.pprint(client.movej2_pose_eaxis(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]), eval(args[11]), eval(args[12]),)) + +elif cmd == 'movel_eaxis': + if len(args) != 13: + print('movel_eaxis requires 13 args') + sys.exit(1) + pp.pprint(client.movel_eaxis(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), args[5], args[6], eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]), eval(args[11]), eval(args[12]),)) + +elif cmd == 'movec_eaxis': + if len(args) != 16: + print('movec_eaxis requires 16 args') + sys.exit(1) + pp.pprint(client.movec_eaxis(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), args[6], args[7], eval(args[8]), eval(args[9]), eval(args[10]), eval(args[11]), eval(args[12]), eval(args[13]), eval(args[14]), eval(args[15]),)) + +elif cmd == 'move_circle_eaxis': + if len(args) != 15: + print('move_circle_eaxis requires 15 args') + sys.exit(1) + pp.pprint(client.move_circle_eaxis(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), args[7], args[8], eval(args[9]), eval(args[10]), eval(args[11]), eval(args[12]), eval(args[13]), eval(args[14]),)) + +elif cmd == 'reach_check': + if len(args) != 5: + print('reach_check requires 5 args') + sys.exit(1) + pp.pprint(client.reach_check(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]),)) + +elif cmd == 'move_jog_eaxis': + if len(args) != 4: + print('move_jog_eaxis requires 4 args') + sys.exit(1) + pp.pprint(client.move_jog_eaxis(args[0], eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'get_eaxis_info': + if len(args) != 0: + print('get_eaxis_info requires 0 args') + sys.exit(1) + pp.pprint(client.get_eaxis_info()) + +elif cmd == 'set_hand_teach_parameter': + if len(args) != 6: + print('set_hand_teach_parameter requires 6 args') + sys.exit(1) + pp.pprint(client.set_hand_teach_parameter(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]),)) + +elif cmd == 'set_pendant_type': + if len(args) != 1: + print('set_pendant_type requires 1 args') + sys.exit(1) + pp.pprint(client.set_pendant_type(eval(args[0]),)) + +elif cmd == 'set_blend_ahead': + if len(args) != 2: + print('set_blend_ahead requires 2 args') + sys.exit(1) + pp.pprint(client.set_blend_ahead(eval(args[0]), eval(args[1]),)) + +elif cmd == 'switch_mode': + if len(args) != 1: + print('switch_mode requires 1 args') + sys.exit(1) + pp.pprint(client.switch_mode(eval(args[0]),)) + +elif cmd == 'read_encoder_count': + if len(args) != 0: + print('read_encoder_count requires 0 args') + sys.exit(1) + pp.pprint(client.read_encoder_count()) + +elif cmd == 'set_kinematic_calibration_params': + if len(args) != 1: + print('set_kinematic_calibration_params requires 1 args') + sys.exit(1) + pp.pprint(client.set_kinematic_calibration_params(eval(args[0]),)) + +elif cmd == 'get_pos_bias': + if len(args) != 0: + print('get_pos_bias requires 0 args') + sys.exit(1) + pp.pprint(client.get_pos_bias()) + +elif cmd == 'get_system_value_lists': + if len(args) != 1: + print('get_system_value_lists requires 1 args') + sys.exit(1) + pp.pprint(client.get_system_value_lists(args[0],)) + +elif cmd == 'get_origin_DH': + if len(args) != 0: + print('get_origin_DH requires 0 args') + sys.exit(1) + pp.pprint(client.get_origin_DH()) + +elif cmd == 'get_calib_DH': + if len(args) != 0: + print('get_calib_DH requires 0 args') + sys.exit(1) + pp.pprint(client.get_calib_DH()) + +elif cmd == 'get_robot_type': + if len(args) != 0: + print('get_robot_type requires 0 args') + sys.exit(1) + pp.pprint(client.get_robot_type()) + +elif cmd == 'get_ext_torque': + if len(args) != 0: + print('get_ext_torque requires 0 args') + sys.exit(1) + pp.pprint(client.get_ext_torque()) + +elif cmd == 'set_dynamic_calibration_params': + if len(args) != 1: + print('set_dynamic_calibration_params requires 1 args') + sys.exit(1) + pp.pprint(client.set_dynamic_calibration_params(eval(args[0]),)) + +elif cmd == 'get_dynamic_calibration_params': + if len(args) != 0: + print('get_dynamic_calibration_params requires 0 args') + sys.exit(1) + pp.pprint(client.get_dynamic_calibration_params()) + +elif cmd == 'upload_robot_param_to_toolboard': + if len(args) != 1: + print('upload_robot_param_to_toolboard requires 1 args') + sys.exit(1) + pp.pprint(client.upload_robot_param_to_toolboard(args[0],)) + +elif cmd == 'set_kinematic_calibration_info': + if len(args) != 2: + print('set_kinematic_calibration_info requires 2 args') + sys.exit(1) + pp.pprint(client.set_kinematic_calibration_info(args[0], args[1],)) + +elif cmd == 'set_dynamic_calibration_info': + if len(args) != 2: + print('set_dynamic_calibration_info requires 2 args') + sys.exit(1) + pp.pprint(client.set_dynamic_calibration_info(args[0], args[1],)) + +elif cmd == 'set_vibration_calibration_info': + if len(args) != 2: + print('set_vibration_calibration_info requires 2 args') + sys.exit(1) + pp.pprint(client.set_vibration_calibration_info(args[0], args[1],)) + +elif cmd == 'get_axis_motor_rated_current': + if len(args) != 0: + print('get_axis_motor_rated_current requires 0 args') + sys.exit(1) + pp.pprint(client.get_axis_motor_rated_current()) + +elif cmd == 'get_axis_motor_kt': + if len(args) != 0: + print('get_axis_motor_kt requires 0 args') + sys.exit(1) + pp.pprint(client.get_axis_motor_kt()) + +elif cmd == 'abort': + if len(args) != 1: + print('abort requires 1 args') + sys.exit(1) + pp.pprint(client.abort(eval(args[0]),)) + +elif cmd == 'get_vibration_calibration_params': + if len(args) != 0: + print('get_vibration_calibration_params requires 0 args') + sys.exit(1) + pp.pprint(client.get_vibration_calibration_params()) + +elif cmd == 'save_kinematic_calibration_params': + if len(args) != 1: + print('save_kinematic_calibration_params requires 1 args') + sys.exit(1) + pp.pprint(client.save_kinematic_calibration_params(args[0],)) + +elif cmd == 'save_dynamic_calibration_params': + if len(args) != 1: + print('save_dynamic_calibration_params requires 1 args') + sys.exit(1) + pp.pprint(client.save_dynamic_calibration_params(args[0],)) + +elif cmd == 'get_flange_pose_cmd': + if len(args) != 0: + print('get_flange_pose_cmd requires 0 args') + sys.exit(1) + pp.pprint(client.get_flange_pose_cmd()) + +elif cmd == 'get_flange_speed_cmd': + if len(args) != 0: + print('get_flange_speed_cmd requires 0 args') + sys.exit(1) + pp.pprint(client.get_flange_speed_cmd()) + +elif cmd == 'get_flange_acceleration_cmd': + if len(args) != 0: + print('get_flange_acceleration_cmd requires 0 args') + sys.exit(1) + pp.pprint(client.get_flange_acceleration_cmd()) + +elif cmd == 'get_tcp_pose_command': + if len(args) != 0: + print('get_tcp_pose_command requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_pose_command()) + +elif cmd == 'get_tcp_speed_command': + if len(args) != 0: + print('get_tcp_speed_command requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_speed_command()) + +elif cmd == 'get_tcp_acceleration_command': + if len(args) != 0: + print('get_tcp_acceleration_command requires 0 args') + sys.exit(1) + pp.pprint(client.get_tcp_acceleration_command()) + +elif cmd == 'get_tcp_pose_command_coord': + if len(args) != 2: + print('get_tcp_pose_command_coord requires 2 args') + sys.exit(1) + pp.pprint(client.get_tcp_pose_command_coord(args[0], args[1],)) + +elif cmd == 'get_tcp_speed_command_coord': + if len(args) != 2: + print('get_tcp_speed_command_coord requires 2 args') + sys.exit(1) + pp.pprint(client.get_tcp_speed_command_coord(args[0], args[1],)) + +elif cmd == 'get_tcp_acceleration_command_coord': + if len(args) != 2: + print('get_tcp_acceleration_command_coord requires 2 args') + sys.exit(1) + pp.pprint(client.get_tcp_acceleration_command_coord(args[0], args[1],)) + +elif cmd == 'get_tcp_speed_coord': + if len(args) != 2: + print('get_tcp_speed_coord requires 2 args') + sys.exit(1) + pp.pprint(client.get_tcp_speed_coord(args[0], args[1],)) + +elif cmd == 'get_tcp_acceleration_coord': + if len(args) != 2: + print('get_tcp_acceleration_coord requires 2 args') + sys.exit(1) + pp.pprint(client.get_tcp_acceleration_coord(args[0], args[1],)) + +elif cmd == 'clear_error_message': + if len(args) != 0: + print('clear_error_message requires 0 args') + sys.exit(1) + pp.pprint(client.clear_error_message()) + +elif cmd == 'cal_traj_time': + if len(args) != 1: + print('cal_traj_time requires 1 args') + sys.exit(1) + pp.pprint(client.cal_traj_time(eval(args[0]),)) + +elif cmd == 'get_hardware_clock': + if len(args) != 0: + print('get_hardware_clock requires 0 args') + sys.exit(1) + pp.pprint(client.get_hardware_clock()) + +elif cmd == 'get_tool_data': + if len(args) != 1: + print('get_tool_data requires 1 args') + sys.exit(1) + pp.pprint(client.get_tool_data(args[0],)) + +elif cmd == 'get_tool_load': + if len(args) != 1: + print('get_tool_load requires 1 args') + sys.exit(1) + pp.pprint(client.get_tool_load(args[0],)) + +elif cmd == 'get_wobj': + if len(args) != 1: + print('get_wobj requires 1 args') + sys.exit(1) + pp.pprint(client.get_wobj(args[0],)) + +elif cmd == 'get_base': + if len(args) != 0: + print('get_base requires 0 args') + sys.exit(1) + pp.pprint(client.get_base()) + +elif cmd == 'get_home_pose': + if len(args) != 0: + print('get_home_pose requires 0 args') + sys.exit(1) + pp.pprint(client.get_home_pose()) + +elif cmd == 'set_tool_data_workcell': + if len(args) != 4: + print('set_tool_data_workcell requires 4 args') + sys.exit(1) + pp.pprint(client.set_tool_data_workcell(args[0], eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'set_wobj_workcell': + if len(args) != 2: + print('set_wobj_workcell requires 2 args') + sys.exit(1) + pp.pprint(client.set_wobj_workcell(args[0], eval(args[1]),)) + +elif cmd == 'get_simulation_state': + if len(args) != 0: + print('get_simulation_state requires 0 args') + sys.exit(1) + pp.pprint(client.get_simulation_state()) + +elif cmd == 'save_stiffness_calibration_params': + if len(args) != 1: + print('save_stiffness_calibration_params requires 1 args') + sys.exit(1) + pp.pprint(client.save_stiffness_calibration_params(args[0],)) + +elif cmd == 'get_stiffness_calibration_params': + if len(args) != 0: + print('get_stiffness_calibration_params requires 0 args') + sys.exit(1) + pp.pprint(client.get_stiffness_calibration_params()) + +elif cmd == 'set_stiffness_calibration_params': + if len(args) != 1: + print('set_stiffness_calibration_params requires 1 args') + sys.exit(1) + pp.pprint(client.set_stiffness_calibration_params(eval(args[0]),)) + +elif cmd == 'get_joint_motion_params': + if len(args) != 0: + print('get_joint_motion_params requires 0 args') + sys.exit(1) + pp.pprint(client.get_joint_motion_params()) + +elif cmd == 'move_fixed': + if len(args) != 10: + print('move_fixed requires 10 args') + sys.exit(1) + pp.pprint(client.move_fixed(args[0], args[1], eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'track_enqueue_op_vel': + if len(args) != 2: + print('track_enqueue_op_vel requires 2 args') + sys.exit(1) + pp.pprint(client.track_enqueue_op_vel(eval(args[0]), eval(args[1]),)) + +elif cmd == 'track_cart_vel_motion': + if len(args) != 4: + print('track_cart_vel_motion requires 4 args') + sys.exit(1) + pp.pprint(client.track_cart_vel_motion(eval(args[0]), args[1], args[2], eval(args[3]),)) + +elif cmd == 'path_offset_cal': + if len(args) != 13: + print('path_offset_cal requires 13 args') + sys.exit(1) + pp.pprint(client.path_offset_cal(eval(args[0]), eval(args[1]), args[2], args[3], eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]), eval(args[11]), eval(args[12]),)) + +elif cmd == 'set_installation': + if len(args) != 1: + print('set_installation requires 1 args') + sys.exit(1) + pp.pprint(client.set_installation(eval(args[0]),)) + +elif cmd == 'get_installation': + if len(args) != 0: + print('get_installation requires 0 args') + sys.exit(1) + pp.pprint(client.get_installation()) + +elif cmd == 'enable_laser_track': + if len(args) != 0: + print('enable_laser_track requires 0 args') + sys.exit(1) + pp.pprint(client.enable_laser_track()) + +elif cmd == 'set_laser_track_params': + if len(args) != 11: + print('set_laser_track_params requires 11 args') + sys.exit(1) + pp.pprint(client.set_laser_track_params(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]), eval(args[10]),)) + +elif cmd == 'disable_laser_track': + if len(args) != 0: + print('disable_laser_track requires 0 args') + sys.exit(1) + pp.pprint(client.disable_laser_track()) + +elif cmd == 'reset_laser_track': + if len(args) != 0: + print('reset_laser_track requires 0 args') + sys.exit(1) + pp.pprint(client.reset_laser_track()) + +elif cmd == 'write_laser_search_pos': + if len(args) != 1: + print('write_laser_search_pos requires 1 args') + sys.exit(1) + pp.pprint(client.write_laser_search_pos(eval(args[0]),)) + +elif cmd == 'set_arc_system_params': + if len(args) != 10: + print('set_arc_system_params requires 10 args') + sys.exit(1) + pp.pprint(client.set_arc_system_params(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'set_arc_track_params': + if len(args) != 10: + print('set_arc_track_params requires 10 args') + sys.exit(1) + pp.pprint(client.set_arc_track_params(eval(args[0]), eval(args[1]), eval(args[2]), eval(args[3]), eval(args[4]), eval(args[5]), eval(args[6]), eval(args[7]), eval(args[8]), eval(args[9]),)) + +elif cmd == 'enable_arc_track': + if len(args) != 1: + print('enable_arc_track requires 1 args') + sys.exit(1) + pp.pprint(client.enable_arc_track(eval(args[0]),)) + +elif cmd == 'disable_arc_track': + if len(args) != 1: + print('disable_arc_track requires 1 args') + sys.exit(1) + pp.pprint(client.disable_arc_track(eval(args[0]),)) + +elif cmd == 'get_arc_track_lag_time': + if len(args) != 0: + print('get_arc_track_lag_time requires 0 args') + sys.exit(1) + pp.pprint(client.get_arc_track_lag_time()) + +elif cmd == 'reset_arc_track': + if len(args) != 0: + print('reset_arc_track requires 0 args') + sys.exit(1) + pp.pprint(client.reset_arc_track()) + +elif cmd == 'write_arc_current_data': + if len(args) != 1: + print('write_arc_current_data requires 1 args') + sys.exit(1) + pp.pprint(client.write_arc_current_data(eval(args[0]),)) + +elif cmd == 'clear_arc_current_data': + if len(args) != 0: + print('clear_arc_current_data requires 0 args') + sys.exit(1) + pp.pprint(client.clear_arc_current_data()) + +elif cmd == 'start_load_identification': + if len(args) != 1: + print('start_load_identification requires 1 args') + sys.exit(1) + pp.pprint(client.start_load_identification(eval(args[0]),)) + +elif cmd == 'get_eaxis_coord': + if len(args) != 1: + print('get_eaxis_coord requires 1 args') + sys.exit(1) + pp.pprint(client.get_eaxis_coord(eval(args[0]),)) + +elif cmd == 'set_eaxis_coord': + if len(args) != 2: + print('set_eaxis_coord requires 2 args') + sys.exit(1) + pp.pprint(client.set_eaxis_coord(eval(args[0]), eval(args[1]),)) + +elif cmd == 'set_remote_tool_data': + if len(args) != 4: + print('set_remote_tool_data requires 4 args') + sys.exit(1) + pp.pprint(client.set_remote_tool_data(args[0], eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'set_remote_tool_data_workcell': + if len(args) != 4: + print('set_remote_tool_data_workcell requires 4 args') + sys.exit(1) + pp.pprint(client.set_remote_tool_data_workcell(args[0], eval(args[1]), eval(args[2]), eval(args[3]),)) + +elif cmd == 'get_eaxis_scheme_info': + if len(args) != 0: + print('get_eaxis_scheme_info requires 0 args') + sys.exit(1) + pp.pprint(client.get_eaxis_scheme_info()) + +else: + print('Unrecognized method %s' % cmd) + sys.exit(1) + +transport.close() diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/RPCRobot.py b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/RPCRobot.py new file mode 100644 index 00000000..51798bc1 --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/RPCRobot.py @@ -0,0 +1,57558 @@ +# +# Autogenerated by Thrift Compiler (0.13.0) +# +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING +# +# options string: py +# + +from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException +from thrift.protocol.TProtocol import TProtocolException +from thrift.TRecursive import fix_spec + +import sys +import logging +from .ttypes import * +from thrift.Thrift import TProcessor +from thrift.transport import TTransport +all_structs = [] + + +class Iface(object): + def power_on(self, block): + """ + Parameters: + - block + + """ + pass + + def power_off(self, block): + """ + Parameters: + - block + + """ + pass + + def enable(self, block): + """ + Parameters: + - block + + """ + pass + + def disable(self, block): + """ + Parameters: + - block + + """ + pass + + def shutdown(self, block): + """ + Parameters: + - block + + """ + pass + + def stop(self, block): + """ + Parameters: + - block + + """ + pass + + def pause(self, block): + """ + Parameters: + - block + + """ + pass + + def resume(self, block): + """ + Parameters: + - block + + """ + pass + + def run_program(self, name, block): + """ + Parameters: + - name + - block + + """ + pass + + def set_tool_data(self, name, tool_offset, payload, inertia_tensor): + """ + Parameters: + - name + - tool_offset + - payload + - inertia_tensor + + """ + pass + + def get_tcp_offset(self): + pass + + def set_wobj(self, name, wobj): + """ + Parameters: + - name + - wobj + + """ + pass + + def set_wobj_offset(self, wobj, active): + """ + Parameters: + - wobj + - active + + """ + pass + + def cal_fkine(self, joints_position, tool, wobj): + """ + Parameters: + - joints_position + - tool + - wobj + + """ + pass + + def cal_ikine(self, p, q_near, tool, wobj): + """ + Parameters: + - p + - q_near + - tool + - wobj + + """ + pass + + def set_digital_output_mode(self, num, type, freq, duty_cycle): + """ + Parameters: + - num + - type + - freq + - duty_cycle + + """ + pass + + def set_standard_digital_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + pass + + def set_tool_digital_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + pass + + def get_standard_digital_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_standard_digital_out(self, num): + """ + Parameters: + - num + + """ + pass + + def get_tool_digital_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_tool_digital_out(self, num): + """ + Parameters: + - num + + """ + pass + + def get_config_digital_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_standard_analog_voltage_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_tool_analog_voltage_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_standard_analog_current_in(self, num): + """ + Parameters: + - num + + """ + pass + + def set_standard_analog_voltage_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + pass + + def set_standard_analog_current_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + pass + + def read_data_485(self): + pass + + def read_raw_data_485(self, len): + """ + Parameters: + - len + + """ + pass + + def read_raw_data_485_ht(self, head, tail): + """ + Parameters: + - head + - tail + + """ + pass + + def read_raw_data_485_h(self, head, len): + """ + Parameters: + - head + - len + + """ + pass + + def write_data_485(self, data): + """ + Parameters: + - data + + """ + pass + + def write_raw_data_485(self, data): + """ + Parameters: + - data + + """ + pass + + def write_raw_data_485_h(self, data, head): + """ + Parameters: + - data + - head + + """ + pass + + def write_raw_data_485_ht(self, data, head, tail): + """ + Parameters: + - data + - head + - tail + + """ + pass + + def tool_read_data_485(self): + pass + + def tool_read_raw_data_485(self, len): + """ + Parameters: + - len + + """ + pass + + def tool_read_raw_data_485_h(self, head, len): + """ + Parameters: + - head + - len + + """ + pass + + def tool_read_raw_data_485_ht(self, head, tail): + """ + Parameters: + - head + - tail + + """ + pass + + def tool_write_data_485(self, data): + """ + Parameters: + - data + + """ + pass + + def tool_write_raw_data_485(self, data): + """ + Parameters: + - data + + """ + pass + + def tool_write_raw_data_485_h(self, data, head): + """ + Parameters: + - data + - head + + """ + pass + + def tool_write_raw_data_485_ht(self, data, head, tail): + """ + Parameters: + - data + - head + - tail + + """ + pass + + def read_data_can(self): + pass + + def read_raw_data_can(self): + pass + + def write_data_can(self, id, data): + """ + Parameters: + - id + - data + + """ + pass + + def write_raw_data_can(self, id, data): + """ + Parameters: + - id + - data + + """ + pass + + def get_function_digital_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_function_digital_out(self, num): + """ + Parameters: + - num + + """ + pass + + def read_bool_reg(self, num): + """ + Parameters: + - num + + """ + pass + + def read_word_reg(self, num): + """ + Parameters: + - num + + """ + pass + + def read_float_reg(self, num): + """ + Parameters: + - num + + """ + pass + + def write_bool_reg(self, num, value): + """ + Parameters: + - num + - value + + """ + pass + + def write_word_reg(self, num, value): + """ + Parameters: + - num + - value + + """ + pass + + def write_float_reg(self, num, value): + """ + Parameters: + - num + - value + + """ + pass + + def get_function_reg_in(self, num): + """ + Parameters: + - num + + """ + pass + + def get_function_reg_out(self, num): + """ + Parameters: + - num + + """ + pass + + def movej(self, joints_list, v, a, r, block, op, def_acc): + """ + Parameters: + - joints_list + - v + - a + - r + - block + - op + - def_acc + + """ + pass + + def movej_pose(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def movej2(self, joints_list, v, a, r, block, op, def_acc): + """ + Parameters: + - joints_list + - v + - a + - r + - block + - op + - def_acc + + """ + pass + + def movej_pose2(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def movel(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def movec(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc, arc_rad): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - mode + - q_near + - tool + - wobj + - block + - op + - def_acc + - arc_rad + + """ + pass + + def move_circle(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - mode + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def tcp_move(self, pose_offset, v, a, r, tool, block, op, def_acc): + """ + Parameters: + - pose_offset + - v + - a + - r + - tool + - block + - op + - def_acc + + """ + pass + + def tcp_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def wobj_move(self, pose_offset, v, a, r, wobj, block, op, def_acc): + """ + Parameters: + - pose_offset + - v + - a + - r + - wobj + - block + - op + - def_acc + + """ + pass + + def wobj_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def spline(self, pose_list, v, a, tool, wobj, block, op, r, def_acc): + """ + Parameters: + - pose_list + - v + - a + - tool + - wobj + - block + - op + - r + - def_acc + + """ + pass + + def spline_op(self, pose_list, v, a, tool, wobj, block, op, r, def_acc): + """ + Parameters: + - pose_list + - v + - a + - tool + - wobj + - block + - op + - r + - def_acc + + """ + pass + + def speedj(self, joints_list, a, time, block): + """ + Parameters: + - joints_list + - a + - time + - block + + """ + pass + + def speedl(self, pose_list, a, time, block): + """ + Parameters: + - pose_list + - a + - time + - block + + """ + pass + + def speed_stop(self, block): + """ + Parameters: + - block + + """ + pass + + def servoj(self, joints_list, v, a, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - joints_list + - v + - a + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + pass + + def servoj_pose(self, pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - pose_list + - v + - a + - q_near + - tool + - wobj + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + pass + + def servo_tcp(self, pose_offset, v, a, tool, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - pose_offset + - v + - a + - tool + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + pass + + def servol(self, pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - pose_list + - v + - a + - q_near + - tool + - wobj + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + pass + + def teach_mode(self, block): + """ + Parameters: + - block + + """ + pass + + def end_teach_mode(self, block): + """ + Parameters: + - block + + """ + pass + + def modbus_add_signal(self, ip, slave_number, signal_address, signal_type, signal_name): + """ + Parameters: + - ip + - slave_number + - signal_address + - signal_type + - signal_name + + """ + pass + + def modbus_delete_signal(self, signal_name): + """ + Parameters: + - signal_name + + """ + pass + + def modbus_read(self, signal_name): + """ + Parameters: + - signal_name + + """ + pass + + def modbus_write(self, signal_name, value): + """ + Parameters: + - signal_name + - value + + """ + pass + + def modbus_set_frequency(self, signal_name, frequence): + """ + Parameters: + - signal_name + - frequence + + """ + pass + + def get_last_error(self): + pass + + def get_noneblock_taskstate(self, id): + """ + Parameters: + - id + + """ + pass + + def log_info(self, message): + """ + Parameters: + - message + + """ + pass + + def log_error(self, message): + """ + Parameters: + - message + + """ + pass + + def simulation(self, sim, block): + """ + Parameters: + - sim + - block + + """ + pass + + def speed(self, val): + """ + Parameters: + - val + + """ + pass + + def get_robot_state(self): + pass + + def get_flange_pose(self): + pass + + def get_flange_speed(self): + pass + + def get_flange_acceleration(self): + pass + + def get_tcp_pose(self): + pass + + def get_tcp_speed(self): + pass + + def get_tcp_acceleration(self): + pass + + def get_tcp_force(self): + pass + + def get_actual_joints_position(self): + pass + + def get_target_joints_position(self): + pass + + def get_actual_joints_speed(self): + pass + + def get_target_joints_speed(self): + pass + + def get_actual_joints_acceleration(self): + pass + + def get_target_joints_acceleration(self): + pass + + def get_actual_joints_torque(self): + pass + + def get_target_joints_torque(self): + pass + + def stop_record_track(self): + pass + + def start_record_track(self, name, mode, tool, wobj, interval): + """ + Parameters: + - name + - mode + - tool + - wobj + - interval + + """ + pass + + def collision_detect(self, value): + """ + Parameters: + - value + + """ + pass + + def replay(self, name, value, mode): + """ + Parameters: + - name + - value + - mode + + """ + pass + + def set_load_data(self, value): + """ + Parameters: + - value + + """ + pass + + def fc_start(self): + pass + + def fc_stop(self): + pass + + def fc_config(self, direction, ref_ft, damp, max_vel, number_list, tool, wobj, value): + """ + Parameters: + - direction + - ref_ft + - damp + - max_vel + - number_list + - tool + - wobj + - value + + """ + pass + + def fc_move(self, block): + """ + Parameters: + - block + + """ + pass + + def fc_guard_act(self, direction, ref_ft, tool, wobj, type, force_property): + """ + Parameters: + - direction + - ref_ft + - tool + - wobj + - type + - force_property + + """ + pass + + def fc_guard_deact(self): + pass + + def fc_force_set_value(self, direction, ref_ft): + """ + Parameters: + - direction + - ref_ft + + """ + pass + + def fc_wait_pos(self, middle, range, absolute, duration, timeout): + """ + Parameters: + - middle + - range + - absolute + - duration + - timeout + + """ + pass + + def fc_wait_vel(self, middle, range, absolute, duration, timeout): + """ + Parameters: + - middle + - range + - absolute + - duration + - timeout + + """ + pass + + def fc_wait_ft(self, middle, range, absolute, duration, timeout): + """ + Parameters: + - middle + - range + - absolute + - duration + - timeout + + """ + pass + + def fc_wait_logic(self, value): + """ + Parameters: + - value + + """ + pass + + def fc_get_ft(self): + pass + + def fc_mode_is_active(self): + pass + + def start_realtime_mode(self, mode, fileter_bandwidth, com_lost_time): + """ + Parameters: + - mode + - fileter_bandwidth + - com_lost_time + + """ + pass + + def end_realtime_mode(self): + pass + + def realtime_data_enqueue(self, realtime_data, block): + """ + Parameters: + - realtime_data + - block + + """ + pass + + def clear_realtime_data_queue(self): + pass + + def get_realtime_data_queue_size(self): + pass + + def enable_speed_optimization(self): + pass + + def disable_speed_optimization(self): + pass + + def change_recipe(self): + pass + + def set_system_value_bool(self, name, value): + """ + Parameters: + - name + - value + + """ + pass + + def set_system_value_double(self, name, value): + """ + Parameters: + - name + - value + + """ + pass + + def set_system_value_str(self, name, value): + """ + Parameters: + - name + - value + + """ + pass + + def set_system_value_list(self, name, value): + """ + Parameters: + - name + - value + + """ + pass + + def get_system_value_bool(self, name): + """ + Parameters: + - name + + """ + pass + + def get_system_value_double(self, name): + """ + Parameters: + - name + + """ + pass + + def get_system_value_str(self, name): + """ + Parameters: + - name + + """ + pass + + def get_system_value_list(self, name): + """ + Parameters: + - name + + """ + pass + + def trackEnqueue(self, track, block): + """ + Parameters: + - track + - block + + """ + pass + + def trackEnqueueOp(self, track, block): + """ + Parameters: + - track + - block + + """ + pass + + def trackClearQueue(self): + pass + + def getQueueSize(self): + pass + + def trackJointMotion(self, speed, acc, block): + """ + Parameters: + - speed + - acc + - block + + """ + pass + + def trackCartMotion(self, speed, acc, block, tool, wobj, radius): + """ + Parameters: + - speed + - acc + - block + - tool + - wobj + - radius + + """ + pass + + def rpc_heartbeat(self, time): + """ + Parameters: + - time + + """ + pass + + def move_spiral(self, p1, p2, rev, len, r, mode, v, a, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - rev + - len + - r + - mode + - v + - a + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + pass + + def enable_acc_optimization(self): + pass + + def disable_acc_optimization(self): + pass + + def set_baudrate_485(self, value, block): + """ + Parameters: + - value + - block + + """ + pass + + def set_baudrate_can(self, value, block): + """ + Parameters: + - value + - block + + """ + pass + + def set_analog_output_mode(self, num, mode, block): + """ + Parameters: + - num + - mode + - block + + """ + pass + + def robotmoving(self): + pass + + def modbus_write_multiple_coils(self, slave_num, name, len, byte_list): + """ + Parameters: + - slave_num + - name + - len + - byte_list + + """ + pass + + def modbus_write_multiple_regs(self, slave_num, name, len, word_list): + """ + Parameters: + - slave_num + - name + - len + - word_list + + """ + pass + + def get_current_project(self): + pass + + def get_files_list(self, path): + """ + Parameters: + - path + + """ + pass + + def getRobotStatus(self): + pass + + def getRobotIOStatus(self): + pass + + def get_tcp_pose_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + pass + + def get_tcp_force_tool(self, tool): + """ + Parameters: + - tool + + """ + pass + + def restart(self, block): + """ + Parameters: + - block + + """ + pass + + def set_servo_config(self, axis_num, id, value, qfmt, block): + """ + Parameters: + - axis_num + - id + - value + - qfmt + - block + + """ + pass + + def apply_servo_config(self, axis_num, block): + """ + Parameters: + - axis_num + - block + + """ + pass + + def get_motor_pole_pair_number(self): + pass + + def get_motor_stator_slots(self): + pass + + def get_axis_ratio(self): + pass + + def collision_detection_reset(self): + pass + + def set_servo_file_params(self, axis_num, id, name, value, qfmt): + """ + Parameters: + - axis_num + - id + - name + - value + - qfmt + + """ + pass + + def combine_motion_config(self, type, ref_plane, fq, amp, el_offset, az_offset, up_height, time, path_dwell, pendulum_height, op_list): + """ + Parameters: + - type + - ref_plane + - fq + - amp + - el_offset + - az_offset + - up_height + - time + - path_dwell + - pendulum_height + - op_list + + """ + pass + + def set_eaxis_param(self, num, param, block): + """ + Parameters: + - num + - param + - block + + """ + pass + + def add_eaxis_scheme(self, num, block): + """ + Parameters: + - num + - block + + """ + pass + + def delete_eaxis_scheme(self, num, block): + """ + Parameters: + - num + - block + + """ + pass + + def enable_eaxis_scheme(self, scheme_name): + """ + Parameters: + - scheme_name + + """ + pass + + def disable_eaxis_scheme(self, scheme_name): + """ + Parameters: + - scheme_name + + """ + pass + + def set_eaxiss_scheme_param(self, num, param, block): + """ + Parameters: + - num + - param + - block + + """ + pass + + def move_jog(self, param, block): + """ + Parameters: + - param + - block + + """ + pass + + def stop_manual_move(self, block): + """ + Parameters: + - block + + """ + pass + + def get_robot_version(self): + pass + + def set_teach_pendant(self, enable): + """ + Parameters: + - enable + + """ + pass + + def get_teach_speed(self): + pass + + def get_global_speed(self): + pass + + def set_teach_speed(self, v): + """ + Parameters: + - v + + """ + pass + + def enable_combine_motion(self): + pass + + def disable_combine_motion(self): + pass + + def enable_singularity_control(self): + pass + + def disable_singularity_control(self): + pass + + def enable_vibration_control(self): + pass + + def disable_vibration_control(self): + pass + + def move_eaxis(self, scheme_name, epose, v, block, op): + """ + Parameters: + - scheme_name + - epose + - v + - block + - op + + """ + pass + + def movej2_eaxis(self, joints_list, v, a, rad, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - joints_list + - v + - a + - rad + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + pass + + def movej2_pose_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + pass + + def movel_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + pass + + def movec_eaxis(self, p1, p2, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc, mode, arc_rad): + """ + Parameters: + - p1 + - p2 + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + - mode + - arc_rad + + """ + pass + + def move_circle_eaxis(self, p1, p2, v, a, rad, mode, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - rad + - mode + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + pass + + def reach_check(self, base, wobj, tool, ref_pos, check_points): + """ + Parameters: + - base + - wobj + - tool + - ref_pos + - check_points + + """ + pass + + def move_jog_eaxis(self, name, direction, vel, block): + """ + Parameters: + - name + - direction + - vel + - block + + """ + pass + + def get_eaxis_info(self): + pass + + def set_hand_teach_parameter(self, space, joint_scale, cart_scale, coord_type, direction, global_scale): + """ + Parameters: + - space + - joint_scale + - cart_scale + - coord_type + - direction + - global_scale + + """ + pass + + def set_pendant_type(self, type): + """ + Parameters: + - type + + """ + pass + + def set_blend_ahead(self, per, num): + """ + Parameters: + - per + - num + + """ + pass + + def switch_mode(self, mode): + """ + Parameters: + - mode + + """ + pass + + def read_encoder_count(self): + pass + + def set_kinematic_calibration_params(self, params): + """ + Parameters: + - params + + """ + pass + + def get_pos_bias(self): + pass + + def get_system_value_lists(self, name): + """ + Parameters: + - name + + """ + pass + + def get_origin_DH(self): + pass + + def get_calib_DH(self): + pass + + def get_robot_type(self): + pass + + def get_ext_torque(self): + pass + + def set_dynamic_calibration_params(self, params): + """ + Parameters: + - params + + """ + pass + + def get_dynamic_calibration_params(self): + pass + + def upload_robot_param_to_toolboard(self, passwd): + """ + Parameters: + - passwd + + """ + pass + + def set_kinematic_calibration_info(self, passwd, info): + """ + Parameters: + - passwd + - info + + """ + pass + + def set_dynamic_calibration_info(self, passwd, info): + """ + Parameters: + - passwd + - info + + """ + pass + + def set_vibration_calibration_info(self, passwd, info): + """ + Parameters: + - passwd + - info + + """ + pass + + def get_axis_motor_rated_current(self): + pass + + def get_axis_motor_kt(self): + pass + + def abort(self, block): + """ + Parameters: + - block + + """ + pass + + def get_vibration_calibration_params(self): + pass + + def save_kinematic_calibration_params(self, passwd): + """ + Parameters: + - passwd + + """ + pass + + def save_dynamic_calibration_params(self, passwd): + """ + Parameters: + - passwd + + """ + pass + + def get_flange_pose_cmd(self): + pass + + def get_flange_speed_cmd(self): + pass + + def get_flange_acceleration_cmd(self): + pass + + def get_tcp_pose_command(self): + pass + + def get_tcp_speed_command(self): + pass + + def get_tcp_acceleration_command(self): + pass + + def get_tcp_pose_command_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + pass + + def get_tcp_speed_command_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + pass + + def get_tcp_acceleration_command_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + pass + + def get_tcp_speed_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + pass + + def get_tcp_acceleration_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + pass + + def clear_error_message(self): + pass + + def cal_traj_time(self, param): + """ + Parameters: + - param + + """ + pass + + def get_hardware_clock(self): + pass + + def get_tool_data(self, name): + """ + Parameters: + - name + + """ + pass + + def get_tool_load(self, name): + """ + Parameters: + - name + + """ + pass + + def get_wobj(self, name): + """ + Parameters: + - name + + """ + pass + + def get_base(self): + pass + + def get_home_pose(self): + pass + + def set_tool_data_workcell(self, name, tool, payload, inertia_tensor): + """ + Parameters: + - name + - tool + - payload + - inertia_tensor + + """ + pass + + def set_wobj_workcell(self, name, wobj): + """ + Parameters: + - name + - wobj + + """ + pass + + def get_simulation_state(self): + pass + + def save_stiffness_calibration_params(self, passwd): + """ + Parameters: + - passwd + + """ + pass + + def get_stiffness_calibration_params(self): + pass + + def set_stiffness_calibration_params(self, params): + """ + Parameters: + - params + + """ + pass + + def get_joint_motion_params(self): + pass + + def move_fixed(self, tool, wobj, axis, timer, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - tool + - wobj + - axis + - timer + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + pass + + def track_enqueue_op_vel(self, track, block): + """ + Parameters: + - track + - block + + """ + pass + + def track_cart_vel_motion(self, acc, tool, wobj, block): + """ + Parameters: + - acc + - tool + - wobj + - block + + """ + pass + + def path_offset_cal(self, path_type, path_point, tool, wobj, path_offset, path_retract, tool_rotation, path_rotation, weld_rotation, tool_z_offset, path_type_list, plane_normals, average_height): + """ + Parameters: + - path_type + - path_point + - tool + - wobj + - path_offset + - path_retract + - tool_rotation + - path_rotation + - weld_rotation + - tool_z_offset + - path_type_list + - plane_normals + - average_height + + """ + pass + + def set_installation(self, installation): + """ + Parameters: + - installation + + """ + pass + + def get_installation(self): + pass + + def enable_laser_track(self): + pass + + def set_laser_track_params(self, plane, freq, lead_len, active, kp, kd, offset, offset_vel, filter_coe, path_length_correction, track_type): + """ + Parameters: + - plane + - freq + - lead_len + - active + - kp + - kd + - offset + - offset_vel + - filter_coe + - path_length_correction + - track_type + + """ + pass + + def disable_laser_track(self): + pass + + def reset_laser_track(self): + pass + + def write_laser_search_pos(self, p): + """ + Parameters: + - p + + """ + pass + + def set_arc_system_params(self, coeff_K, lag_time, start_time, sensi_coeff, diff_constant, diff_value, voltage_track, cal_lag_time, arc_direction, ref_cycle_times): + """ + Parameters: + - coeff_K + - lag_time + - start_time + - sensi_coeff + - diff_constant + - diff_value + - voltage_track + - cal_lag_time + - arc_direction + - ref_cycle_times + + """ + pass + + def set_arc_track_params(self, freq, active, kp, kd, max_displacement, max_displacement_vel, base_current, track_type, max_displacement_cycle, constant_bias): + """ + Parameters: + - freq + - active + - kp + - kd + - max_displacement + - max_displacement_vel + - base_current + - track_type + - max_displacement_cycle + - constant_bias + + """ + pass + + def enable_arc_track(self, block): + """ + Parameters: + - block + + """ + pass + + def disable_arc_track(self, block): + """ + Parameters: + - block + + """ + pass + + def get_arc_track_lag_time(self): + pass + + def reset_arc_track(self): + pass + + def write_arc_current_data(self, current): + """ + Parameters: + - current + + """ + pass + + def clear_arc_current_data(self): + pass + + def start_load_identification(self, block): + """ + Parameters: + - block + + """ + pass + + def get_eaxis_coord(self, eaxis_scheme_name): + """ + Parameters: + - eaxis_scheme_name + + """ + pass + + def set_eaxis_coord(self, eaxis_scheme_name, coord_list): + """ + Parameters: + - eaxis_scheme_name + - coord_list + + """ + pass + + def set_remote_tool_data(self, name, tool_offset, payload, inertia_tensor): + """ + Parameters: + - name + - tool_offset + - payload + - inertia_tensor + + """ + pass + + def set_remote_tool_data_workcell(self, name, tool_offset, payload, inertia_tensor): + """ + Parameters: + - name + - tool_offset + - payload + - inertia_tensor + + """ + pass + + def get_eaxis_scheme_info(self): + pass + + +class Client(Iface): + def __init__(self, iprot, oprot=None): + self._iprot = self._oprot = iprot + if oprot is not None: + self._oprot = oprot + self._seqid = 0 + + def power_on(self, block): + """ + Parameters: + - block + + """ + self.send_power_on(block) + return self.recv_power_on() + + def send_power_on(self, block): + self._oprot.writeMessageBegin('power_on', TMessageType.CALL, self._seqid) + args = power_on_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_power_on(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = power_on_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "power_on failed: unknown result") + + def power_off(self, block): + """ + Parameters: + - block + + """ + self.send_power_off(block) + return self.recv_power_off() + + def send_power_off(self, block): + self._oprot.writeMessageBegin('power_off', TMessageType.CALL, self._seqid) + args = power_off_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_power_off(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = power_off_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "power_off failed: unknown result") + + def enable(self, block): + """ + Parameters: + - block + + """ + self.send_enable(block) + return self.recv_enable() + + def send_enable(self, block): + self._oprot.writeMessageBegin('enable', TMessageType.CALL, self._seqid) + args = enable_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable failed: unknown result") + + def disable(self, block): + """ + Parameters: + - block + + """ + self.send_disable(block) + return self.recv_disable() + + def send_disable(self, block): + self._oprot.writeMessageBegin('disable', TMessageType.CALL, self._seqid) + args = disable_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable failed: unknown result") + + def shutdown(self, block): + """ + Parameters: + - block + + """ + self.send_shutdown(block) + return self.recv_shutdown() + + def send_shutdown(self, block): + self._oprot.writeMessageBegin('shutdown', TMessageType.CALL, self._seqid) + args = shutdown_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_shutdown(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = shutdown_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "shutdown failed: unknown result") + + def stop(self, block): + """ + Parameters: + - block + + """ + self.send_stop(block) + return self.recv_stop() + + def send_stop(self, block): + self._oprot.writeMessageBegin('stop', TMessageType.CALL, self._seqid) + args = stop_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_stop(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = stop_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "stop failed: unknown result") + + def pause(self, block): + """ + Parameters: + - block + + """ + self.send_pause(block) + return self.recv_pause() + + def send_pause(self, block): + self._oprot.writeMessageBegin('pause', TMessageType.CALL, self._seqid) + args = pause_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_pause(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = pause_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "pause failed: unknown result") + + def resume(self, block): + """ + Parameters: + - block + + """ + self.send_resume(block) + return self.recv_resume() + + def send_resume(self, block): + self._oprot.writeMessageBegin('resume', TMessageType.CALL, self._seqid) + args = resume_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_resume(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = resume_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "resume failed: unknown result") + + def run_program(self, name, block): + """ + Parameters: + - name + - block + + """ + self.send_run_program(name, block) + return self.recv_run_program() + + def send_run_program(self, name, block): + self._oprot.writeMessageBegin('run_program', TMessageType.CALL, self._seqid) + args = run_program_args() + args.name = name + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_run_program(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = run_program_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "run_program failed: unknown result") + + def set_tool_data(self, name, tool_offset, payload, inertia_tensor): + """ + Parameters: + - name + - tool_offset + - payload + - inertia_tensor + + """ + self.send_set_tool_data(name, tool_offset, payload, inertia_tensor) + return self.recv_set_tool_data() + + def send_set_tool_data(self, name, tool_offset, payload, inertia_tensor): + self._oprot.writeMessageBegin('set_tool_data', TMessageType.CALL, self._seqid) + args = set_tool_data_args() + args.name = name + args.tool_offset = tool_offset + args.payload = payload + args.inertia_tensor = inertia_tensor + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_tool_data(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_tool_data_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_tool_data failed: unknown result") + + def get_tcp_offset(self): + self.send_get_tcp_offset() + return self.recv_get_tcp_offset() + + def send_get_tcp_offset(self): + self._oprot.writeMessageBegin('get_tcp_offset', TMessageType.CALL, self._seqid) + args = get_tcp_offset_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_offset(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_offset_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_offset failed: unknown result") + + def set_wobj(self, name, wobj): + """ + Parameters: + - name + - wobj + + """ + self.send_set_wobj(name, wobj) + return self.recv_set_wobj() + + def send_set_wobj(self, name, wobj): + self._oprot.writeMessageBegin('set_wobj', TMessageType.CALL, self._seqid) + args = set_wobj_args() + args.name = name + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_wobj(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_wobj_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_wobj failed: unknown result") + + def set_wobj_offset(self, wobj, active): + """ + Parameters: + - wobj + - active + + """ + self.send_set_wobj_offset(wobj, active) + return self.recv_set_wobj_offset() + + def send_set_wobj_offset(self, wobj, active): + self._oprot.writeMessageBegin('set_wobj_offset', TMessageType.CALL, self._seqid) + args = set_wobj_offset_args() + args.wobj = wobj + args.active = active + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_wobj_offset(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_wobj_offset_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_wobj_offset failed: unknown result") + + def cal_fkine(self, joints_position, tool, wobj): + """ + Parameters: + - joints_position + - tool + - wobj + + """ + self.send_cal_fkine(joints_position, tool, wobj) + return self.recv_cal_fkine() + + def send_cal_fkine(self, joints_position, tool, wobj): + self._oprot.writeMessageBegin('cal_fkine', TMessageType.CALL, self._seqid) + args = cal_fkine_args() + args.joints_position = joints_position + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_cal_fkine(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = cal_fkine_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "cal_fkine failed: unknown result") + + def cal_ikine(self, p, q_near, tool, wobj): + """ + Parameters: + - p + - q_near + - tool + - wobj + + """ + self.send_cal_ikine(p, q_near, tool, wobj) + return self.recv_cal_ikine() + + def send_cal_ikine(self, p, q_near, tool, wobj): + self._oprot.writeMessageBegin('cal_ikine', TMessageType.CALL, self._seqid) + args = cal_ikine_args() + args.p = p + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_cal_ikine(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = cal_ikine_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "cal_ikine failed: unknown result") + + def set_digital_output_mode(self, num, type, freq, duty_cycle): + """ + Parameters: + - num + - type + - freq + - duty_cycle + + """ + self.send_set_digital_output_mode(num, type, freq, duty_cycle) + return self.recv_set_digital_output_mode() + + def send_set_digital_output_mode(self, num, type, freq, duty_cycle): + self._oprot.writeMessageBegin('set_digital_output_mode', TMessageType.CALL, self._seqid) + args = set_digital_output_mode_args() + args.num = num + args.type = type + args.freq = freq + args.duty_cycle = duty_cycle + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_digital_output_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_digital_output_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_digital_output_mode failed: unknown result") + + def set_standard_digital_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + self.send_set_standard_digital_out(num, value, block) + return self.recv_set_standard_digital_out() + + def send_set_standard_digital_out(self, num, value, block): + self._oprot.writeMessageBegin('set_standard_digital_out', TMessageType.CALL, self._seqid) + args = set_standard_digital_out_args() + args.num = num + args.value = value + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_standard_digital_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_standard_digital_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_standard_digital_out failed: unknown result") + + def set_tool_digital_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + self.send_set_tool_digital_out(num, value, block) + return self.recv_set_tool_digital_out() + + def send_set_tool_digital_out(self, num, value, block): + self._oprot.writeMessageBegin('set_tool_digital_out', TMessageType.CALL, self._seqid) + args = set_tool_digital_out_args() + args.num = num + args.value = value + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_tool_digital_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_tool_digital_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_tool_digital_out failed: unknown result") + + def get_standard_digital_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_standard_digital_in(num) + return self.recv_get_standard_digital_in() + + def send_get_standard_digital_in(self, num): + self._oprot.writeMessageBegin('get_standard_digital_in', TMessageType.CALL, self._seqid) + args = get_standard_digital_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_standard_digital_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_standard_digital_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_standard_digital_in failed: unknown result") + + def get_standard_digital_out(self, num): + """ + Parameters: + - num + + """ + self.send_get_standard_digital_out(num) + return self.recv_get_standard_digital_out() + + def send_get_standard_digital_out(self, num): + self._oprot.writeMessageBegin('get_standard_digital_out', TMessageType.CALL, self._seqid) + args = get_standard_digital_out_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_standard_digital_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_standard_digital_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_standard_digital_out failed: unknown result") + + def get_tool_digital_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_tool_digital_in(num) + return self.recv_get_tool_digital_in() + + def send_get_tool_digital_in(self, num): + self._oprot.writeMessageBegin('get_tool_digital_in', TMessageType.CALL, self._seqid) + args = get_tool_digital_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tool_digital_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tool_digital_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tool_digital_in failed: unknown result") + + def get_tool_digital_out(self, num): + """ + Parameters: + - num + + """ + self.send_get_tool_digital_out(num) + return self.recv_get_tool_digital_out() + + def send_get_tool_digital_out(self, num): + self._oprot.writeMessageBegin('get_tool_digital_out', TMessageType.CALL, self._seqid) + args = get_tool_digital_out_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tool_digital_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tool_digital_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tool_digital_out failed: unknown result") + + def get_config_digital_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_config_digital_in(num) + return self.recv_get_config_digital_in() + + def send_get_config_digital_in(self, num): + self._oprot.writeMessageBegin('get_config_digital_in', TMessageType.CALL, self._seqid) + args = get_config_digital_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_config_digital_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_config_digital_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_config_digital_in failed: unknown result") + + def get_standard_analog_voltage_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_standard_analog_voltage_in(num) + return self.recv_get_standard_analog_voltage_in() + + def send_get_standard_analog_voltage_in(self, num): + self._oprot.writeMessageBegin('get_standard_analog_voltage_in', TMessageType.CALL, self._seqid) + args = get_standard_analog_voltage_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_standard_analog_voltage_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_standard_analog_voltage_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_standard_analog_voltage_in failed: unknown result") + + def get_tool_analog_voltage_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_tool_analog_voltage_in(num) + return self.recv_get_tool_analog_voltage_in() + + def send_get_tool_analog_voltage_in(self, num): + self._oprot.writeMessageBegin('get_tool_analog_voltage_in', TMessageType.CALL, self._seqid) + args = get_tool_analog_voltage_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tool_analog_voltage_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tool_analog_voltage_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tool_analog_voltage_in failed: unknown result") + + def get_standard_analog_current_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_standard_analog_current_in(num) + return self.recv_get_standard_analog_current_in() + + def send_get_standard_analog_current_in(self, num): + self._oprot.writeMessageBegin('get_standard_analog_current_in', TMessageType.CALL, self._seqid) + args = get_standard_analog_current_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_standard_analog_current_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_standard_analog_current_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_standard_analog_current_in failed: unknown result") + + def set_standard_analog_voltage_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + self.send_set_standard_analog_voltage_out(num, value, block) + return self.recv_set_standard_analog_voltage_out() + + def send_set_standard_analog_voltage_out(self, num, value, block): + self._oprot.writeMessageBegin('set_standard_analog_voltage_out', TMessageType.CALL, self._seqid) + args = set_standard_analog_voltage_out_args() + args.num = num + args.value = value + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_standard_analog_voltage_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_standard_analog_voltage_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_standard_analog_voltage_out failed: unknown result") + + def set_standard_analog_current_out(self, num, value, block): + """ + Parameters: + - num + - value + - block + + """ + self.send_set_standard_analog_current_out(num, value, block) + return self.recv_set_standard_analog_current_out() + + def send_set_standard_analog_current_out(self, num, value, block): + self._oprot.writeMessageBegin('set_standard_analog_current_out', TMessageType.CALL, self._seqid) + args = set_standard_analog_current_out_args() + args.num = num + args.value = value + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_standard_analog_current_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_standard_analog_current_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_standard_analog_current_out failed: unknown result") + + def read_data_485(self): + self.send_read_data_485() + return self.recv_read_data_485() + + def send_read_data_485(self): + self._oprot.writeMessageBegin('read_data_485', TMessageType.CALL, self._seqid) + args = read_data_485_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_data_485 failed: unknown result") + + def read_raw_data_485(self, len): + """ + Parameters: + - len + + """ + self.send_read_raw_data_485(len) + return self.recv_read_raw_data_485() + + def send_read_raw_data_485(self, len): + self._oprot.writeMessageBegin('read_raw_data_485', TMessageType.CALL, self._seqid) + args = read_raw_data_485_args() + args.len = len + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_raw_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_raw_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_raw_data_485 failed: unknown result") + + def read_raw_data_485_ht(self, head, tail): + """ + Parameters: + - head + - tail + + """ + self.send_read_raw_data_485_ht(head, tail) + return self.recv_read_raw_data_485_ht() + + def send_read_raw_data_485_ht(self, head, tail): + self._oprot.writeMessageBegin('read_raw_data_485_ht', TMessageType.CALL, self._seqid) + args = read_raw_data_485_ht_args() + args.head = head + args.tail = tail + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_raw_data_485_ht(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_raw_data_485_ht_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_raw_data_485_ht failed: unknown result") + + def read_raw_data_485_h(self, head, len): + """ + Parameters: + - head + - len + + """ + self.send_read_raw_data_485_h(head, len) + return self.recv_read_raw_data_485_h() + + def send_read_raw_data_485_h(self, head, len): + self._oprot.writeMessageBegin('read_raw_data_485_h', TMessageType.CALL, self._seqid) + args = read_raw_data_485_h_args() + args.head = head + args.len = len + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_raw_data_485_h(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_raw_data_485_h_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_raw_data_485_h failed: unknown result") + + def write_data_485(self, data): + """ + Parameters: + - data + + """ + self.send_write_data_485(data) + return self.recv_write_data_485() + + def send_write_data_485(self, data): + self._oprot.writeMessageBegin('write_data_485', TMessageType.CALL, self._seqid) + args = write_data_485_args() + args.data = data + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_data_485 failed: unknown result") + + def write_raw_data_485(self, data): + """ + Parameters: + - data + + """ + self.send_write_raw_data_485(data) + return self.recv_write_raw_data_485() + + def send_write_raw_data_485(self, data): + self._oprot.writeMessageBegin('write_raw_data_485', TMessageType.CALL, self._seqid) + args = write_raw_data_485_args() + args.data = data + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_raw_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_raw_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_raw_data_485 failed: unknown result") + + def write_raw_data_485_h(self, data, head): + """ + Parameters: + - data + - head + + """ + self.send_write_raw_data_485_h(data, head) + return self.recv_write_raw_data_485_h() + + def send_write_raw_data_485_h(self, data, head): + self._oprot.writeMessageBegin('write_raw_data_485_h', TMessageType.CALL, self._seqid) + args = write_raw_data_485_h_args() + args.data = data + args.head = head + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_raw_data_485_h(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_raw_data_485_h_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_raw_data_485_h failed: unknown result") + + def write_raw_data_485_ht(self, data, head, tail): + """ + Parameters: + - data + - head + - tail + + """ + self.send_write_raw_data_485_ht(data, head, tail) + return self.recv_write_raw_data_485_ht() + + def send_write_raw_data_485_ht(self, data, head, tail): + self._oprot.writeMessageBegin('write_raw_data_485_ht', TMessageType.CALL, self._seqid) + args = write_raw_data_485_ht_args() + args.data = data + args.head = head + args.tail = tail + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_raw_data_485_ht(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_raw_data_485_ht_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_raw_data_485_ht failed: unknown result") + + def tool_read_data_485(self): + self.send_tool_read_data_485() + return self.recv_tool_read_data_485() + + def send_tool_read_data_485(self): + self._oprot.writeMessageBegin('tool_read_data_485', TMessageType.CALL, self._seqid) + args = tool_read_data_485_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_read_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_read_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_read_data_485 failed: unknown result") + + def tool_read_raw_data_485(self, len): + """ + Parameters: + - len + + """ + self.send_tool_read_raw_data_485(len) + return self.recv_tool_read_raw_data_485() + + def send_tool_read_raw_data_485(self, len): + self._oprot.writeMessageBegin('tool_read_raw_data_485', TMessageType.CALL, self._seqid) + args = tool_read_raw_data_485_args() + args.len = len + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_read_raw_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_read_raw_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_read_raw_data_485 failed: unknown result") + + def tool_read_raw_data_485_h(self, head, len): + """ + Parameters: + - head + - len + + """ + self.send_tool_read_raw_data_485_h(head, len) + return self.recv_tool_read_raw_data_485_h() + + def send_tool_read_raw_data_485_h(self, head, len): + self._oprot.writeMessageBegin('tool_read_raw_data_485_h', TMessageType.CALL, self._seqid) + args = tool_read_raw_data_485_h_args() + args.head = head + args.len = len + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_read_raw_data_485_h(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_read_raw_data_485_h_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_read_raw_data_485_h failed: unknown result") + + def tool_read_raw_data_485_ht(self, head, tail): + """ + Parameters: + - head + - tail + + """ + self.send_tool_read_raw_data_485_ht(head, tail) + return self.recv_tool_read_raw_data_485_ht() + + def send_tool_read_raw_data_485_ht(self, head, tail): + self._oprot.writeMessageBegin('tool_read_raw_data_485_ht', TMessageType.CALL, self._seqid) + args = tool_read_raw_data_485_ht_args() + args.head = head + args.tail = tail + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_read_raw_data_485_ht(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_read_raw_data_485_ht_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_read_raw_data_485_ht failed: unknown result") + + def tool_write_data_485(self, data): + """ + Parameters: + - data + + """ + self.send_tool_write_data_485(data) + return self.recv_tool_write_data_485() + + def send_tool_write_data_485(self, data): + self._oprot.writeMessageBegin('tool_write_data_485', TMessageType.CALL, self._seqid) + args = tool_write_data_485_args() + args.data = data + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_write_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_write_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_write_data_485 failed: unknown result") + + def tool_write_raw_data_485(self, data): + """ + Parameters: + - data + + """ + self.send_tool_write_raw_data_485(data) + return self.recv_tool_write_raw_data_485() + + def send_tool_write_raw_data_485(self, data): + self._oprot.writeMessageBegin('tool_write_raw_data_485', TMessageType.CALL, self._seqid) + args = tool_write_raw_data_485_args() + args.data = data + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_write_raw_data_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_write_raw_data_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_write_raw_data_485 failed: unknown result") + + def tool_write_raw_data_485_h(self, data, head): + """ + Parameters: + - data + - head + + """ + self.send_tool_write_raw_data_485_h(data, head) + return self.recv_tool_write_raw_data_485_h() + + def send_tool_write_raw_data_485_h(self, data, head): + self._oprot.writeMessageBegin('tool_write_raw_data_485_h', TMessageType.CALL, self._seqid) + args = tool_write_raw_data_485_h_args() + args.data = data + args.head = head + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_write_raw_data_485_h(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_write_raw_data_485_h_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_write_raw_data_485_h failed: unknown result") + + def tool_write_raw_data_485_ht(self, data, head, tail): + """ + Parameters: + - data + - head + - tail + + """ + self.send_tool_write_raw_data_485_ht(data, head, tail) + return self.recv_tool_write_raw_data_485_ht() + + def send_tool_write_raw_data_485_ht(self, data, head, tail): + self._oprot.writeMessageBegin('tool_write_raw_data_485_ht', TMessageType.CALL, self._seqid) + args = tool_write_raw_data_485_ht_args() + args.data = data + args.head = head + args.tail = tail + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tool_write_raw_data_485_ht(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tool_write_raw_data_485_ht_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tool_write_raw_data_485_ht failed: unknown result") + + def read_data_can(self): + self.send_read_data_can() + return self.recv_read_data_can() + + def send_read_data_can(self): + self._oprot.writeMessageBegin('read_data_can', TMessageType.CALL, self._seqid) + args = read_data_can_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_data_can(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_data_can_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_data_can failed: unknown result") + + def read_raw_data_can(self): + self.send_read_raw_data_can() + return self.recv_read_raw_data_can() + + def send_read_raw_data_can(self): + self._oprot.writeMessageBegin('read_raw_data_can', TMessageType.CALL, self._seqid) + args = read_raw_data_can_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_raw_data_can(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_raw_data_can_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_raw_data_can failed: unknown result") + + def write_data_can(self, id, data): + """ + Parameters: + - id + - data + + """ + self.send_write_data_can(id, data) + return self.recv_write_data_can() + + def send_write_data_can(self, id, data): + self._oprot.writeMessageBegin('write_data_can', TMessageType.CALL, self._seqid) + args = write_data_can_args() + args.id = id + args.data = data + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_data_can(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_data_can_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_data_can failed: unknown result") + + def write_raw_data_can(self, id, data): + """ + Parameters: + - id + - data + + """ + self.send_write_raw_data_can(id, data) + return self.recv_write_raw_data_can() + + def send_write_raw_data_can(self, id, data): + self._oprot.writeMessageBegin('write_raw_data_can', TMessageType.CALL, self._seqid) + args = write_raw_data_can_args() + args.id = id + args.data = data + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_raw_data_can(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_raw_data_can_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_raw_data_can failed: unknown result") + + def get_function_digital_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_function_digital_in(num) + return self.recv_get_function_digital_in() + + def send_get_function_digital_in(self, num): + self._oprot.writeMessageBegin('get_function_digital_in', TMessageType.CALL, self._seqid) + args = get_function_digital_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_function_digital_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_function_digital_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_function_digital_in failed: unknown result") + + def get_function_digital_out(self, num): + """ + Parameters: + - num + + """ + self.send_get_function_digital_out(num) + return self.recv_get_function_digital_out() + + def send_get_function_digital_out(self, num): + self._oprot.writeMessageBegin('get_function_digital_out', TMessageType.CALL, self._seqid) + args = get_function_digital_out_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_function_digital_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_function_digital_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_function_digital_out failed: unknown result") + + def read_bool_reg(self, num): + """ + Parameters: + - num + + """ + self.send_read_bool_reg(num) + return self.recv_read_bool_reg() + + def send_read_bool_reg(self, num): + self._oprot.writeMessageBegin('read_bool_reg', TMessageType.CALL, self._seqid) + args = read_bool_reg_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_bool_reg(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_bool_reg_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_bool_reg failed: unknown result") + + def read_word_reg(self, num): + """ + Parameters: + - num + + """ + self.send_read_word_reg(num) + return self.recv_read_word_reg() + + def send_read_word_reg(self, num): + self._oprot.writeMessageBegin('read_word_reg', TMessageType.CALL, self._seqid) + args = read_word_reg_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_word_reg(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_word_reg_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_word_reg failed: unknown result") + + def read_float_reg(self, num): + """ + Parameters: + - num + + """ + self.send_read_float_reg(num) + return self.recv_read_float_reg() + + def send_read_float_reg(self, num): + self._oprot.writeMessageBegin('read_float_reg', TMessageType.CALL, self._seqid) + args = read_float_reg_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_float_reg(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_float_reg_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_float_reg failed: unknown result") + + def write_bool_reg(self, num, value): + """ + Parameters: + - num + - value + + """ + self.send_write_bool_reg(num, value) + return self.recv_write_bool_reg() + + def send_write_bool_reg(self, num, value): + self._oprot.writeMessageBegin('write_bool_reg', TMessageType.CALL, self._seqid) + args = write_bool_reg_args() + args.num = num + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_bool_reg(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_bool_reg_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_bool_reg failed: unknown result") + + def write_word_reg(self, num, value): + """ + Parameters: + - num + - value + + """ + self.send_write_word_reg(num, value) + return self.recv_write_word_reg() + + def send_write_word_reg(self, num, value): + self._oprot.writeMessageBegin('write_word_reg', TMessageType.CALL, self._seqid) + args = write_word_reg_args() + args.num = num + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_word_reg(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_word_reg_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_word_reg failed: unknown result") + + def write_float_reg(self, num, value): + """ + Parameters: + - num + - value + + """ + self.send_write_float_reg(num, value) + return self.recv_write_float_reg() + + def send_write_float_reg(self, num, value): + self._oprot.writeMessageBegin('write_float_reg', TMessageType.CALL, self._seqid) + args = write_float_reg_args() + args.num = num + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_float_reg(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_float_reg_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_float_reg failed: unknown result") + + def get_function_reg_in(self, num): + """ + Parameters: + - num + + """ + self.send_get_function_reg_in(num) + return self.recv_get_function_reg_in() + + def send_get_function_reg_in(self, num): + self._oprot.writeMessageBegin('get_function_reg_in', TMessageType.CALL, self._seqid) + args = get_function_reg_in_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_function_reg_in(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_function_reg_in_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_function_reg_in failed: unknown result") + + def get_function_reg_out(self, num): + """ + Parameters: + - num + + """ + self.send_get_function_reg_out(num) + return self.recv_get_function_reg_out() + + def send_get_function_reg_out(self, num): + self._oprot.writeMessageBegin('get_function_reg_out', TMessageType.CALL, self._seqid) + args = get_function_reg_out_args() + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_function_reg_out(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_function_reg_out_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_function_reg_out failed: unknown result") + + def movej(self, joints_list, v, a, r, block, op, def_acc): + """ + Parameters: + - joints_list + - v + - a + - r + - block + - op + - def_acc + + """ + self.send_movej(joints_list, v, a, r, block, op, def_acc) + return self.recv_movej() + + def send_movej(self, joints_list, v, a, r, block, op, def_acc): + self._oprot.writeMessageBegin('movej', TMessageType.CALL, self._seqid) + args = movej_args() + args.joints_list = joints_list + args.v = v + args.a = a + args.r = r + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movej(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movej_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movej failed: unknown result") + + def movej_pose(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_movej_pose(p, v, a, r, q_near, tool, wobj, block, op, def_acc) + return self.recv_movej_pose() + + def send_movej_pose(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('movej_pose', TMessageType.CALL, self._seqid) + args = movej_pose_args() + args.p = p + args.v = v + args.a = a + args.r = r + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movej_pose(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movej_pose_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movej_pose failed: unknown result") + + def movej2(self, joints_list, v, a, r, block, op, def_acc): + """ + Parameters: + - joints_list + - v + - a + - r + - block + - op + - def_acc + + """ + self.send_movej2(joints_list, v, a, r, block, op, def_acc) + return self.recv_movej2() + + def send_movej2(self, joints_list, v, a, r, block, op, def_acc): + self._oprot.writeMessageBegin('movej2', TMessageType.CALL, self._seqid) + args = movej2_args() + args.joints_list = joints_list + args.v = v + args.a = a + args.r = r + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movej2(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movej2_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movej2 failed: unknown result") + + def movej_pose2(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_movej_pose2(p, v, a, r, q_near, tool, wobj, block, op, def_acc) + return self.recv_movej_pose2() + + def send_movej_pose2(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('movej_pose2', TMessageType.CALL, self._seqid) + args = movej_pose2_args() + args.p = p + args.v = v + args.a = a + args.r = r + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movej_pose2(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movej_pose2_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movej_pose2 failed: unknown result") + + def movel(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_movel(p, v, a, r, q_near, tool, wobj, block, op, def_acc) + return self.recv_movel() + + def send_movel(self, p, v, a, r, q_near, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('movel', TMessageType.CALL, self._seqid) + args = movel_args() + args.p = p + args.v = v + args.a = a + args.r = r + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movel(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movel_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movel failed: unknown result") + + def movec(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc, arc_rad): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - mode + - q_near + - tool + - wobj + - block + - op + - def_acc + - arc_rad + + """ + self.send_movec(p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc, arc_rad) + return self.recv_movec() + + def send_movec(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc, arc_rad): + self._oprot.writeMessageBegin('movec', TMessageType.CALL, self._seqid) + args = movec_args() + args.p1 = p1 + args.p2 = p2 + args.v = v + args.a = a + args.r = r + args.mode = mode + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.arc_rad = arc_rad + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movec(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movec_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movec failed: unknown result") + + def move_circle(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - mode + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_move_circle(p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc) + return self.recv_move_circle() + + def send_move_circle(self, p1, p2, v, a, r, mode, q_near, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('move_circle', TMessageType.CALL, self._seqid) + args = move_circle_args() + args.p1 = p1 + args.p2 = p2 + args.v = v + args.a = a + args.r = r + args.mode = mode + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_circle(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_circle_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_circle failed: unknown result") + + def tcp_move(self, pose_offset, v, a, r, tool, block, op, def_acc): + """ + Parameters: + - pose_offset + - v + - a + - r + - tool + - block + - op + - def_acc + + """ + self.send_tcp_move(pose_offset, v, a, r, tool, block, op, def_acc) + return self.recv_tcp_move() + + def send_tcp_move(self, pose_offset, v, a, r, tool, block, op, def_acc): + self._oprot.writeMessageBegin('tcp_move', TMessageType.CALL, self._seqid) + args = tcp_move_args() + args.pose_offset = pose_offset + args.v = v + args.a = a + args.r = r + args.tool = tool + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tcp_move(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tcp_move_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tcp_move failed: unknown result") + + def tcp_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_tcp_move_2p(p1, p2, v, a, r, tool, wobj, block, op, def_acc) + return self.recv_tcp_move_2p() + + def send_tcp_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('tcp_move_2p', TMessageType.CALL, self._seqid) + args = tcp_move_2p_args() + args.p1 = p1 + args.p2 = p2 + args.v = v + args.a = a + args.r = r + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_tcp_move_2p(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = tcp_move_2p_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "tcp_move_2p failed: unknown result") + + def wobj_move(self, pose_offset, v, a, r, wobj, block, op, def_acc): + """ + Parameters: + - pose_offset + - v + - a + - r + - wobj + - block + - op + - def_acc + + """ + self.send_wobj_move(pose_offset, v, a, r, wobj, block, op, def_acc) + return self.recv_wobj_move() + + def send_wobj_move(self, pose_offset, v, a, r, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('wobj_move', TMessageType.CALL, self._seqid) + args = wobj_move_args() + args.pose_offset = pose_offset + args.v = v + args.a = a + args.r = r + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_wobj_move(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = wobj_move_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "wobj_move failed: unknown result") + + def wobj_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - r + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_wobj_move_2p(p1, p2, v, a, r, tool, wobj, block, op, def_acc) + return self.recv_wobj_move_2p() + + def send_wobj_move_2p(self, p1, p2, v, a, r, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('wobj_move_2p', TMessageType.CALL, self._seqid) + args = wobj_move_2p_args() + args.p1 = p1 + args.p2 = p2 + args.v = v + args.a = a + args.r = r + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_wobj_move_2p(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = wobj_move_2p_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "wobj_move_2p failed: unknown result") + + def spline(self, pose_list, v, a, tool, wobj, block, op, r, def_acc): + """ + Parameters: + - pose_list + - v + - a + - tool + - wobj + - block + - op + - r + - def_acc + + """ + self.send_spline(pose_list, v, a, tool, wobj, block, op, r, def_acc) + return self.recv_spline() + + def send_spline(self, pose_list, v, a, tool, wobj, block, op, r, def_acc): + self._oprot.writeMessageBegin('spline', TMessageType.CALL, self._seqid) + args = spline_args() + args.pose_list = pose_list + args.v = v + args.a = a + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.r = r + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_spline(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = spline_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "spline failed: unknown result") + + def spline_op(self, pose_list, v, a, tool, wobj, block, op, r, def_acc): + """ + Parameters: + - pose_list + - v + - a + - tool + - wobj + - block + - op + - r + - def_acc + + """ + self.send_spline_op(pose_list, v, a, tool, wobj, block, op, r, def_acc) + return self.recv_spline_op() + + def send_spline_op(self, pose_list, v, a, tool, wobj, block, op, r, def_acc): + self._oprot.writeMessageBegin('spline_op', TMessageType.CALL, self._seqid) + args = spline_op_args() + args.pose_list = pose_list + args.v = v + args.a = a + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.r = r + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_spline_op(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = spline_op_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "spline_op failed: unknown result") + + def speedj(self, joints_list, a, time, block): + """ + Parameters: + - joints_list + - a + - time + - block + + """ + self.send_speedj(joints_list, a, time, block) + return self.recv_speedj() + + def send_speedj(self, joints_list, a, time, block): + self._oprot.writeMessageBegin('speedj', TMessageType.CALL, self._seqid) + args = speedj_args() + args.joints_list = joints_list + args.a = a + args.time = time + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_speedj(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = speedj_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "speedj failed: unknown result") + + def speedl(self, pose_list, a, time, block): + """ + Parameters: + - pose_list + - a + - time + - block + + """ + self.send_speedl(pose_list, a, time, block) + return self.recv_speedl() + + def send_speedl(self, pose_list, a, time, block): + self._oprot.writeMessageBegin('speedl', TMessageType.CALL, self._seqid) + args = speedl_args() + args.pose_list = pose_list + args.a = a + args.time = time + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_speedl(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = speedl_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "speedl failed: unknown result") + + def speed_stop(self, block): + """ + Parameters: + - block + + """ + self.send_speed_stop(block) + return self.recv_speed_stop() + + def send_speed_stop(self, block): + self._oprot.writeMessageBegin('speed_stop', TMessageType.CALL, self._seqid) + args = speed_stop_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_speed_stop(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = speed_stop_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "speed_stop failed: unknown result") + + def servoj(self, joints_list, v, a, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - joints_list + - v + - a + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + self.send_servoj(joints_list, v, a, block, kp, kd, smooth_vel, smooth_acc) + return self.recv_servoj() + + def send_servoj(self, joints_list, v, a, block, kp, kd, smooth_vel, smooth_acc): + self._oprot.writeMessageBegin('servoj', TMessageType.CALL, self._seqid) + args = servoj_args() + args.joints_list = joints_list + args.v = v + args.a = a + args.block = block + args.kp = kp + args.kd = kd + args.smooth_vel = smooth_vel + args.smooth_acc = smooth_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_servoj(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = servoj_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "servoj failed: unknown result") + + def servoj_pose(self, pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - pose_list + - v + - a + - q_near + - tool + - wobj + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + self.send_servoj_pose(pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc) + return self.recv_servoj_pose() + + def send_servoj_pose(self, pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc): + self._oprot.writeMessageBegin('servoj_pose', TMessageType.CALL, self._seqid) + args = servoj_pose_args() + args.pose_list = pose_list + args.v = v + args.a = a + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.kp = kp + args.kd = kd + args.smooth_vel = smooth_vel + args.smooth_acc = smooth_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_servoj_pose(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = servoj_pose_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "servoj_pose failed: unknown result") + + def servo_tcp(self, pose_offset, v, a, tool, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - pose_offset + - v + - a + - tool + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + self.send_servo_tcp(pose_offset, v, a, tool, block, kp, kd, smooth_vel, smooth_acc) + return self.recv_servo_tcp() + + def send_servo_tcp(self, pose_offset, v, a, tool, block, kp, kd, smooth_vel, smooth_acc): + self._oprot.writeMessageBegin('servo_tcp', TMessageType.CALL, self._seqid) + args = servo_tcp_args() + args.pose_offset = pose_offset + args.v = v + args.a = a + args.tool = tool + args.block = block + args.kp = kp + args.kd = kd + args.smooth_vel = smooth_vel + args.smooth_acc = smooth_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_servo_tcp(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = servo_tcp_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "servo_tcp failed: unknown result") + + def servol(self, pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc): + """ + Parameters: + - pose_list + - v + - a + - q_near + - tool + - wobj + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + self.send_servol(pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc) + return self.recv_servol() + + def send_servol(self, pose_list, v, a, q_near, tool, wobj, block, kp, kd, smooth_vel, smooth_acc): + self._oprot.writeMessageBegin('servol', TMessageType.CALL, self._seqid) + args = servol_args() + args.pose_list = pose_list + args.v = v + args.a = a + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.kp = kp + args.kd = kd + args.smooth_vel = smooth_vel + args.smooth_acc = smooth_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_servol(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = servol_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "servol failed: unknown result") + + def teach_mode(self, block): + """ + Parameters: + - block + + """ + self.send_teach_mode(block) + return self.recv_teach_mode() + + def send_teach_mode(self, block): + self._oprot.writeMessageBegin('teach_mode', TMessageType.CALL, self._seqid) + args = teach_mode_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_teach_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = teach_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "teach_mode failed: unknown result") + + def end_teach_mode(self, block): + """ + Parameters: + - block + + """ + self.send_end_teach_mode(block) + return self.recv_end_teach_mode() + + def send_end_teach_mode(self, block): + self._oprot.writeMessageBegin('end_teach_mode', TMessageType.CALL, self._seqid) + args = end_teach_mode_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_end_teach_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = end_teach_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "end_teach_mode failed: unknown result") + + def modbus_add_signal(self, ip, slave_number, signal_address, signal_type, signal_name): + """ + Parameters: + - ip + - slave_number + - signal_address + - signal_type + - signal_name + + """ + self.send_modbus_add_signal(ip, slave_number, signal_address, signal_type, signal_name) + return self.recv_modbus_add_signal() + + def send_modbus_add_signal(self, ip, slave_number, signal_address, signal_type, signal_name): + self._oprot.writeMessageBegin('modbus_add_signal', TMessageType.CALL, self._seqid) + args = modbus_add_signal_args() + args.ip = ip + args.slave_number = slave_number + args.signal_address = signal_address + args.signal_type = signal_type + args.signal_name = signal_name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_add_signal(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_add_signal_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "modbus_add_signal failed: unknown result") + + def modbus_delete_signal(self, signal_name): + """ + Parameters: + - signal_name + + """ + self.send_modbus_delete_signal(signal_name) + return self.recv_modbus_delete_signal() + + def send_modbus_delete_signal(self, signal_name): + self._oprot.writeMessageBegin('modbus_delete_signal', TMessageType.CALL, self._seqid) + args = modbus_delete_signal_args() + args.signal_name = signal_name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_delete_signal(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_delete_signal_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "modbus_delete_signal failed: unknown result") + + def modbus_read(self, signal_name): + """ + Parameters: + - signal_name + + """ + self.send_modbus_read(signal_name) + return self.recv_modbus_read() + + def send_modbus_read(self, signal_name): + self._oprot.writeMessageBegin('modbus_read', TMessageType.CALL, self._seqid) + args = modbus_read_args() + args.signal_name = signal_name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_read(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_read_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "modbus_read failed: unknown result") + + def modbus_write(self, signal_name, value): + """ + Parameters: + - signal_name + - value + + """ + self.send_modbus_write(signal_name, value) + return self.recv_modbus_write() + + def send_modbus_write(self, signal_name, value): + self._oprot.writeMessageBegin('modbus_write', TMessageType.CALL, self._seqid) + args = modbus_write_args() + args.signal_name = signal_name + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_write(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_write_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "modbus_write failed: unknown result") + + def modbus_set_frequency(self, signal_name, frequence): + """ + Parameters: + - signal_name + - frequence + + """ + self.send_modbus_set_frequency(signal_name, frequence) + self.recv_modbus_set_frequency() + + def send_modbus_set_frequency(self, signal_name, frequence): + self._oprot.writeMessageBegin('modbus_set_frequency', TMessageType.CALL, self._seqid) + args = modbus_set_frequency_args() + args.signal_name = signal_name + args.frequence = frequence + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_set_frequency(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_set_frequency_result() + result.read(iprot) + iprot.readMessageEnd() + return + + def get_last_error(self): + self.send_get_last_error() + return self.recv_get_last_error() + + def send_get_last_error(self): + self._oprot.writeMessageBegin('get_last_error', TMessageType.CALL, self._seqid) + args = get_last_error_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_last_error(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_last_error_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_last_error failed: unknown result") + + def get_noneblock_taskstate(self, id): + """ + Parameters: + - id + + """ + self.send_get_noneblock_taskstate(id) + return self.recv_get_noneblock_taskstate() + + def send_get_noneblock_taskstate(self, id): + self._oprot.writeMessageBegin('get_noneblock_taskstate', TMessageType.CALL, self._seqid) + args = get_noneblock_taskstate_args() + args.id = id + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_noneblock_taskstate(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_noneblock_taskstate_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_noneblock_taskstate failed: unknown result") + + def log_info(self, message): + """ + Parameters: + - message + + """ + self.send_log_info(message) + self.recv_log_info() + + def send_log_info(self, message): + self._oprot.writeMessageBegin('log_info', TMessageType.CALL, self._seqid) + args = log_info_args() + args.message = message + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_log_info(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = log_info_result() + result.read(iprot) + iprot.readMessageEnd() + return + + def log_error(self, message): + """ + Parameters: + - message + + """ + self.send_log_error(message) + self.recv_log_error() + + def send_log_error(self, message): + self._oprot.writeMessageBegin('log_error', TMessageType.CALL, self._seqid) + args = log_error_args() + args.message = message + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_log_error(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = log_error_result() + result.read(iprot) + iprot.readMessageEnd() + return + + def simulation(self, sim, block): + """ + Parameters: + - sim + - block + + """ + self.send_simulation(sim, block) + return self.recv_simulation() + + def send_simulation(self, sim, block): + self._oprot.writeMessageBegin('simulation', TMessageType.CALL, self._seqid) + args = simulation_args() + args.sim = sim + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_simulation(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = simulation_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "simulation failed: unknown result") + + def speed(self, val): + """ + Parameters: + - val + + """ + self.send_speed(val) + return self.recv_speed() + + def send_speed(self, val): + self._oprot.writeMessageBegin('speed', TMessageType.CALL, self._seqid) + args = speed_args() + args.val = val + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "speed failed: unknown result") + + def get_robot_state(self): + self.send_get_robot_state() + return self.recv_get_robot_state() + + def send_get_robot_state(self): + self._oprot.writeMessageBegin('get_robot_state', TMessageType.CALL, self._seqid) + args = get_robot_state_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_robot_state(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_robot_state_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_robot_state failed: unknown result") + + def get_flange_pose(self): + self.send_get_flange_pose() + return self.recv_get_flange_pose() + + def send_get_flange_pose(self): + self._oprot.writeMessageBegin('get_flange_pose', TMessageType.CALL, self._seqid) + args = get_flange_pose_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_flange_pose(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_flange_pose_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_flange_pose failed: unknown result") + + def get_flange_speed(self): + self.send_get_flange_speed() + return self.recv_get_flange_speed() + + def send_get_flange_speed(self): + self._oprot.writeMessageBegin('get_flange_speed', TMessageType.CALL, self._seqid) + args = get_flange_speed_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_flange_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_flange_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_flange_speed failed: unknown result") + + def get_flange_acceleration(self): + self.send_get_flange_acceleration() + return self.recv_get_flange_acceleration() + + def send_get_flange_acceleration(self): + self._oprot.writeMessageBegin('get_flange_acceleration', TMessageType.CALL, self._seqid) + args = get_flange_acceleration_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_flange_acceleration(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_flange_acceleration_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_flange_acceleration failed: unknown result") + + def get_tcp_pose(self): + self.send_get_tcp_pose() + return self.recv_get_tcp_pose() + + def send_get_tcp_pose(self): + self._oprot.writeMessageBegin('get_tcp_pose', TMessageType.CALL, self._seqid) + args = get_tcp_pose_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_pose(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_pose_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_pose failed: unknown result") + + def get_tcp_speed(self): + self.send_get_tcp_speed() + return self.recv_get_tcp_speed() + + def send_get_tcp_speed(self): + self._oprot.writeMessageBegin('get_tcp_speed', TMessageType.CALL, self._seqid) + args = get_tcp_speed_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_speed failed: unknown result") + + def get_tcp_acceleration(self): + self.send_get_tcp_acceleration() + return self.recv_get_tcp_acceleration() + + def send_get_tcp_acceleration(self): + self._oprot.writeMessageBegin('get_tcp_acceleration', TMessageType.CALL, self._seqid) + args = get_tcp_acceleration_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_acceleration(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_acceleration_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_acceleration failed: unknown result") + + def get_tcp_force(self): + self.send_get_tcp_force() + return self.recv_get_tcp_force() + + def send_get_tcp_force(self): + self._oprot.writeMessageBegin('get_tcp_force', TMessageType.CALL, self._seqid) + args = get_tcp_force_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_force(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_force_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_force failed: unknown result") + + def get_actual_joints_position(self): + self.send_get_actual_joints_position() + return self.recv_get_actual_joints_position() + + def send_get_actual_joints_position(self): + self._oprot.writeMessageBegin('get_actual_joints_position', TMessageType.CALL, self._seqid) + args = get_actual_joints_position_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_actual_joints_position(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_actual_joints_position_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_actual_joints_position failed: unknown result") + + def get_target_joints_position(self): + self.send_get_target_joints_position() + return self.recv_get_target_joints_position() + + def send_get_target_joints_position(self): + self._oprot.writeMessageBegin('get_target_joints_position', TMessageType.CALL, self._seqid) + args = get_target_joints_position_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_target_joints_position(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_target_joints_position_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_target_joints_position failed: unknown result") + + def get_actual_joints_speed(self): + self.send_get_actual_joints_speed() + return self.recv_get_actual_joints_speed() + + def send_get_actual_joints_speed(self): + self._oprot.writeMessageBegin('get_actual_joints_speed', TMessageType.CALL, self._seqid) + args = get_actual_joints_speed_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_actual_joints_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_actual_joints_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_actual_joints_speed failed: unknown result") + + def get_target_joints_speed(self): + self.send_get_target_joints_speed() + return self.recv_get_target_joints_speed() + + def send_get_target_joints_speed(self): + self._oprot.writeMessageBegin('get_target_joints_speed', TMessageType.CALL, self._seqid) + args = get_target_joints_speed_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_target_joints_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_target_joints_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_target_joints_speed failed: unknown result") + + def get_actual_joints_acceleration(self): + self.send_get_actual_joints_acceleration() + return self.recv_get_actual_joints_acceleration() + + def send_get_actual_joints_acceleration(self): + self._oprot.writeMessageBegin('get_actual_joints_acceleration', TMessageType.CALL, self._seqid) + args = get_actual_joints_acceleration_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_actual_joints_acceleration(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_actual_joints_acceleration_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_actual_joints_acceleration failed: unknown result") + + def get_target_joints_acceleration(self): + self.send_get_target_joints_acceleration() + return self.recv_get_target_joints_acceleration() + + def send_get_target_joints_acceleration(self): + self._oprot.writeMessageBegin('get_target_joints_acceleration', TMessageType.CALL, self._seqid) + args = get_target_joints_acceleration_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_target_joints_acceleration(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_target_joints_acceleration_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_target_joints_acceleration failed: unknown result") + + def get_actual_joints_torque(self): + self.send_get_actual_joints_torque() + return self.recv_get_actual_joints_torque() + + def send_get_actual_joints_torque(self): + self._oprot.writeMessageBegin('get_actual_joints_torque', TMessageType.CALL, self._seqid) + args = get_actual_joints_torque_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_actual_joints_torque(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_actual_joints_torque_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_actual_joints_torque failed: unknown result") + + def get_target_joints_torque(self): + self.send_get_target_joints_torque() + return self.recv_get_target_joints_torque() + + def send_get_target_joints_torque(self): + self._oprot.writeMessageBegin('get_target_joints_torque', TMessageType.CALL, self._seqid) + args = get_target_joints_torque_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_target_joints_torque(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_target_joints_torque_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_target_joints_torque failed: unknown result") + + def stop_record_track(self): + self.send_stop_record_track() + return self.recv_stop_record_track() + + def send_stop_record_track(self): + self._oprot.writeMessageBegin('stop_record_track', TMessageType.CALL, self._seqid) + args = stop_record_track_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_stop_record_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = stop_record_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "stop_record_track failed: unknown result") + + def start_record_track(self, name, mode, tool, wobj, interval): + """ + Parameters: + - name + - mode + - tool + - wobj + - interval + + """ + self.send_start_record_track(name, mode, tool, wobj, interval) + return self.recv_start_record_track() + + def send_start_record_track(self, name, mode, tool, wobj, interval): + self._oprot.writeMessageBegin('start_record_track', TMessageType.CALL, self._seqid) + args = start_record_track_args() + args.name = name + args.mode = mode + args.tool = tool + args.wobj = wobj + args.interval = interval + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_start_record_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = start_record_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "start_record_track failed: unknown result") + + def collision_detect(self, value): + """ + Parameters: + - value + + """ + self.send_collision_detect(value) + return self.recv_collision_detect() + + def send_collision_detect(self, value): + self._oprot.writeMessageBegin('collision_detect', TMessageType.CALL, self._seqid) + args = collision_detect_args() + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_collision_detect(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = collision_detect_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "collision_detect failed: unknown result") + + def replay(self, name, value, mode): + """ + Parameters: + - name + - value + - mode + + """ + self.send_replay(name, value, mode) + return self.recv_replay() + + def send_replay(self, name, value, mode): + self._oprot.writeMessageBegin('replay', TMessageType.CALL, self._seqid) + args = replay_args() + args.name = name + args.value = value + args.mode = mode + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_replay(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = replay_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "replay failed: unknown result") + + def set_load_data(self, value): + """ + Parameters: + - value + + """ + self.send_set_load_data(value) + return self.recv_set_load_data() + + def send_set_load_data(self, value): + self._oprot.writeMessageBegin('set_load_data', TMessageType.CALL, self._seqid) + args = set_load_data_args() + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_load_data(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_load_data_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_load_data failed: unknown result") + + def fc_start(self): + self.send_fc_start() + return self.recv_fc_start() + + def send_fc_start(self): + self._oprot.writeMessageBegin('fc_start', TMessageType.CALL, self._seqid) + args = fc_start_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_start(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_start_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_start failed: unknown result") + + def fc_stop(self): + self.send_fc_stop() + return self.recv_fc_stop() + + def send_fc_stop(self): + self._oprot.writeMessageBegin('fc_stop', TMessageType.CALL, self._seqid) + args = fc_stop_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_stop(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_stop_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_stop failed: unknown result") + + def fc_config(self, direction, ref_ft, damp, max_vel, number_list, tool, wobj, value): + """ + Parameters: + - direction + - ref_ft + - damp + - max_vel + - number_list + - tool + - wobj + - value + + """ + self.send_fc_config(direction, ref_ft, damp, max_vel, number_list, tool, wobj, value) + return self.recv_fc_config() + + def send_fc_config(self, direction, ref_ft, damp, max_vel, number_list, tool, wobj, value): + self._oprot.writeMessageBegin('fc_config', TMessageType.CALL, self._seqid) + args = fc_config_args() + args.direction = direction + args.ref_ft = ref_ft + args.damp = damp + args.max_vel = max_vel + args.number_list = number_list + args.tool = tool + args.wobj = wobj + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_config(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_config_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_config failed: unknown result") + + def fc_move(self, block): + """ + Parameters: + - block + + """ + self.send_fc_move(block) + return self.recv_fc_move() + + def send_fc_move(self, block): + self._oprot.writeMessageBegin('fc_move', TMessageType.CALL, self._seqid) + args = fc_move_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_move(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_move_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_move failed: unknown result") + + def fc_guard_act(self, direction, ref_ft, tool, wobj, type, force_property): + """ + Parameters: + - direction + - ref_ft + - tool + - wobj + - type + - force_property + + """ + self.send_fc_guard_act(direction, ref_ft, tool, wobj, type, force_property) + return self.recv_fc_guard_act() + + def send_fc_guard_act(self, direction, ref_ft, tool, wobj, type, force_property): + self._oprot.writeMessageBegin('fc_guard_act', TMessageType.CALL, self._seqid) + args = fc_guard_act_args() + args.direction = direction + args.ref_ft = ref_ft + args.tool = tool + args.wobj = wobj + args.type = type + args.force_property = force_property + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_guard_act(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_guard_act_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_guard_act failed: unknown result") + + def fc_guard_deact(self): + self.send_fc_guard_deact() + return self.recv_fc_guard_deact() + + def send_fc_guard_deact(self): + self._oprot.writeMessageBegin('fc_guard_deact', TMessageType.CALL, self._seqid) + args = fc_guard_deact_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_guard_deact(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_guard_deact_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_guard_deact failed: unknown result") + + def fc_force_set_value(self, direction, ref_ft): + """ + Parameters: + - direction + - ref_ft + + """ + self.send_fc_force_set_value(direction, ref_ft) + return self.recv_fc_force_set_value() + + def send_fc_force_set_value(self, direction, ref_ft): + self._oprot.writeMessageBegin('fc_force_set_value', TMessageType.CALL, self._seqid) + args = fc_force_set_value_args() + args.direction = direction + args.ref_ft = ref_ft + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_force_set_value(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_force_set_value_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_force_set_value failed: unknown result") + + def fc_wait_pos(self, middle, range, absolute, duration, timeout): + """ + Parameters: + - middle + - range + - absolute + - duration + - timeout + + """ + self.send_fc_wait_pos(middle, range, absolute, duration, timeout) + return self.recv_fc_wait_pos() + + def send_fc_wait_pos(self, middle, range, absolute, duration, timeout): + self._oprot.writeMessageBegin('fc_wait_pos', TMessageType.CALL, self._seqid) + args = fc_wait_pos_args() + args.middle = middle + args.range = range + args.absolute = absolute + args.duration = duration + args.timeout = timeout + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_wait_pos(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_wait_pos_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_wait_pos failed: unknown result") + + def fc_wait_vel(self, middle, range, absolute, duration, timeout): + """ + Parameters: + - middle + - range + - absolute + - duration + - timeout + + """ + self.send_fc_wait_vel(middle, range, absolute, duration, timeout) + return self.recv_fc_wait_vel() + + def send_fc_wait_vel(self, middle, range, absolute, duration, timeout): + self._oprot.writeMessageBegin('fc_wait_vel', TMessageType.CALL, self._seqid) + args = fc_wait_vel_args() + args.middle = middle + args.range = range + args.absolute = absolute + args.duration = duration + args.timeout = timeout + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_wait_vel(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_wait_vel_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_wait_vel failed: unknown result") + + def fc_wait_ft(self, middle, range, absolute, duration, timeout): + """ + Parameters: + - middle + - range + - absolute + - duration + - timeout + + """ + self.send_fc_wait_ft(middle, range, absolute, duration, timeout) + return self.recv_fc_wait_ft() + + def send_fc_wait_ft(self, middle, range, absolute, duration, timeout): + self._oprot.writeMessageBegin('fc_wait_ft', TMessageType.CALL, self._seqid) + args = fc_wait_ft_args() + args.middle = middle + args.range = range + args.absolute = absolute + args.duration = duration + args.timeout = timeout + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_wait_ft(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_wait_ft_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_wait_ft failed: unknown result") + + def fc_wait_logic(self, value): + """ + Parameters: + - value + + """ + self.send_fc_wait_logic(value) + return self.recv_fc_wait_logic() + + def send_fc_wait_logic(self, value): + self._oprot.writeMessageBegin('fc_wait_logic', TMessageType.CALL, self._seqid) + args = fc_wait_logic_args() + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_wait_logic(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_wait_logic_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_wait_logic failed: unknown result") + + def fc_get_ft(self): + self.send_fc_get_ft() + return self.recv_fc_get_ft() + + def send_fc_get_ft(self): + self._oprot.writeMessageBegin('fc_get_ft', TMessageType.CALL, self._seqid) + args = fc_get_ft_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_get_ft(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_get_ft_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_get_ft failed: unknown result") + + def fc_mode_is_active(self): + self.send_fc_mode_is_active() + return self.recv_fc_mode_is_active() + + def send_fc_mode_is_active(self): + self._oprot.writeMessageBegin('fc_mode_is_active', TMessageType.CALL, self._seqid) + args = fc_mode_is_active_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_fc_mode_is_active(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = fc_mode_is_active_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "fc_mode_is_active failed: unknown result") + + def start_realtime_mode(self, mode, fileter_bandwidth, com_lost_time): + """ + Parameters: + - mode + - fileter_bandwidth + - com_lost_time + + """ + self.send_start_realtime_mode(mode, fileter_bandwidth, com_lost_time) + return self.recv_start_realtime_mode() + + def send_start_realtime_mode(self, mode, fileter_bandwidth, com_lost_time): + self._oprot.writeMessageBegin('start_realtime_mode', TMessageType.CALL, self._seqid) + args = start_realtime_mode_args() + args.mode = mode + args.fileter_bandwidth = fileter_bandwidth + args.com_lost_time = com_lost_time + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_start_realtime_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = start_realtime_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "start_realtime_mode failed: unknown result") + + def end_realtime_mode(self): + self.send_end_realtime_mode() + return self.recv_end_realtime_mode() + + def send_end_realtime_mode(self): + self._oprot.writeMessageBegin('end_realtime_mode', TMessageType.CALL, self._seqid) + args = end_realtime_mode_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_end_realtime_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = end_realtime_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "end_realtime_mode failed: unknown result") + + def realtime_data_enqueue(self, realtime_data, block): + """ + Parameters: + - realtime_data + - block + + """ + self.send_realtime_data_enqueue(realtime_data, block) + return self.recv_realtime_data_enqueue() + + def send_realtime_data_enqueue(self, realtime_data, block): + self._oprot.writeMessageBegin('realtime_data_enqueue', TMessageType.CALL, self._seqid) + args = realtime_data_enqueue_args() + args.realtime_data = realtime_data + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_realtime_data_enqueue(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = realtime_data_enqueue_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "realtime_data_enqueue failed: unknown result") + + def clear_realtime_data_queue(self): + self.send_clear_realtime_data_queue() + return self.recv_clear_realtime_data_queue() + + def send_clear_realtime_data_queue(self): + self._oprot.writeMessageBegin('clear_realtime_data_queue', TMessageType.CALL, self._seqid) + args = clear_realtime_data_queue_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_clear_realtime_data_queue(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = clear_realtime_data_queue_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "clear_realtime_data_queue failed: unknown result") + + def get_realtime_data_queue_size(self): + self.send_get_realtime_data_queue_size() + return self.recv_get_realtime_data_queue_size() + + def send_get_realtime_data_queue_size(self): + self._oprot.writeMessageBegin('get_realtime_data_queue_size', TMessageType.CALL, self._seqid) + args = get_realtime_data_queue_size_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_realtime_data_queue_size(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_realtime_data_queue_size_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_realtime_data_queue_size failed: unknown result") + + def enable_speed_optimization(self): + self.send_enable_speed_optimization() + return self.recv_enable_speed_optimization() + + def send_enable_speed_optimization(self): + self._oprot.writeMessageBegin('enable_speed_optimization', TMessageType.CALL, self._seqid) + args = enable_speed_optimization_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_speed_optimization(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_speed_optimization_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_speed_optimization failed: unknown result") + + def disable_speed_optimization(self): + self.send_disable_speed_optimization() + return self.recv_disable_speed_optimization() + + def send_disable_speed_optimization(self): + self._oprot.writeMessageBegin('disable_speed_optimization', TMessageType.CALL, self._seqid) + args = disable_speed_optimization_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_speed_optimization(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_speed_optimization_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_speed_optimization failed: unknown result") + + def change_recipe(self): + self.send_change_recipe() + self.recv_change_recipe() + + def send_change_recipe(self): + self._oprot.writeMessageBegin('change_recipe', TMessageType.CALL, self._seqid) + args = change_recipe_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_change_recipe(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = change_recipe_result() + result.read(iprot) + iprot.readMessageEnd() + return + + def set_system_value_bool(self, name, value): + """ + Parameters: + - name + - value + + """ + self.send_set_system_value_bool(name, value) + return self.recv_set_system_value_bool() + + def send_set_system_value_bool(self, name, value): + self._oprot.writeMessageBegin('set_system_value_bool', TMessageType.CALL, self._seqid) + args = set_system_value_bool_args() + args.name = name + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_system_value_bool(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_system_value_bool_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_system_value_bool failed: unknown result") + + def set_system_value_double(self, name, value): + """ + Parameters: + - name + - value + + """ + self.send_set_system_value_double(name, value) + return self.recv_set_system_value_double() + + def send_set_system_value_double(self, name, value): + self._oprot.writeMessageBegin('set_system_value_double', TMessageType.CALL, self._seqid) + args = set_system_value_double_args() + args.name = name + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_system_value_double(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_system_value_double_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_system_value_double failed: unknown result") + + def set_system_value_str(self, name, value): + """ + Parameters: + - name + - value + + """ + self.send_set_system_value_str(name, value) + return self.recv_set_system_value_str() + + def send_set_system_value_str(self, name, value): + self._oprot.writeMessageBegin('set_system_value_str', TMessageType.CALL, self._seqid) + args = set_system_value_str_args() + args.name = name + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_system_value_str(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_system_value_str_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_system_value_str failed: unknown result") + + def set_system_value_list(self, name, value): + """ + Parameters: + - name + - value + + """ + self.send_set_system_value_list(name, value) + return self.recv_set_system_value_list() + + def send_set_system_value_list(self, name, value): + self._oprot.writeMessageBegin('set_system_value_list', TMessageType.CALL, self._seqid) + args = set_system_value_list_args() + args.name = name + args.value = value + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_system_value_list(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_system_value_list_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_system_value_list failed: unknown result") + + def get_system_value_bool(self, name): + """ + Parameters: + - name + + """ + self.send_get_system_value_bool(name) + return self.recv_get_system_value_bool() + + def send_get_system_value_bool(self, name): + self._oprot.writeMessageBegin('get_system_value_bool', TMessageType.CALL, self._seqid) + args = get_system_value_bool_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_system_value_bool(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_system_value_bool_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_system_value_bool failed: unknown result") + + def get_system_value_double(self, name): + """ + Parameters: + - name + + """ + self.send_get_system_value_double(name) + return self.recv_get_system_value_double() + + def send_get_system_value_double(self, name): + self._oprot.writeMessageBegin('get_system_value_double', TMessageType.CALL, self._seqid) + args = get_system_value_double_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_system_value_double(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_system_value_double_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_system_value_double failed: unknown result") + + def get_system_value_str(self, name): + """ + Parameters: + - name + + """ + self.send_get_system_value_str(name) + return self.recv_get_system_value_str() + + def send_get_system_value_str(self, name): + self._oprot.writeMessageBegin('get_system_value_str', TMessageType.CALL, self._seqid) + args = get_system_value_str_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_system_value_str(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_system_value_str_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_system_value_str failed: unknown result") + + def get_system_value_list(self, name): + """ + Parameters: + - name + + """ + self.send_get_system_value_list(name) + return self.recv_get_system_value_list() + + def send_get_system_value_list(self, name): + self._oprot.writeMessageBegin('get_system_value_list', TMessageType.CALL, self._seqid) + args = get_system_value_list_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_system_value_list(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_system_value_list_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_system_value_list failed: unknown result") + + def trackEnqueue(self, track, block): + """ + Parameters: + - track + - block + + """ + self.send_trackEnqueue(track, block) + return self.recv_trackEnqueue() + + def send_trackEnqueue(self, track, block): + self._oprot.writeMessageBegin('trackEnqueue', TMessageType.CALL, self._seqid) + args = trackEnqueue_args() + args.track = track + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_trackEnqueue(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = trackEnqueue_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "trackEnqueue failed: unknown result") + + def trackEnqueueOp(self, track, block): + """ + Parameters: + - track + - block + + """ + self.send_trackEnqueueOp(track, block) + return self.recv_trackEnqueueOp() + + def send_trackEnqueueOp(self, track, block): + self._oprot.writeMessageBegin('trackEnqueueOp', TMessageType.CALL, self._seqid) + args = trackEnqueueOp_args() + args.track = track + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_trackEnqueueOp(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = trackEnqueueOp_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "trackEnqueueOp failed: unknown result") + + def trackClearQueue(self): + self.send_trackClearQueue() + return self.recv_trackClearQueue() + + def send_trackClearQueue(self): + self._oprot.writeMessageBegin('trackClearQueue', TMessageType.CALL, self._seqid) + args = trackClearQueue_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_trackClearQueue(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = trackClearQueue_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "trackClearQueue failed: unknown result") + + def getQueueSize(self): + self.send_getQueueSize() + return self.recv_getQueueSize() + + def send_getQueueSize(self): + self._oprot.writeMessageBegin('getQueueSize', TMessageType.CALL, self._seqid) + args = getQueueSize_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_getQueueSize(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = getQueueSize_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueueSize failed: unknown result") + + def trackJointMotion(self, speed, acc, block): + """ + Parameters: + - speed + - acc + - block + + """ + self.send_trackJointMotion(speed, acc, block) + return self.recv_trackJointMotion() + + def send_trackJointMotion(self, speed, acc, block): + self._oprot.writeMessageBegin('trackJointMotion', TMessageType.CALL, self._seqid) + args = trackJointMotion_args() + args.speed = speed + args.acc = acc + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_trackJointMotion(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = trackJointMotion_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "trackJointMotion failed: unknown result") + + def trackCartMotion(self, speed, acc, block, tool, wobj, radius): + """ + Parameters: + - speed + - acc + - block + - tool + - wobj + - radius + + """ + self.send_trackCartMotion(speed, acc, block, tool, wobj, radius) + return self.recv_trackCartMotion() + + def send_trackCartMotion(self, speed, acc, block, tool, wobj, radius): + self._oprot.writeMessageBegin('trackCartMotion', TMessageType.CALL, self._seqid) + args = trackCartMotion_args() + args.speed = speed + args.acc = acc + args.block = block + args.tool = tool + args.wobj = wobj + args.radius = radius + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_trackCartMotion(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = trackCartMotion_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "trackCartMotion failed: unknown result") + + def rpc_heartbeat(self, time): + """ + Parameters: + - time + + """ + self.send_rpc_heartbeat(time) + self.recv_rpc_heartbeat() + + def send_rpc_heartbeat(self, time): + self._oprot.writeMessageBegin('rpc_heartbeat', TMessageType.CALL, self._seqid) + args = rpc_heartbeat_args() + args.time = time + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_rpc_heartbeat(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = rpc_heartbeat_result() + result.read(iprot) + iprot.readMessageEnd() + return + + def move_spiral(self, p1, p2, rev, len, r, mode, v, a, q_near, tool, wobj, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - rev + - len + - r + - mode + - v + - a + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + self.send_move_spiral(p1, p2, rev, len, r, mode, v, a, q_near, tool, wobj, block, op, def_acc) + return self.recv_move_spiral() + + def send_move_spiral(self, p1, p2, rev, len, r, mode, v, a, q_near, tool, wobj, block, op, def_acc): + self._oprot.writeMessageBegin('move_spiral', TMessageType.CALL, self._seqid) + args = move_spiral_args() + args.p1 = p1 + args.p2 = p2 + args.rev = rev + args.len = len + args.r = r + args.mode = mode + args.v = v + args.a = a + args.q_near = q_near + args.tool = tool + args.wobj = wobj + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_spiral(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_spiral_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_spiral failed: unknown result") + + def enable_acc_optimization(self): + self.send_enable_acc_optimization() + return self.recv_enable_acc_optimization() + + def send_enable_acc_optimization(self): + self._oprot.writeMessageBegin('enable_acc_optimization', TMessageType.CALL, self._seqid) + args = enable_acc_optimization_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_acc_optimization(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_acc_optimization_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_acc_optimization failed: unknown result") + + def disable_acc_optimization(self): + self.send_disable_acc_optimization() + return self.recv_disable_acc_optimization() + + def send_disable_acc_optimization(self): + self._oprot.writeMessageBegin('disable_acc_optimization', TMessageType.CALL, self._seqid) + args = disable_acc_optimization_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_acc_optimization(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_acc_optimization_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_acc_optimization failed: unknown result") + + def set_baudrate_485(self, value, block): + """ + Parameters: + - value + - block + + """ + self.send_set_baudrate_485(value, block) + return self.recv_set_baudrate_485() + + def send_set_baudrate_485(self, value, block): + self._oprot.writeMessageBegin('set_baudrate_485', TMessageType.CALL, self._seqid) + args = set_baudrate_485_args() + args.value = value + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_baudrate_485(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_baudrate_485_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_baudrate_485 failed: unknown result") + + def set_baudrate_can(self, value, block): + """ + Parameters: + - value + - block + + """ + self.send_set_baudrate_can(value, block) + return self.recv_set_baudrate_can() + + def send_set_baudrate_can(self, value, block): + self._oprot.writeMessageBegin('set_baudrate_can', TMessageType.CALL, self._seqid) + args = set_baudrate_can_args() + args.value = value + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_baudrate_can(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_baudrate_can_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_baudrate_can failed: unknown result") + + def set_analog_output_mode(self, num, mode, block): + """ + Parameters: + - num + - mode + - block + + """ + self.send_set_analog_output_mode(num, mode, block) + return self.recv_set_analog_output_mode() + + def send_set_analog_output_mode(self, num, mode, block): + self._oprot.writeMessageBegin('set_analog_output_mode', TMessageType.CALL, self._seqid) + args = set_analog_output_mode_args() + args.num = num + args.mode = mode + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_analog_output_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_analog_output_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_analog_output_mode failed: unknown result") + + def robotmoving(self): + self.send_robotmoving() + return self.recv_robotmoving() + + def send_robotmoving(self): + self._oprot.writeMessageBegin('robotmoving', TMessageType.CALL, self._seqid) + args = robotmoving_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_robotmoving(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = robotmoving_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "robotmoving failed: unknown result") + + def modbus_write_multiple_coils(self, slave_num, name, len, byte_list): + """ + Parameters: + - slave_num + - name + - len + - byte_list + + """ + self.send_modbus_write_multiple_coils(slave_num, name, len, byte_list) + return self.recv_modbus_write_multiple_coils() + + def send_modbus_write_multiple_coils(self, slave_num, name, len, byte_list): + self._oprot.writeMessageBegin('modbus_write_multiple_coils', TMessageType.CALL, self._seqid) + args = modbus_write_multiple_coils_args() + args.slave_num = slave_num + args.name = name + args.len = len + args.byte_list = byte_list + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_write_multiple_coils(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_write_multiple_coils_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "modbus_write_multiple_coils failed: unknown result") + + def modbus_write_multiple_regs(self, slave_num, name, len, word_list): + """ + Parameters: + - slave_num + - name + - len + - word_list + + """ + self.send_modbus_write_multiple_regs(slave_num, name, len, word_list) + return self.recv_modbus_write_multiple_regs() + + def send_modbus_write_multiple_regs(self, slave_num, name, len, word_list): + self._oprot.writeMessageBegin('modbus_write_multiple_regs', TMessageType.CALL, self._seqid) + args = modbus_write_multiple_regs_args() + args.slave_num = slave_num + args.name = name + args.len = len + args.word_list = word_list + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_modbus_write_multiple_regs(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = modbus_write_multiple_regs_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "modbus_write_multiple_regs failed: unknown result") + + def get_current_project(self): + self.send_get_current_project() + return self.recv_get_current_project() + + def send_get_current_project(self): + self._oprot.writeMessageBegin('get_current_project', TMessageType.CALL, self._seqid) + args = get_current_project_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_current_project(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_current_project_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_current_project failed: unknown result") + + def get_files_list(self, path): + """ + Parameters: + - path + + """ + self.send_get_files_list(path) + return self.recv_get_files_list() + + def send_get_files_list(self, path): + self._oprot.writeMessageBegin('get_files_list', TMessageType.CALL, self._seqid) + args = get_files_list_args() + args.path = path + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_files_list(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_files_list_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_files_list failed: unknown result") + + def getRobotStatus(self): + self.send_getRobotStatus() + return self.recv_getRobotStatus() + + def send_getRobotStatus(self): + self._oprot.writeMessageBegin('getRobotStatus', TMessageType.CALL, self._seqid) + args = getRobotStatus_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_getRobotStatus(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = getRobotStatus_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "getRobotStatus failed: unknown result") + + def getRobotIOStatus(self): + self.send_getRobotIOStatus() + return self.recv_getRobotIOStatus() + + def send_getRobotIOStatus(self): + self._oprot.writeMessageBegin('getRobotIOStatus', TMessageType.CALL, self._seqid) + args = getRobotIOStatus_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_getRobotIOStatus(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = getRobotIOStatus_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "getRobotIOStatus failed: unknown result") + + def get_tcp_pose_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + self.send_get_tcp_pose_coord(tool, wobj) + return self.recv_get_tcp_pose_coord() + + def send_get_tcp_pose_coord(self, tool, wobj): + self._oprot.writeMessageBegin('get_tcp_pose_coord', TMessageType.CALL, self._seqid) + args = get_tcp_pose_coord_args() + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_pose_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_pose_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_pose_coord failed: unknown result") + + def get_tcp_force_tool(self, tool): + """ + Parameters: + - tool + + """ + self.send_get_tcp_force_tool(tool) + return self.recv_get_tcp_force_tool() + + def send_get_tcp_force_tool(self, tool): + self._oprot.writeMessageBegin('get_tcp_force_tool', TMessageType.CALL, self._seqid) + args = get_tcp_force_tool_args() + args.tool = tool + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_force_tool(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_force_tool_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_force_tool failed: unknown result") + + def restart(self, block): + """ + Parameters: + - block + + """ + self.send_restart(block) + return self.recv_restart() + + def send_restart(self, block): + self._oprot.writeMessageBegin('restart', TMessageType.CALL, self._seqid) + args = restart_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_restart(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = restart_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "restart failed: unknown result") + + def set_servo_config(self, axis_num, id, value, qfmt, block): + """ + Parameters: + - axis_num + - id + - value + - qfmt + - block + + """ + self.send_set_servo_config(axis_num, id, value, qfmt, block) + return self.recv_set_servo_config() + + def send_set_servo_config(self, axis_num, id, value, qfmt, block): + self._oprot.writeMessageBegin('set_servo_config', TMessageType.CALL, self._seqid) + args = set_servo_config_args() + args.axis_num = axis_num + args.id = id + args.value = value + args.qfmt = qfmt + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_servo_config(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_servo_config_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_servo_config failed: unknown result") + + def apply_servo_config(self, axis_num, block): + """ + Parameters: + - axis_num + - block + + """ + self.send_apply_servo_config(axis_num, block) + return self.recv_apply_servo_config() + + def send_apply_servo_config(self, axis_num, block): + self._oprot.writeMessageBegin('apply_servo_config', TMessageType.CALL, self._seqid) + args = apply_servo_config_args() + args.axis_num = axis_num + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_apply_servo_config(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = apply_servo_config_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "apply_servo_config failed: unknown result") + + def get_motor_pole_pair_number(self): + self.send_get_motor_pole_pair_number() + return self.recv_get_motor_pole_pair_number() + + def send_get_motor_pole_pair_number(self): + self._oprot.writeMessageBegin('get_motor_pole_pair_number', TMessageType.CALL, self._seqid) + args = get_motor_pole_pair_number_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_motor_pole_pair_number(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_motor_pole_pair_number_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_motor_pole_pair_number failed: unknown result") + + def get_motor_stator_slots(self): + self.send_get_motor_stator_slots() + return self.recv_get_motor_stator_slots() + + def send_get_motor_stator_slots(self): + self._oprot.writeMessageBegin('get_motor_stator_slots', TMessageType.CALL, self._seqid) + args = get_motor_stator_slots_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_motor_stator_slots(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_motor_stator_slots_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_motor_stator_slots failed: unknown result") + + def get_axis_ratio(self): + self.send_get_axis_ratio() + return self.recv_get_axis_ratio() + + def send_get_axis_ratio(self): + self._oprot.writeMessageBegin('get_axis_ratio', TMessageType.CALL, self._seqid) + args = get_axis_ratio_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_axis_ratio(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_axis_ratio_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_axis_ratio failed: unknown result") + + def collision_detection_reset(self): + self.send_collision_detection_reset() + return self.recv_collision_detection_reset() + + def send_collision_detection_reset(self): + self._oprot.writeMessageBegin('collision_detection_reset', TMessageType.CALL, self._seqid) + args = collision_detection_reset_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_collision_detection_reset(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = collision_detection_reset_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "collision_detection_reset failed: unknown result") + + def set_servo_file_params(self, axis_num, id, name, value, qfmt): + """ + Parameters: + - axis_num + - id + - name + - value + - qfmt + + """ + self.send_set_servo_file_params(axis_num, id, name, value, qfmt) + return self.recv_set_servo_file_params() + + def send_set_servo_file_params(self, axis_num, id, name, value, qfmt): + self._oprot.writeMessageBegin('set_servo_file_params', TMessageType.CALL, self._seqid) + args = set_servo_file_params_args() + args.axis_num = axis_num + args.id = id + args.name = name + args.value = value + args.qfmt = qfmt + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_servo_file_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_servo_file_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_servo_file_params failed: unknown result") + + def combine_motion_config(self, type, ref_plane, fq, amp, el_offset, az_offset, up_height, time, path_dwell, pendulum_height, op_list): + """ + Parameters: + - type + - ref_plane + - fq + - amp + - el_offset + - az_offset + - up_height + - time + - path_dwell + - pendulum_height + - op_list + + """ + self.send_combine_motion_config(type, ref_plane, fq, amp, el_offset, az_offset, up_height, time, path_dwell, pendulum_height, op_list) + return self.recv_combine_motion_config() + + def send_combine_motion_config(self, type, ref_plane, fq, amp, el_offset, az_offset, up_height, time, path_dwell, pendulum_height, op_list): + self._oprot.writeMessageBegin('combine_motion_config', TMessageType.CALL, self._seqid) + args = combine_motion_config_args() + args.type = type + args.ref_plane = ref_plane + args.fq = fq + args.amp = amp + args.el_offset = el_offset + args.az_offset = az_offset + args.up_height = up_height + args.time = time + args.path_dwell = path_dwell + args.pendulum_height = pendulum_height + args.op_list = op_list + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_combine_motion_config(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = combine_motion_config_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "combine_motion_config failed: unknown result") + + def set_eaxis_param(self, num, param, block): + """ + Parameters: + - num + - param + - block + + """ + self.send_set_eaxis_param(num, param, block) + return self.recv_set_eaxis_param() + + def send_set_eaxis_param(self, num, param, block): + self._oprot.writeMessageBegin('set_eaxis_param', TMessageType.CALL, self._seqid) + args = set_eaxis_param_args() + args.num = num + args.param = param + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_eaxis_param(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_eaxis_param_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_eaxis_param failed: unknown result") + + def add_eaxis_scheme(self, num, block): + """ + Parameters: + - num + - block + + """ + self.send_add_eaxis_scheme(num, block) + return self.recv_add_eaxis_scheme() + + def send_add_eaxis_scheme(self, num, block): + self._oprot.writeMessageBegin('add_eaxis_scheme', TMessageType.CALL, self._seqid) + args = add_eaxis_scheme_args() + args.num = num + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_add_eaxis_scheme(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = add_eaxis_scheme_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "add_eaxis_scheme failed: unknown result") + + def delete_eaxis_scheme(self, num, block): + """ + Parameters: + - num + - block + + """ + self.send_delete_eaxis_scheme(num, block) + return self.recv_delete_eaxis_scheme() + + def send_delete_eaxis_scheme(self, num, block): + self._oprot.writeMessageBegin('delete_eaxis_scheme', TMessageType.CALL, self._seqid) + args = delete_eaxis_scheme_args() + args.num = num + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_delete_eaxis_scheme(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = delete_eaxis_scheme_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "delete_eaxis_scheme failed: unknown result") + + def enable_eaxis_scheme(self, scheme_name): + """ + Parameters: + - scheme_name + + """ + self.send_enable_eaxis_scheme(scheme_name) + return self.recv_enable_eaxis_scheme() + + def send_enable_eaxis_scheme(self, scheme_name): + self._oprot.writeMessageBegin('enable_eaxis_scheme', TMessageType.CALL, self._seqid) + args = enable_eaxis_scheme_args() + args.scheme_name = scheme_name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_eaxis_scheme(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_eaxis_scheme_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_eaxis_scheme failed: unknown result") + + def disable_eaxis_scheme(self, scheme_name): + """ + Parameters: + - scheme_name + + """ + self.send_disable_eaxis_scheme(scheme_name) + return self.recv_disable_eaxis_scheme() + + def send_disable_eaxis_scheme(self, scheme_name): + self._oprot.writeMessageBegin('disable_eaxis_scheme', TMessageType.CALL, self._seqid) + args = disable_eaxis_scheme_args() + args.scheme_name = scheme_name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_eaxis_scheme(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_eaxis_scheme_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_eaxis_scheme failed: unknown result") + + def set_eaxiss_scheme_param(self, num, param, block): + """ + Parameters: + - num + - param + - block + + """ + self.send_set_eaxiss_scheme_param(num, param, block) + return self.recv_set_eaxiss_scheme_param() + + def send_set_eaxiss_scheme_param(self, num, param, block): + self._oprot.writeMessageBegin('set_eaxiss_scheme_param', TMessageType.CALL, self._seqid) + args = set_eaxiss_scheme_param_args() + args.num = num + args.param = param + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_eaxiss_scheme_param(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_eaxiss_scheme_param_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_eaxiss_scheme_param failed: unknown result") + + def move_jog(self, param, block): + """ + Parameters: + - param + - block + + """ + self.send_move_jog(param, block) + return self.recv_move_jog() + + def send_move_jog(self, param, block): + self._oprot.writeMessageBegin('move_jog', TMessageType.CALL, self._seqid) + args = move_jog_args() + args.param = param + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_jog(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_jog_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_jog failed: unknown result") + + def stop_manual_move(self, block): + """ + Parameters: + - block + + """ + self.send_stop_manual_move(block) + return self.recv_stop_manual_move() + + def send_stop_manual_move(self, block): + self._oprot.writeMessageBegin('stop_manual_move', TMessageType.CALL, self._seqid) + args = stop_manual_move_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_stop_manual_move(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = stop_manual_move_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "stop_manual_move failed: unknown result") + + def get_robot_version(self): + self.send_get_robot_version() + return self.recv_get_robot_version() + + def send_get_robot_version(self): + self._oprot.writeMessageBegin('get_robot_version', TMessageType.CALL, self._seqid) + args = get_robot_version_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_robot_version(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_robot_version_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_robot_version failed: unknown result") + + def set_teach_pendant(self, enable): + """ + Parameters: + - enable + + """ + self.send_set_teach_pendant(enable) + return self.recv_set_teach_pendant() + + def send_set_teach_pendant(self, enable): + self._oprot.writeMessageBegin('set_teach_pendant', TMessageType.CALL, self._seqid) + args = set_teach_pendant_args() + args.enable = enable + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_teach_pendant(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_teach_pendant_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_teach_pendant failed: unknown result") + + def get_teach_speed(self): + self.send_get_teach_speed() + return self.recv_get_teach_speed() + + def send_get_teach_speed(self): + self._oprot.writeMessageBegin('get_teach_speed', TMessageType.CALL, self._seqid) + args = get_teach_speed_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_teach_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_teach_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_teach_speed failed: unknown result") + + def get_global_speed(self): + self.send_get_global_speed() + return self.recv_get_global_speed() + + def send_get_global_speed(self): + self._oprot.writeMessageBegin('get_global_speed', TMessageType.CALL, self._seqid) + args = get_global_speed_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_global_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_global_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_global_speed failed: unknown result") + + def set_teach_speed(self, v): + """ + Parameters: + - v + + """ + self.send_set_teach_speed(v) + return self.recv_set_teach_speed() + + def send_set_teach_speed(self, v): + self._oprot.writeMessageBegin('set_teach_speed', TMessageType.CALL, self._seqid) + args = set_teach_speed_args() + args.v = v + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_teach_speed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_teach_speed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_teach_speed failed: unknown result") + + def enable_combine_motion(self): + self.send_enable_combine_motion() + return self.recv_enable_combine_motion() + + def send_enable_combine_motion(self): + self._oprot.writeMessageBegin('enable_combine_motion', TMessageType.CALL, self._seqid) + args = enable_combine_motion_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_combine_motion(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_combine_motion_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_combine_motion failed: unknown result") + + def disable_combine_motion(self): + self.send_disable_combine_motion() + return self.recv_disable_combine_motion() + + def send_disable_combine_motion(self): + self._oprot.writeMessageBegin('disable_combine_motion', TMessageType.CALL, self._seqid) + args = disable_combine_motion_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_combine_motion(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_combine_motion_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_combine_motion failed: unknown result") + + def enable_singularity_control(self): + self.send_enable_singularity_control() + return self.recv_enable_singularity_control() + + def send_enable_singularity_control(self): + self._oprot.writeMessageBegin('enable_singularity_control', TMessageType.CALL, self._seqid) + args = enable_singularity_control_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_singularity_control(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_singularity_control_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_singularity_control failed: unknown result") + + def disable_singularity_control(self): + self.send_disable_singularity_control() + return self.recv_disable_singularity_control() + + def send_disable_singularity_control(self): + self._oprot.writeMessageBegin('disable_singularity_control', TMessageType.CALL, self._seqid) + args = disable_singularity_control_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_singularity_control(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_singularity_control_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_singularity_control failed: unknown result") + + def enable_vibration_control(self): + self.send_enable_vibration_control() + return self.recv_enable_vibration_control() + + def send_enable_vibration_control(self): + self._oprot.writeMessageBegin('enable_vibration_control', TMessageType.CALL, self._seqid) + args = enable_vibration_control_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_vibration_control(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_vibration_control_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_vibration_control failed: unknown result") + + def disable_vibration_control(self): + self.send_disable_vibration_control() + return self.recv_disable_vibration_control() + + def send_disable_vibration_control(self): + self._oprot.writeMessageBegin('disable_vibration_control', TMessageType.CALL, self._seqid) + args = disable_vibration_control_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_vibration_control(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_vibration_control_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_vibration_control failed: unknown result") + + def move_eaxis(self, scheme_name, epose, v, block, op): + """ + Parameters: + - scheme_name + - epose + - v + - block + - op + + """ + self.send_move_eaxis(scheme_name, epose, v, block, op) + return self.recv_move_eaxis() + + def send_move_eaxis(self, scheme_name, epose, v, block, op): + self._oprot.writeMessageBegin('move_eaxis', TMessageType.CALL, self._seqid) + args = move_eaxis_args() + args.scheme_name = scheme_name + args.epose = epose + args.v = v + args.block = block + args.op = op + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_eaxis failed: unknown result") + + def movej2_eaxis(self, joints_list, v, a, rad, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - joints_list + - v + - a + - rad + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + self.send_movej2_eaxis(joints_list, v, a, rad, scheme_name, epose, eaxis_v, block, op, def_acc) + return self.recv_movej2_eaxis() + + def send_movej2_eaxis(self, joints_list, v, a, rad, scheme_name, epose, eaxis_v, block, op, def_acc): + self._oprot.writeMessageBegin('movej2_eaxis', TMessageType.CALL, self._seqid) + args = movej2_eaxis_args() + args.joints_list = joints_list + args.v = v + args.a = a + args.rad = rad + args.scheme_name = scheme_name + args.epose = epose + args.eaxis_v = eaxis_v + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movej2_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movej2_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movej2_eaxis failed: unknown result") + + def movej2_pose_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + self.send_movej2_pose_eaxis(p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc) + return self.recv_movej2_pose_eaxis() + + def send_movej2_pose_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + self._oprot.writeMessageBegin('movej2_pose_eaxis', TMessageType.CALL, self._seqid) + args = movej2_pose_eaxis_args() + args.p = p + args.v = v + args.a = a + args.rad = rad + args.qnear = qnear + args.tool = tool + args.wobj = wobj + args.scheme_name = scheme_name + args.epose = epose + args.eaxis_v = eaxis_v + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movej2_pose_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movej2_pose_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movej2_pose_eaxis failed: unknown result") + + def movel_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - p + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + self.send_movel_eaxis(p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc) + return self.recv_movel_eaxis() + + def send_movel_eaxis(self, p, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + self._oprot.writeMessageBegin('movel_eaxis', TMessageType.CALL, self._seqid) + args = movel_eaxis_args() + args.p = p + args.v = v + args.a = a + args.rad = rad + args.qnear = qnear + args.tool = tool + args.wobj = wobj + args.scheme_name = scheme_name + args.epose = epose + args.eaxis_v = eaxis_v + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movel_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movel_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movel_eaxis failed: unknown result") + + def movec_eaxis(self, p1, p2, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc, mode, arc_rad): + """ + Parameters: + - p1 + - p2 + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + - mode + - arc_rad + + """ + self.send_movec_eaxis(p1, p2, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc, mode, arc_rad) + return self.recv_movec_eaxis() + + def send_movec_eaxis(self, p1, p2, v, a, rad, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc, mode, arc_rad): + self._oprot.writeMessageBegin('movec_eaxis', TMessageType.CALL, self._seqid) + args = movec_eaxis_args() + args.p1 = p1 + args.p2 = p2 + args.v = v + args.a = a + args.rad = rad + args.qnear = qnear + args.tool = tool + args.wobj = wobj + args.scheme_name = scheme_name + args.epose = epose + args.eaxis_v = eaxis_v + args.block = block + args.op = op + args.def_acc = def_acc + args.mode = mode + args.arc_rad = arc_rad + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_movec_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = movec_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "movec_eaxis failed: unknown result") + + def move_circle_eaxis(self, p1, p2, v, a, rad, mode, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - p1 + - p2 + - v + - a + - rad + - mode + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + self.send_move_circle_eaxis(p1, p2, v, a, rad, mode, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc) + return self.recv_move_circle_eaxis() + + def send_move_circle_eaxis(self, p1, p2, v, a, rad, mode, qnear, tool, wobj, scheme_name, epose, eaxis_v, block, op, def_acc): + self._oprot.writeMessageBegin('move_circle_eaxis', TMessageType.CALL, self._seqid) + args = move_circle_eaxis_args() + args.p1 = p1 + args.p2 = p2 + args.v = v + args.a = a + args.rad = rad + args.mode = mode + args.qnear = qnear + args.tool = tool + args.wobj = wobj + args.scheme_name = scheme_name + args.epose = epose + args.eaxis_v = eaxis_v + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_circle_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_circle_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_circle_eaxis failed: unknown result") + + def reach_check(self, base, wobj, tool, ref_pos, check_points): + """ + Parameters: + - base + - wobj + - tool + - ref_pos + - check_points + + """ + self.send_reach_check(base, wobj, tool, ref_pos, check_points) + return self.recv_reach_check() + + def send_reach_check(self, base, wobj, tool, ref_pos, check_points): + self._oprot.writeMessageBegin('reach_check', TMessageType.CALL, self._seqid) + args = reach_check_args() + args.base = base + args.wobj = wobj + args.tool = tool + args.ref_pos = ref_pos + args.check_points = check_points + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_reach_check(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = reach_check_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "reach_check failed: unknown result") + + def move_jog_eaxis(self, name, direction, vel, block): + """ + Parameters: + - name + - direction + - vel + - block + + """ + self.send_move_jog_eaxis(name, direction, vel, block) + return self.recv_move_jog_eaxis() + + def send_move_jog_eaxis(self, name, direction, vel, block): + self._oprot.writeMessageBegin('move_jog_eaxis', TMessageType.CALL, self._seqid) + args = move_jog_eaxis_args() + args.name = name + args.direction = direction + args.vel = vel + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_jog_eaxis(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_jog_eaxis_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_jog_eaxis failed: unknown result") + + def get_eaxis_info(self): + self.send_get_eaxis_info() + return self.recv_get_eaxis_info() + + def send_get_eaxis_info(self): + self._oprot.writeMessageBegin('get_eaxis_info', TMessageType.CALL, self._seqid) + args = get_eaxis_info_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_eaxis_info(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_eaxis_info_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_eaxis_info failed: unknown result") + + def set_hand_teach_parameter(self, space, joint_scale, cart_scale, coord_type, direction, global_scale): + """ + Parameters: + - space + - joint_scale + - cart_scale + - coord_type + - direction + - global_scale + + """ + self.send_set_hand_teach_parameter(space, joint_scale, cart_scale, coord_type, direction, global_scale) + return self.recv_set_hand_teach_parameter() + + def send_set_hand_teach_parameter(self, space, joint_scale, cart_scale, coord_type, direction, global_scale): + self._oprot.writeMessageBegin('set_hand_teach_parameter', TMessageType.CALL, self._seqid) + args = set_hand_teach_parameter_args() + args.space = space + args.joint_scale = joint_scale + args.cart_scale = cart_scale + args.coord_type = coord_type + args.direction = direction + args.global_scale = global_scale + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_hand_teach_parameter(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_hand_teach_parameter_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_hand_teach_parameter failed: unknown result") + + def set_pendant_type(self, type): + """ + Parameters: + - type + + """ + self.send_set_pendant_type(type) + return self.recv_set_pendant_type() + + def send_set_pendant_type(self, type): + self._oprot.writeMessageBegin('set_pendant_type', TMessageType.CALL, self._seqid) + args = set_pendant_type_args() + args.type = type + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_pendant_type(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_pendant_type_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_pendant_type failed: unknown result") + + def set_blend_ahead(self, per, num): + """ + Parameters: + - per + - num + + """ + self.send_set_blend_ahead(per, num) + return self.recv_set_blend_ahead() + + def send_set_blend_ahead(self, per, num): + self._oprot.writeMessageBegin('set_blend_ahead', TMessageType.CALL, self._seqid) + args = set_blend_ahead_args() + args.per = per + args.num = num + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_blend_ahead(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_blend_ahead_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_blend_ahead failed: unknown result") + + def switch_mode(self, mode): + """ + Parameters: + - mode + + """ + self.send_switch_mode(mode) + return self.recv_switch_mode() + + def send_switch_mode(self, mode): + self._oprot.writeMessageBegin('switch_mode', TMessageType.CALL, self._seqid) + args = switch_mode_args() + args.mode = mode + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_switch_mode(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = switch_mode_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "switch_mode failed: unknown result") + + def read_encoder_count(self): + self.send_read_encoder_count() + return self.recv_read_encoder_count() + + def send_read_encoder_count(self): + self._oprot.writeMessageBegin('read_encoder_count', TMessageType.CALL, self._seqid) + args = read_encoder_count_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_read_encoder_count(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = read_encoder_count_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "read_encoder_count failed: unknown result") + + def set_kinematic_calibration_params(self, params): + """ + Parameters: + - params + + """ + self.send_set_kinematic_calibration_params(params) + return self.recv_set_kinematic_calibration_params() + + def send_set_kinematic_calibration_params(self, params): + self._oprot.writeMessageBegin('set_kinematic_calibration_params', TMessageType.CALL, self._seqid) + args = set_kinematic_calibration_params_args() + args.params = params + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_kinematic_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_kinematic_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_kinematic_calibration_params failed: unknown result") + + def get_pos_bias(self): + self.send_get_pos_bias() + return self.recv_get_pos_bias() + + def send_get_pos_bias(self): + self._oprot.writeMessageBegin('get_pos_bias', TMessageType.CALL, self._seqid) + args = get_pos_bias_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_pos_bias(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_pos_bias_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_pos_bias failed: unknown result") + + def get_system_value_lists(self, name): + """ + Parameters: + - name + + """ + self.send_get_system_value_lists(name) + return self.recv_get_system_value_lists() + + def send_get_system_value_lists(self, name): + self._oprot.writeMessageBegin('get_system_value_lists', TMessageType.CALL, self._seqid) + args = get_system_value_lists_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_system_value_lists(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_system_value_lists_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_system_value_lists failed: unknown result") + + def get_origin_DH(self): + self.send_get_origin_DH() + return self.recv_get_origin_DH() + + def send_get_origin_DH(self): + self._oprot.writeMessageBegin('get_origin_DH', TMessageType.CALL, self._seqid) + args = get_origin_DH_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_origin_DH(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_origin_DH_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_origin_DH failed: unknown result") + + def get_calib_DH(self): + self.send_get_calib_DH() + return self.recv_get_calib_DH() + + def send_get_calib_DH(self): + self._oprot.writeMessageBegin('get_calib_DH', TMessageType.CALL, self._seqid) + args = get_calib_DH_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_calib_DH(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_calib_DH_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_calib_DH failed: unknown result") + + def get_robot_type(self): + self.send_get_robot_type() + return self.recv_get_robot_type() + + def send_get_robot_type(self): + self._oprot.writeMessageBegin('get_robot_type', TMessageType.CALL, self._seqid) + args = get_robot_type_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_robot_type(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_robot_type_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_robot_type failed: unknown result") + + def get_ext_torque(self): + self.send_get_ext_torque() + return self.recv_get_ext_torque() + + def send_get_ext_torque(self): + self._oprot.writeMessageBegin('get_ext_torque', TMessageType.CALL, self._seqid) + args = get_ext_torque_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_ext_torque(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_ext_torque_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_ext_torque failed: unknown result") + + def set_dynamic_calibration_params(self, params): + """ + Parameters: + - params + + """ + self.send_set_dynamic_calibration_params(params) + return self.recv_set_dynamic_calibration_params() + + def send_set_dynamic_calibration_params(self, params): + self._oprot.writeMessageBegin('set_dynamic_calibration_params', TMessageType.CALL, self._seqid) + args = set_dynamic_calibration_params_args() + args.params = params + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_dynamic_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_dynamic_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_dynamic_calibration_params failed: unknown result") + + def get_dynamic_calibration_params(self): + self.send_get_dynamic_calibration_params() + return self.recv_get_dynamic_calibration_params() + + def send_get_dynamic_calibration_params(self): + self._oprot.writeMessageBegin('get_dynamic_calibration_params', TMessageType.CALL, self._seqid) + args = get_dynamic_calibration_params_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_dynamic_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_dynamic_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_dynamic_calibration_params failed: unknown result") + + def upload_robot_param_to_toolboard(self, passwd): + """ + Parameters: + - passwd + + """ + self.send_upload_robot_param_to_toolboard(passwd) + return self.recv_upload_robot_param_to_toolboard() + + def send_upload_robot_param_to_toolboard(self, passwd): + self._oprot.writeMessageBegin('upload_robot_param_to_toolboard', TMessageType.CALL, self._seqid) + args = upload_robot_param_to_toolboard_args() + args.passwd = passwd + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_upload_robot_param_to_toolboard(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = upload_robot_param_to_toolboard_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "upload_robot_param_to_toolboard failed: unknown result") + + def set_kinematic_calibration_info(self, passwd, info): + """ + Parameters: + - passwd + - info + + """ + self.send_set_kinematic_calibration_info(passwd, info) + return self.recv_set_kinematic_calibration_info() + + def send_set_kinematic_calibration_info(self, passwd, info): + self._oprot.writeMessageBegin('set_kinematic_calibration_info', TMessageType.CALL, self._seqid) + args = set_kinematic_calibration_info_args() + args.passwd = passwd + args.info = info + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_kinematic_calibration_info(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_kinematic_calibration_info_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_kinematic_calibration_info failed: unknown result") + + def set_dynamic_calibration_info(self, passwd, info): + """ + Parameters: + - passwd + - info + + """ + self.send_set_dynamic_calibration_info(passwd, info) + return self.recv_set_dynamic_calibration_info() + + def send_set_dynamic_calibration_info(self, passwd, info): + self._oprot.writeMessageBegin('set_dynamic_calibration_info', TMessageType.CALL, self._seqid) + args = set_dynamic_calibration_info_args() + args.passwd = passwd + args.info = info + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_dynamic_calibration_info(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_dynamic_calibration_info_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_dynamic_calibration_info failed: unknown result") + + def set_vibration_calibration_info(self, passwd, info): + """ + Parameters: + - passwd + - info + + """ + self.send_set_vibration_calibration_info(passwd, info) + return self.recv_set_vibration_calibration_info() + + def send_set_vibration_calibration_info(self, passwd, info): + self._oprot.writeMessageBegin('set_vibration_calibration_info', TMessageType.CALL, self._seqid) + args = set_vibration_calibration_info_args() + args.passwd = passwd + args.info = info + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_vibration_calibration_info(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_vibration_calibration_info_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_vibration_calibration_info failed: unknown result") + + def get_axis_motor_rated_current(self): + self.send_get_axis_motor_rated_current() + return self.recv_get_axis_motor_rated_current() + + def send_get_axis_motor_rated_current(self): + self._oprot.writeMessageBegin('get_axis_motor_rated_current', TMessageType.CALL, self._seqid) + args = get_axis_motor_rated_current_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_axis_motor_rated_current(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_axis_motor_rated_current_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_axis_motor_rated_current failed: unknown result") + + def get_axis_motor_kt(self): + self.send_get_axis_motor_kt() + return self.recv_get_axis_motor_kt() + + def send_get_axis_motor_kt(self): + self._oprot.writeMessageBegin('get_axis_motor_kt', TMessageType.CALL, self._seqid) + args = get_axis_motor_kt_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_axis_motor_kt(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_axis_motor_kt_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_axis_motor_kt failed: unknown result") + + def abort(self, block): + """ + Parameters: + - block + + """ + self.send_abort(block) + return self.recv_abort() + + def send_abort(self, block): + self._oprot.writeMessageBegin('abort', TMessageType.CALL, self._seqid) + args = abort_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_abort(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = abort_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "abort failed: unknown result") + + def get_vibration_calibration_params(self): + self.send_get_vibration_calibration_params() + return self.recv_get_vibration_calibration_params() + + def send_get_vibration_calibration_params(self): + self._oprot.writeMessageBegin('get_vibration_calibration_params', TMessageType.CALL, self._seqid) + args = get_vibration_calibration_params_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_vibration_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_vibration_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_vibration_calibration_params failed: unknown result") + + def save_kinematic_calibration_params(self, passwd): + """ + Parameters: + - passwd + + """ + self.send_save_kinematic_calibration_params(passwd) + return self.recv_save_kinematic_calibration_params() + + def send_save_kinematic_calibration_params(self, passwd): + self._oprot.writeMessageBegin('save_kinematic_calibration_params', TMessageType.CALL, self._seqid) + args = save_kinematic_calibration_params_args() + args.passwd = passwd + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_save_kinematic_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = save_kinematic_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "save_kinematic_calibration_params failed: unknown result") + + def save_dynamic_calibration_params(self, passwd): + """ + Parameters: + - passwd + + """ + self.send_save_dynamic_calibration_params(passwd) + return self.recv_save_dynamic_calibration_params() + + def send_save_dynamic_calibration_params(self, passwd): + self._oprot.writeMessageBegin('save_dynamic_calibration_params', TMessageType.CALL, self._seqid) + args = save_dynamic_calibration_params_args() + args.passwd = passwd + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_save_dynamic_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = save_dynamic_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "save_dynamic_calibration_params failed: unknown result") + + def get_flange_pose_cmd(self): + self.send_get_flange_pose_cmd() + return self.recv_get_flange_pose_cmd() + + def send_get_flange_pose_cmd(self): + self._oprot.writeMessageBegin('get_flange_pose_cmd', TMessageType.CALL, self._seqid) + args = get_flange_pose_cmd_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_flange_pose_cmd(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_flange_pose_cmd_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_flange_pose_cmd failed: unknown result") + + def get_flange_speed_cmd(self): + self.send_get_flange_speed_cmd() + return self.recv_get_flange_speed_cmd() + + def send_get_flange_speed_cmd(self): + self._oprot.writeMessageBegin('get_flange_speed_cmd', TMessageType.CALL, self._seqid) + args = get_flange_speed_cmd_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_flange_speed_cmd(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_flange_speed_cmd_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_flange_speed_cmd failed: unknown result") + + def get_flange_acceleration_cmd(self): + self.send_get_flange_acceleration_cmd() + return self.recv_get_flange_acceleration_cmd() + + def send_get_flange_acceleration_cmd(self): + self._oprot.writeMessageBegin('get_flange_acceleration_cmd', TMessageType.CALL, self._seqid) + args = get_flange_acceleration_cmd_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_flange_acceleration_cmd(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_flange_acceleration_cmd_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_flange_acceleration_cmd failed: unknown result") + + def get_tcp_pose_command(self): + self.send_get_tcp_pose_command() + return self.recv_get_tcp_pose_command() + + def send_get_tcp_pose_command(self): + self._oprot.writeMessageBegin('get_tcp_pose_command', TMessageType.CALL, self._seqid) + args = get_tcp_pose_command_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_pose_command(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_pose_command_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_pose_command failed: unknown result") + + def get_tcp_speed_command(self): + self.send_get_tcp_speed_command() + return self.recv_get_tcp_speed_command() + + def send_get_tcp_speed_command(self): + self._oprot.writeMessageBegin('get_tcp_speed_command', TMessageType.CALL, self._seqid) + args = get_tcp_speed_command_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_speed_command(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_speed_command_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_speed_command failed: unknown result") + + def get_tcp_acceleration_command(self): + self.send_get_tcp_acceleration_command() + return self.recv_get_tcp_acceleration_command() + + def send_get_tcp_acceleration_command(self): + self._oprot.writeMessageBegin('get_tcp_acceleration_command', TMessageType.CALL, self._seqid) + args = get_tcp_acceleration_command_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_acceleration_command(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_acceleration_command_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_acceleration_command failed: unknown result") + + def get_tcp_pose_command_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + self.send_get_tcp_pose_command_coord(tool, wobj) + return self.recv_get_tcp_pose_command_coord() + + def send_get_tcp_pose_command_coord(self, tool, wobj): + self._oprot.writeMessageBegin('get_tcp_pose_command_coord', TMessageType.CALL, self._seqid) + args = get_tcp_pose_command_coord_args() + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_pose_command_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_pose_command_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_pose_command_coord failed: unknown result") + + def get_tcp_speed_command_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + self.send_get_tcp_speed_command_coord(tool, wobj) + return self.recv_get_tcp_speed_command_coord() + + def send_get_tcp_speed_command_coord(self, tool, wobj): + self._oprot.writeMessageBegin('get_tcp_speed_command_coord', TMessageType.CALL, self._seqid) + args = get_tcp_speed_command_coord_args() + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_speed_command_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_speed_command_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_speed_command_coord failed: unknown result") + + def get_tcp_acceleration_command_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + self.send_get_tcp_acceleration_command_coord(tool, wobj) + return self.recv_get_tcp_acceleration_command_coord() + + def send_get_tcp_acceleration_command_coord(self, tool, wobj): + self._oprot.writeMessageBegin('get_tcp_acceleration_command_coord', TMessageType.CALL, self._seqid) + args = get_tcp_acceleration_command_coord_args() + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_acceleration_command_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_acceleration_command_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_acceleration_command_coord failed: unknown result") + + def get_tcp_speed_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + self.send_get_tcp_speed_coord(tool, wobj) + return self.recv_get_tcp_speed_coord() + + def send_get_tcp_speed_coord(self, tool, wobj): + self._oprot.writeMessageBegin('get_tcp_speed_coord', TMessageType.CALL, self._seqid) + args = get_tcp_speed_coord_args() + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_speed_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_speed_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_speed_coord failed: unknown result") + + def get_tcp_acceleration_coord(self, tool, wobj): + """ + Parameters: + - tool + - wobj + + """ + self.send_get_tcp_acceleration_coord(tool, wobj) + return self.recv_get_tcp_acceleration_coord() + + def send_get_tcp_acceleration_coord(self, tool, wobj): + self._oprot.writeMessageBegin('get_tcp_acceleration_coord', TMessageType.CALL, self._seqid) + args = get_tcp_acceleration_coord_args() + args.tool = tool + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tcp_acceleration_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tcp_acceleration_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tcp_acceleration_coord failed: unknown result") + + def clear_error_message(self): + self.send_clear_error_message() + self.recv_clear_error_message() + + def send_clear_error_message(self): + self._oprot.writeMessageBegin('clear_error_message', TMessageType.CALL, self._seqid) + args = clear_error_message_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_clear_error_message(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = clear_error_message_result() + result.read(iprot) + iprot.readMessageEnd() + return + + def cal_traj_time(self, param): + """ + Parameters: + - param + + """ + self.send_cal_traj_time(param) + return self.recv_cal_traj_time() + + def send_cal_traj_time(self, param): + self._oprot.writeMessageBegin('cal_traj_time', TMessageType.CALL, self._seqid) + args = cal_traj_time_args() + args.param = param + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_cal_traj_time(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = cal_traj_time_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "cal_traj_time failed: unknown result") + + def get_hardware_clock(self): + self.send_get_hardware_clock() + return self.recv_get_hardware_clock() + + def send_get_hardware_clock(self): + self._oprot.writeMessageBegin('get_hardware_clock', TMessageType.CALL, self._seqid) + args = get_hardware_clock_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_hardware_clock(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_hardware_clock_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_hardware_clock failed: unknown result") + + def get_tool_data(self, name): + """ + Parameters: + - name + + """ + self.send_get_tool_data(name) + return self.recv_get_tool_data() + + def send_get_tool_data(self, name): + self._oprot.writeMessageBegin('get_tool_data', TMessageType.CALL, self._seqid) + args = get_tool_data_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tool_data(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tool_data_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tool_data failed: unknown result") + + def get_tool_load(self, name): + """ + Parameters: + - name + + """ + self.send_get_tool_load(name) + return self.recv_get_tool_load() + + def send_get_tool_load(self, name): + self._oprot.writeMessageBegin('get_tool_load', TMessageType.CALL, self._seqid) + args = get_tool_load_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_tool_load(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_tool_load_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_tool_load failed: unknown result") + + def get_wobj(self, name): + """ + Parameters: + - name + + """ + self.send_get_wobj(name) + return self.recv_get_wobj() + + def send_get_wobj(self, name): + self._oprot.writeMessageBegin('get_wobj', TMessageType.CALL, self._seqid) + args = get_wobj_args() + args.name = name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_wobj(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_wobj_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_wobj failed: unknown result") + + def get_base(self): + self.send_get_base() + return self.recv_get_base() + + def send_get_base(self): + self._oprot.writeMessageBegin('get_base', TMessageType.CALL, self._seqid) + args = get_base_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_base(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_base_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_base failed: unknown result") + + def get_home_pose(self): + self.send_get_home_pose() + return self.recv_get_home_pose() + + def send_get_home_pose(self): + self._oprot.writeMessageBegin('get_home_pose', TMessageType.CALL, self._seqid) + args = get_home_pose_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_home_pose(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_home_pose_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_home_pose failed: unknown result") + + def set_tool_data_workcell(self, name, tool, payload, inertia_tensor): + """ + Parameters: + - name + - tool + - payload + - inertia_tensor + + """ + self.send_set_tool_data_workcell(name, tool, payload, inertia_tensor) + return self.recv_set_tool_data_workcell() + + def send_set_tool_data_workcell(self, name, tool, payload, inertia_tensor): + self._oprot.writeMessageBegin('set_tool_data_workcell', TMessageType.CALL, self._seqid) + args = set_tool_data_workcell_args() + args.name = name + args.tool = tool + args.payload = payload + args.inertia_tensor = inertia_tensor + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_tool_data_workcell(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_tool_data_workcell_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_tool_data_workcell failed: unknown result") + + def set_wobj_workcell(self, name, wobj): + """ + Parameters: + - name + - wobj + + """ + self.send_set_wobj_workcell(name, wobj) + return self.recv_set_wobj_workcell() + + def send_set_wobj_workcell(self, name, wobj): + self._oprot.writeMessageBegin('set_wobj_workcell', TMessageType.CALL, self._seqid) + args = set_wobj_workcell_args() + args.name = name + args.wobj = wobj + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_wobj_workcell(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_wobj_workcell_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_wobj_workcell failed: unknown result") + + def get_simulation_state(self): + self.send_get_simulation_state() + return self.recv_get_simulation_state() + + def send_get_simulation_state(self): + self._oprot.writeMessageBegin('get_simulation_state', TMessageType.CALL, self._seqid) + args = get_simulation_state_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_simulation_state(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_simulation_state_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_simulation_state failed: unknown result") + + def save_stiffness_calibration_params(self, passwd): + """ + Parameters: + - passwd + + """ + self.send_save_stiffness_calibration_params(passwd) + return self.recv_save_stiffness_calibration_params() + + def send_save_stiffness_calibration_params(self, passwd): + self._oprot.writeMessageBegin('save_stiffness_calibration_params', TMessageType.CALL, self._seqid) + args = save_stiffness_calibration_params_args() + args.passwd = passwd + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_save_stiffness_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = save_stiffness_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "save_stiffness_calibration_params failed: unknown result") + + def get_stiffness_calibration_params(self): + self.send_get_stiffness_calibration_params() + return self.recv_get_stiffness_calibration_params() + + def send_get_stiffness_calibration_params(self): + self._oprot.writeMessageBegin('get_stiffness_calibration_params', TMessageType.CALL, self._seqid) + args = get_stiffness_calibration_params_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_stiffness_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_stiffness_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_stiffness_calibration_params failed: unknown result") + + def set_stiffness_calibration_params(self, params): + """ + Parameters: + - params + + """ + self.send_set_stiffness_calibration_params(params) + return self.recv_set_stiffness_calibration_params() + + def send_set_stiffness_calibration_params(self, params): + self._oprot.writeMessageBegin('set_stiffness_calibration_params', TMessageType.CALL, self._seqid) + args = set_stiffness_calibration_params_args() + args.params = params + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_stiffness_calibration_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_stiffness_calibration_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_stiffness_calibration_params failed: unknown result") + + def get_joint_motion_params(self): + self.send_get_joint_motion_params() + return self.recv_get_joint_motion_params() + + def send_get_joint_motion_params(self): + self._oprot.writeMessageBegin('get_joint_motion_params', TMessageType.CALL, self._seqid) + args = get_joint_motion_params_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_joint_motion_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_joint_motion_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_joint_motion_params failed: unknown result") + + def move_fixed(self, tool, wobj, axis, timer, scheme_name, epose, eaxis_v, block, op, def_acc): + """ + Parameters: + - tool + - wobj + - axis + - timer + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + self.send_move_fixed(tool, wobj, axis, timer, scheme_name, epose, eaxis_v, block, op, def_acc) + return self.recv_move_fixed() + + def send_move_fixed(self, tool, wobj, axis, timer, scheme_name, epose, eaxis_v, block, op, def_acc): + self._oprot.writeMessageBegin('move_fixed', TMessageType.CALL, self._seqid) + args = move_fixed_args() + args.tool = tool + args.wobj = wobj + args.axis = axis + args.timer = timer + args.scheme_name = scheme_name + args.epose = epose + args.eaxis_v = eaxis_v + args.block = block + args.op = op + args.def_acc = def_acc + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_move_fixed(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = move_fixed_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "move_fixed failed: unknown result") + + def track_enqueue_op_vel(self, track, block): + """ + Parameters: + - track + - block + + """ + self.send_track_enqueue_op_vel(track, block) + return self.recv_track_enqueue_op_vel() + + def send_track_enqueue_op_vel(self, track, block): + self._oprot.writeMessageBegin('track_enqueue_op_vel', TMessageType.CALL, self._seqid) + args = track_enqueue_op_vel_args() + args.track = track + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_track_enqueue_op_vel(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = track_enqueue_op_vel_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "track_enqueue_op_vel failed: unknown result") + + def track_cart_vel_motion(self, acc, tool, wobj, block): + """ + Parameters: + - acc + - tool + - wobj + - block + + """ + self.send_track_cart_vel_motion(acc, tool, wobj, block) + return self.recv_track_cart_vel_motion() + + def send_track_cart_vel_motion(self, acc, tool, wobj, block): + self._oprot.writeMessageBegin('track_cart_vel_motion', TMessageType.CALL, self._seqid) + args = track_cart_vel_motion_args() + args.acc = acc + args.tool = tool + args.wobj = wobj + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_track_cart_vel_motion(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = track_cart_vel_motion_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "track_cart_vel_motion failed: unknown result") + + def path_offset_cal(self, path_type, path_point, tool, wobj, path_offset, path_retract, tool_rotation, path_rotation, weld_rotation, tool_z_offset, path_type_list, plane_normals, average_height): + """ + Parameters: + - path_type + - path_point + - tool + - wobj + - path_offset + - path_retract + - tool_rotation + - path_rotation + - weld_rotation + - tool_z_offset + - path_type_list + - plane_normals + - average_height + + """ + self.send_path_offset_cal(path_type, path_point, tool, wobj, path_offset, path_retract, tool_rotation, path_rotation, weld_rotation, tool_z_offset, path_type_list, plane_normals, average_height) + return self.recv_path_offset_cal() + + def send_path_offset_cal(self, path_type, path_point, tool, wobj, path_offset, path_retract, tool_rotation, path_rotation, weld_rotation, tool_z_offset, path_type_list, plane_normals, average_height): + self._oprot.writeMessageBegin('path_offset_cal', TMessageType.CALL, self._seqid) + args = path_offset_cal_args() + args.path_type = path_type + args.path_point = path_point + args.tool = tool + args.wobj = wobj + args.path_offset = path_offset + args.path_retract = path_retract + args.tool_rotation = tool_rotation + args.path_rotation = path_rotation + args.weld_rotation = weld_rotation + args.tool_z_offset = tool_z_offset + args.path_type_list = path_type_list + args.plane_normals = plane_normals + args.average_height = average_height + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_path_offset_cal(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = path_offset_cal_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "path_offset_cal failed: unknown result") + + def set_installation(self, installation): + """ + Parameters: + - installation + + """ + self.send_set_installation(installation) + return self.recv_set_installation() + + def send_set_installation(self, installation): + self._oprot.writeMessageBegin('set_installation', TMessageType.CALL, self._seqid) + args = set_installation_args() + args.installation = installation + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_installation(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_installation_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_installation failed: unknown result") + + def get_installation(self): + self.send_get_installation() + return self.recv_get_installation() + + def send_get_installation(self): + self._oprot.writeMessageBegin('get_installation', TMessageType.CALL, self._seqid) + args = get_installation_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_installation(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_installation_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_installation failed: unknown result") + + def enable_laser_track(self): + self.send_enable_laser_track() + return self.recv_enable_laser_track() + + def send_enable_laser_track(self): + self._oprot.writeMessageBegin('enable_laser_track', TMessageType.CALL, self._seqid) + args = enable_laser_track_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_laser_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_laser_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_laser_track failed: unknown result") + + def set_laser_track_params(self, plane, freq, lead_len, active, kp, kd, offset, offset_vel, filter_coe, path_length_correction, track_type): + """ + Parameters: + - plane + - freq + - lead_len + - active + - kp + - kd + - offset + - offset_vel + - filter_coe + - path_length_correction + - track_type + + """ + self.send_set_laser_track_params(plane, freq, lead_len, active, kp, kd, offset, offset_vel, filter_coe, path_length_correction, track_type) + return self.recv_set_laser_track_params() + + def send_set_laser_track_params(self, plane, freq, lead_len, active, kp, kd, offset, offset_vel, filter_coe, path_length_correction, track_type): + self._oprot.writeMessageBegin('set_laser_track_params', TMessageType.CALL, self._seqid) + args = set_laser_track_params_args() + args.plane = plane + args.freq = freq + args.lead_len = lead_len + args.active = active + args.kp = kp + args.kd = kd + args.offset = offset + args.offset_vel = offset_vel + args.filter_coe = filter_coe + args.path_length_correction = path_length_correction + args.track_type = track_type + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_laser_track_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_laser_track_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_laser_track_params failed: unknown result") + + def disable_laser_track(self): + self.send_disable_laser_track() + return self.recv_disable_laser_track() + + def send_disable_laser_track(self): + self._oprot.writeMessageBegin('disable_laser_track', TMessageType.CALL, self._seqid) + args = disable_laser_track_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_laser_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_laser_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_laser_track failed: unknown result") + + def reset_laser_track(self): + self.send_reset_laser_track() + return self.recv_reset_laser_track() + + def send_reset_laser_track(self): + self._oprot.writeMessageBegin('reset_laser_track', TMessageType.CALL, self._seqid) + args = reset_laser_track_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_reset_laser_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = reset_laser_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "reset_laser_track failed: unknown result") + + def write_laser_search_pos(self, p): + """ + Parameters: + - p + + """ + self.send_write_laser_search_pos(p) + return self.recv_write_laser_search_pos() + + def send_write_laser_search_pos(self, p): + self._oprot.writeMessageBegin('write_laser_search_pos', TMessageType.CALL, self._seqid) + args = write_laser_search_pos_args() + args.p = p + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_laser_search_pos(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_laser_search_pos_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_laser_search_pos failed: unknown result") + + def set_arc_system_params(self, coeff_K, lag_time, start_time, sensi_coeff, diff_constant, diff_value, voltage_track, cal_lag_time, arc_direction, ref_cycle_times): + """ + Parameters: + - coeff_K + - lag_time + - start_time + - sensi_coeff + - diff_constant + - diff_value + - voltage_track + - cal_lag_time + - arc_direction + - ref_cycle_times + + """ + self.send_set_arc_system_params(coeff_K, lag_time, start_time, sensi_coeff, diff_constant, diff_value, voltage_track, cal_lag_time, arc_direction, ref_cycle_times) + return self.recv_set_arc_system_params() + + def send_set_arc_system_params(self, coeff_K, lag_time, start_time, sensi_coeff, diff_constant, diff_value, voltage_track, cal_lag_time, arc_direction, ref_cycle_times): + self._oprot.writeMessageBegin('set_arc_system_params', TMessageType.CALL, self._seqid) + args = set_arc_system_params_args() + args.coeff_K = coeff_K + args.lag_time = lag_time + args.start_time = start_time + args.sensi_coeff = sensi_coeff + args.diff_constant = diff_constant + args.diff_value = diff_value + args.voltage_track = voltage_track + args.cal_lag_time = cal_lag_time + args.arc_direction = arc_direction + args.ref_cycle_times = ref_cycle_times + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_arc_system_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_arc_system_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_arc_system_params failed: unknown result") + + def set_arc_track_params(self, freq, active, kp, kd, max_displacement, max_displacement_vel, base_current, track_type, max_displacement_cycle, constant_bias): + """ + Parameters: + - freq + - active + - kp + - kd + - max_displacement + - max_displacement_vel + - base_current + - track_type + - max_displacement_cycle + - constant_bias + + """ + self.send_set_arc_track_params(freq, active, kp, kd, max_displacement, max_displacement_vel, base_current, track_type, max_displacement_cycle, constant_bias) + return self.recv_set_arc_track_params() + + def send_set_arc_track_params(self, freq, active, kp, kd, max_displacement, max_displacement_vel, base_current, track_type, max_displacement_cycle, constant_bias): + self._oprot.writeMessageBegin('set_arc_track_params', TMessageType.CALL, self._seqid) + args = set_arc_track_params_args() + args.freq = freq + args.active = active + args.kp = kp + args.kd = kd + args.max_displacement = max_displacement + args.max_displacement_vel = max_displacement_vel + args.base_current = base_current + args.track_type = track_type + args.max_displacement_cycle = max_displacement_cycle + args.constant_bias = constant_bias + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_arc_track_params(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_arc_track_params_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_arc_track_params failed: unknown result") + + def enable_arc_track(self, block): + """ + Parameters: + - block + + """ + self.send_enable_arc_track(block) + return self.recv_enable_arc_track() + + def send_enable_arc_track(self, block): + self._oprot.writeMessageBegin('enable_arc_track', TMessageType.CALL, self._seqid) + args = enable_arc_track_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_enable_arc_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = enable_arc_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "enable_arc_track failed: unknown result") + + def disable_arc_track(self, block): + """ + Parameters: + - block + + """ + self.send_disable_arc_track(block) + return self.recv_disable_arc_track() + + def send_disable_arc_track(self, block): + self._oprot.writeMessageBegin('disable_arc_track', TMessageType.CALL, self._seqid) + args = disable_arc_track_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_disable_arc_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = disable_arc_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "disable_arc_track failed: unknown result") + + def get_arc_track_lag_time(self): + self.send_get_arc_track_lag_time() + return self.recv_get_arc_track_lag_time() + + def send_get_arc_track_lag_time(self): + self._oprot.writeMessageBegin('get_arc_track_lag_time', TMessageType.CALL, self._seqid) + args = get_arc_track_lag_time_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_arc_track_lag_time(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_arc_track_lag_time_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_arc_track_lag_time failed: unknown result") + + def reset_arc_track(self): + self.send_reset_arc_track() + return self.recv_reset_arc_track() + + def send_reset_arc_track(self): + self._oprot.writeMessageBegin('reset_arc_track', TMessageType.CALL, self._seqid) + args = reset_arc_track_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_reset_arc_track(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = reset_arc_track_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "reset_arc_track failed: unknown result") + + def write_arc_current_data(self, current): + """ + Parameters: + - current + + """ + self.send_write_arc_current_data(current) + return self.recv_write_arc_current_data() + + def send_write_arc_current_data(self, current): + self._oprot.writeMessageBegin('write_arc_current_data', TMessageType.CALL, self._seqid) + args = write_arc_current_data_args() + args.current = current + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_write_arc_current_data(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = write_arc_current_data_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "write_arc_current_data failed: unknown result") + + def clear_arc_current_data(self): + self.send_clear_arc_current_data() + return self.recv_clear_arc_current_data() + + def send_clear_arc_current_data(self): + self._oprot.writeMessageBegin('clear_arc_current_data', TMessageType.CALL, self._seqid) + args = clear_arc_current_data_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_clear_arc_current_data(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = clear_arc_current_data_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "clear_arc_current_data failed: unknown result") + + def start_load_identification(self, block): + """ + Parameters: + - block + + """ + self.send_start_load_identification(block) + return self.recv_start_load_identification() + + def send_start_load_identification(self, block): + self._oprot.writeMessageBegin('start_load_identification', TMessageType.CALL, self._seqid) + args = start_load_identification_args() + args.block = block + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_start_load_identification(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = start_load_identification_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "start_load_identification failed: unknown result") + + def get_eaxis_coord(self, eaxis_scheme_name): + """ + Parameters: + - eaxis_scheme_name + + """ + self.send_get_eaxis_coord(eaxis_scheme_name) + return self.recv_get_eaxis_coord() + + def send_get_eaxis_coord(self, eaxis_scheme_name): + self._oprot.writeMessageBegin('get_eaxis_coord', TMessageType.CALL, self._seqid) + args = get_eaxis_coord_args() + args.eaxis_scheme_name = eaxis_scheme_name + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_eaxis_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_eaxis_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_eaxis_coord failed: unknown result") + + def set_eaxis_coord(self, eaxis_scheme_name, coord_list): + """ + Parameters: + - eaxis_scheme_name + - coord_list + + """ + self.send_set_eaxis_coord(eaxis_scheme_name, coord_list) + return self.recv_set_eaxis_coord() + + def send_set_eaxis_coord(self, eaxis_scheme_name, coord_list): + self._oprot.writeMessageBegin('set_eaxis_coord', TMessageType.CALL, self._seqid) + args = set_eaxis_coord_args() + args.eaxis_scheme_name = eaxis_scheme_name + args.coord_list = coord_list + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_eaxis_coord(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_eaxis_coord_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_eaxis_coord failed: unknown result") + + def set_remote_tool_data(self, name, tool_offset, payload, inertia_tensor): + """ + Parameters: + - name + - tool_offset + - payload + - inertia_tensor + + """ + self.send_set_remote_tool_data(name, tool_offset, payload, inertia_tensor) + return self.recv_set_remote_tool_data() + + def send_set_remote_tool_data(self, name, tool_offset, payload, inertia_tensor): + self._oprot.writeMessageBegin('set_remote_tool_data', TMessageType.CALL, self._seqid) + args = set_remote_tool_data_args() + args.name = name + args.tool_offset = tool_offset + args.payload = payload + args.inertia_tensor = inertia_tensor + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_remote_tool_data(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_remote_tool_data_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_remote_tool_data failed: unknown result") + + def set_remote_tool_data_workcell(self, name, tool_offset, payload, inertia_tensor): + """ + Parameters: + - name + - tool_offset + - payload + - inertia_tensor + + """ + self.send_set_remote_tool_data_workcell(name, tool_offset, payload, inertia_tensor) + return self.recv_set_remote_tool_data_workcell() + + def send_set_remote_tool_data_workcell(self, name, tool_offset, payload, inertia_tensor): + self._oprot.writeMessageBegin('set_remote_tool_data_workcell', TMessageType.CALL, self._seqid) + args = set_remote_tool_data_workcell_args() + args.name = name + args.tool_offset = tool_offset + args.payload = payload + args.inertia_tensor = inertia_tensor + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_set_remote_tool_data_workcell(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = set_remote_tool_data_workcell_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "set_remote_tool_data_workcell failed: unknown result") + + def get_eaxis_scheme_info(self): + self.send_get_eaxis_scheme_info() + return self.recv_get_eaxis_scheme_info() + + def send_get_eaxis_scheme_info(self): + self._oprot.writeMessageBegin('get_eaxis_scheme_info', TMessageType.CALL, self._seqid) + args = get_eaxis_scheme_info_args() + args.write(self._oprot) + self._oprot.writeMessageEnd() + self._oprot.trans.flush() + + def recv_get_eaxis_scheme_info(self): + iprot = self._iprot + (fname, mtype, rseqid) = iprot.readMessageBegin() + if mtype == TMessageType.EXCEPTION: + x = TApplicationException() + x.read(iprot) + iprot.readMessageEnd() + raise x + result = get_eaxis_scheme_info_result() + result.read(iprot) + iprot.readMessageEnd() + if result.success is not None: + return result.success + raise TApplicationException(TApplicationException.MISSING_RESULT, "get_eaxis_scheme_info failed: unknown result") + + +class Processor(Iface, TProcessor): + def __init__(self, handler): + self._handler = handler + self._processMap = {} + self._processMap["power_on"] = Processor.process_power_on + self._processMap["power_off"] = Processor.process_power_off + self._processMap["enable"] = Processor.process_enable + self._processMap["disable"] = Processor.process_disable + self._processMap["shutdown"] = Processor.process_shutdown + self._processMap["stop"] = Processor.process_stop + self._processMap["pause"] = Processor.process_pause + self._processMap["resume"] = Processor.process_resume + self._processMap["run_program"] = Processor.process_run_program + self._processMap["set_tool_data"] = Processor.process_set_tool_data + self._processMap["get_tcp_offset"] = Processor.process_get_tcp_offset + self._processMap["set_wobj"] = Processor.process_set_wobj + self._processMap["set_wobj_offset"] = Processor.process_set_wobj_offset + self._processMap["cal_fkine"] = Processor.process_cal_fkine + self._processMap["cal_ikine"] = Processor.process_cal_ikine + self._processMap["set_digital_output_mode"] = Processor.process_set_digital_output_mode + self._processMap["set_standard_digital_out"] = Processor.process_set_standard_digital_out + self._processMap["set_tool_digital_out"] = Processor.process_set_tool_digital_out + self._processMap["get_standard_digital_in"] = Processor.process_get_standard_digital_in + self._processMap["get_standard_digital_out"] = Processor.process_get_standard_digital_out + self._processMap["get_tool_digital_in"] = Processor.process_get_tool_digital_in + self._processMap["get_tool_digital_out"] = Processor.process_get_tool_digital_out + self._processMap["get_config_digital_in"] = Processor.process_get_config_digital_in + self._processMap["get_standard_analog_voltage_in"] = Processor.process_get_standard_analog_voltage_in + self._processMap["get_tool_analog_voltage_in"] = Processor.process_get_tool_analog_voltage_in + self._processMap["get_standard_analog_current_in"] = Processor.process_get_standard_analog_current_in + self._processMap["set_standard_analog_voltage_out"] = Processor.process_set_standard_analog_voltage_out + self._processMap["set_standard_analog_current_out"] = Processor.process_set_standard_analog_current_out + self._processMap["read_data_485"] = Processor.process_read_data_485 + self._processMap["read_raw_data_485"] = Processor.process_read_raw_data_485 + self._processMap["read_raw_data_485_ht"] = Processor.process_read_raw_data_485_ht + self._processMap["read_raw_data_485_h"] = Processor.process_read_raw_data_485_h + self._processMap["write_data_485"] = Processor.process_write_data_485 + self._processMap["write_raw_data_485"] = Processor.process_write_raw_data_485 + self._processMap["write_raw_data_485_h"] = Processor.process_write_raw_data_485_h + self._processMap["write_raw_data_485_ht"] = Processor.process_write_raw_data_485_ht + self._processMap["tool_read_data_485"] = Processor.process_tool_read_data_485 + self._processMap["tool_read_raw_data_485"] = Processor.process_tool_read_raw_data_485 + self._processMap["tool_read_raw_data_485_h"] = Processor.process_tool_read_raw_data_485_h + self._processMap["tool_read_raw_data_485_ht"] = Processor.process_tool_read_raw_data_485_ht + self._processMap["tool_write_data_485"] = Processor.process_tool_write_data_485 + self._processMap["tool_write_raw_data_485"] = Processor.process_tool_write_raw_data_485 + self._processMap["tool_write_raw_data_485_h"] = Processor.process_tool_write_raw_data_485_h + self._processMap["tool_write_raw_data_485_ht"] = Processor.process_tool_write_raw_data_485_ht + self._processMap["read_data_can"] = Processor.process_read_data_can + self._processMap["read_raw_data_can"] = Processor.process_read_raw_data_can + self._processMap["write_data_can"] = Processor.process_write_data_can + self._processMap["write_raw_data_can"] = Processor.process_write_raw_data_can + self._processMap["get_function_digital_in"] = Processor.process_get_function_digital_in + self._processMap["get_function_digital_out"] = Processor.process_get_function_digital_out + self._processMap["read_bool_reg"] = Processor.process_read_bool_reg + self._processMap["read_word_reg"] = Processor.process_read_word_reg + self._processMap["read_float_reg"] = Processor.process_read_float_reg + self._processMap["write_bool_reg"] = Processor.process_write_bool_reg + self._processMap["write_word_reg"] = Processor.process_write_word_reg + self._processMap["write_float_reg"] = Processor.process_write_float_reg + self._processMap["get_function_reg_in"] = Processor.process_get_function_reg_in + self._processMap["get_function_reg_out"] = Processor.process_get_function_reg_out + self._processMap["movej"] = Processor.process_movej + self._processMap["movej_pose"] = Processor.process_movej_pose + self._processMap["movej2"] = Processor.process_movej2 + self._processMap["movej_pose2"] = Processor.process_movej_pose2 + self._processMap["movel"] = Processor.process_movel + self._processMap["movec"] = Processor.process_movec + self._processMap["move_circle"] = Processor.process_move_circle + self._processMap["tcp_move"] = Processor.process_tcp_move + self._processMap["tcp_move_2p"] = Processor.process_tcp_move_2p + self._processMap["wobj_move"] = Processor.process_wobj_move + self._processMap["wobj_move_2p"] = Processor.process_wobj_move_2p + self._processMap["spline"] = Processor.process_spline + self._processMap["spline_op"] = Processor.process_spline_op + self._processMap["speedj"] = Processor.process_speedj + self._processMap["speedl"] = Processor.process_speedl + self._processMap["speed_stop"] = Processor.process_speed_stop + self._processMap["servoj"] = Processor.process_servoj + self._processMap["servoj_pose"] = Processor.process_servoj_pose + self._processMap["servo_tcp"] = Processor.process_servo_tcp + self._processMap["servol"] = Processor.process_servol + self._processMap["teach_mode"] = Processor.process_teach_mode + self._processMap["end_teach_mode"] = Processor.process_end_teach_mode + self._processMap["modbus_add_signal"] = Processor.process_modbus_add_signal + self._processMap["modbus_delete_signal"] = Processor.process_modbus_delete_signal + self._processMap["modbus_read"] = Processor.process_modbus_read + self._processMap["modbus_write"] = Processor.process_modbus_write + self._processMap["modbus_set_frequency"] = Processor.process_modbus_set_frequency + self._processMap["get_last_error"] = Processor.process_get_last_error + self._processMap["get_noneblock_taskstate"] = Processor.process_get_noneblock_taskstate + self._processMap["log_info"] = Processor.process_log_info + self._processMap["log_error"] = Processor.process_log_error + self._processMap["simulation"] = Processor.process_simulation + self._processMap["speed"] = Processor.process_speed + self._processMap["get_robot_state"] = Processor.process_get_robot_state + self._processMap["get_flange_pose"] = Processor.process_get_flange_pose + self._processMap["get_flange_speed"] = Processor.process_get_flange_speed + self._processMap["get_flange_acceleration"] = Processor.process_get_flange_acceleration + self._processMap["get_tcp_pose"] = Processor.process_get_tcp_pose + self._processMap["get_tcp_speed"] = Processor.process_get_tcp_speed + self._processMap["get_tcp_acceleration"] = Processor.process_get_tcp_acceleration + self._processMap["get_tcp_force"] = Processor.process_get_tcp_force + self._processMap["get_actual_joints_position"] = Processor.process_get_actual_joints_position + self._processMap["get_target_joints_position"] = Processor.process_get_target_joints_position + self._processMap["get_actual_joints_speed"] = Processor.process_get_actual_joints_speed + self._processMap["get_target_joints_speed"] = Processor.process_get_target_joints_speed + self._processMap["get_actual_joints_acceleration"] = Processor.process_get_actual_joints_acceleration + self._processMap["get_target_joints_acceleration"] = Processor.process_get_target_joints_acceleration + self._processMap["get_actual_joints_torque"] = Processor.process_get_actual_joints_torque + self._processMap["get_target_joints_torque"] = Processor.process_get_target_joints_torque + self._processMap["stop_record_track"] = Processor.process_stop_record_track + self._processMap["start_record_track"] = Processor.process_start_record_track + self._processMap["collision_detect"] = Processor.process_collision_detect + self._processMap["replay"] = Processor.process_replay + self._processMap["set_load_data"] = Processor.process_set_load_data + self._processMap["fc_start"] = Processor.process_fc_start + self._processMap["fc_stop"] = Processor.process_fc_stop + self._processMap["fc_config"] = Processor.process_fc_config + self._processMap["fc_move"] = Processor.process_fc_move + self._processMap["fc_guard_act"] = Processor.process_fc_guard_act + self._processMap["fc_guard_deact"] = Processor.process_fc_guard_deact + self._processMap["fc_force_set_value"] = Processor.process_fc_force_set_value + self._processMap["fc_wait_pos"] = Processor.process_fc_wait_pos + self._processMap["fc_wait_vel"] = Processor.process_fc_wait_vel + self._processMap["fc_wait_ft"] = Processor.process_fc_wait_ft + self._processMap["fc_wait_logic"] = Processor.process_fc_wait_logic + self._processMap["fc_get_ft"] = Processor.process_fc_get_ft + self._processMap["fc_mode_is_active"] = Processor.process_fc_mode_is_active + self._processMap["start_realtime_mode"] = Processor.process_start_realtime_mode + self._processMap["end_realtime_mode"] = Processor.process_end_realtime_mode + self._processMap["realtime_data_enqueue"] = Processor.process_realtime_data_enqueue + self._processMap["clear_realtime_data_queue"] = Processor.process_clear_realtime_data_queue + self._processMap["get_realtime_data_queue_size"] = Processor.process_get_realtime_data_queue_size + self._processMap["enable_speed_optimization"] = Processor.process_enable_speed_optimization + self._processMap["disable_speed_optimization"] = Processor.process_disable_speed_optimization + self._processMap["change_recipe"] = Processor.process_change_recipe + self._processMap["set_system_value_bool"] = Processor.process_set_system_value_bool + self._processMap["set_system_value_double"] = Processor.process_set_system_value_double + self._processMap["set_system_value_str"] = Processor.process_set_system_value_str + self._processMap["set_system_value_list"] = Processor.process_set_system_value_list + self._processMap["get_system_value_bool"] = Processor.process_get_system_value_bool + self._processMap["get_system_value_double"] = Processor.process_get_system_value_double + self._processMap["get_system_value_str"] = Processor.process_get_system_value_str + self._processMap["get_system_value_list"] = Processor.process_get_system_value_list + self._processMap["trackEnqueue"] = Processor.process_trackEnqueue + self._processMap["trackEnqueueOp"] = Processor.process_trackEnqueueOp + self._processMap["trackClearQueue"] = Processor.process_trackClearQueue + self._processMap["getQueueSize"] = Processor.process_getQueueSize + self._processMap["trackJointMotion"] = Processor.process_trackJointMotion + self._processMap["trackCartMotion"] = Processor.process_trackCartMotion + self._processMap["rpc_heartbeat"] = Processor.process_rpc_heartbeat + self._processMap["move_spiral"] = Processor.process_move_spiral + self._processMap["enable_acc_optimization"] = Processor.process_enable_acc_optimization + self._processMap["disable_acc_optimization"] = Processor.process_disable_acc_optimization + self._processMap["set_baudrate_485"] = Processor.process_set_baudrate_485 + self._processMap["set_baudrate_can"] = Processor.process_set_baudrate_can + self._processMap["set_analog_output_mode"] = Processor.process_set_analog_output_mode + self._processMap["robotmoving"] = Processor.process_robotmoving + self._processMap["modbus_write_multiple_coils"] = Processor.process_modbus_write_multiple_coils + self._processMap["modbus_write_multiple_regs"] = Processor.process_modbus_write_multiple_regs + self._processMap["get_current_project"] = Processor.process_get_current_project + self._processMap["get_files_list"] = Processor.process_get_files_list + self._processMap["getRobotStatus"] = Processor.process_getRobotStatus + self._processMap["getRobotIOStatus"] = Processor.process_getRobotIOStatus + self._processMap["get_tcp_pose_coord"] = Processor.process_get_tcp_pose_coord + self._processMap["get_tcp_force_tool"] = Processor.process_get_tcp_force_tool + self._processMap["restart"] = Processor.process_restart + self._processMap["set_servo_config"] = Processor.process_set_servo_config + self._processMap["apply_servo_config"] = Processor.process_apply_servo_config + self._processMap["get_motor_pole_pair_number"] = Processor.process_get_motor_pole_pair_number + self._processMap["get_motor_stator_slots"] = Processor.process_get_motor_stator_slots + self._processMap["get_axis_ratio"] = Processor.process_get_axis_ratio + self._processMap["collision_detection_reset"] = Processor.process_collision_detection_reset + self._processMap["set_servo_file_params"] = Processor.process_set_servo_file_params + self._processMap["combine_motion_config"] = Processor.process_combine_motion_config + self._processMap["set_eaxis_param"] = Processor.process_set_eaxis_param + self._processMap["add_eaxis_scheme"] = Processor.process_add_eaxis_scheme + self._processMap["delete_eaxis_scheme"] = Processor.process_delete_eaxis_scheme + self._processMap["enable_eaxis_scheme"] = Processor.process_enable_eaxis_scheme + self._processMap["disable_eaxis_scheme"] = Processor.process_disable_eaxis_scheme + self._processMap["set_eaxiss_scheme_param"] = Processor.process_set_eaxiss_scheme_param + self._processMap["move_jog"] = Processor.process_move_jog + self._processMap["stop_manual_move"] = Processor.process_stop_manual_move + self._processMap["get_robot_version"] = Processor.process_get_robot_version + self._processMap["set_teach_pendant"] = Processor.process_set_teach_pendant + self._processMap["get_teach_speed"] = Processor.process_get_teach_speed + self._processMap["get_global_speed"] = Processor.process_get_global_speed + self._processMap["set_teach_speed"] = Processor.process_set_teach_speed + self._processMap["enable_combine_motion"] = Processor.process_enable_combine_motion + self._processMap["disable_combine_motion"] = Processor.process_disable_combine_motion + self._processMap["enable_singularity_control"] = Processor.process_enable_singularity_control + self._processMap["disable_singularity_control"] = Processor.process_disable_singularity_control + self._processMap["enable_vibration_control"] = Processor.process_enable_vibration_control + self._processMap["disable_vibration_control"] = Processor.process_disable_vibration_control + self._processMap["move_eaxis"] = Processor.process_move_eaxis + self._processMap["movej2_eaxis"] = Processor.process_movej2_eaxis + self._processMap["movej2_pose_eaxis"] = Processor.process_movej2_pose_eaxis + self._processMap["movel_eaxis"] = Processor.process_movel_eaxis + self._processMap["movec_eaxis"] = Processor.process_movec_eaxis + self._processMap["move_circle_eaxis"] = Processor.process_move_circle_eaxis + self._processMap["reach_check"] = Processor.process_reach_check + self._processMap["move_jog_eaxis"] = Processor.process_move_jog_eaxis + self._processMap["get_eaxis_info"] = Processor.process_get_eaxis_info + self._processMap["set_hand_teach_parameter"] = Processor.process_set_hand_teach_parameter + self._processMap["set_pendant_type"] = Processor.process_set_pendant_type + self._processMap["set_blend_ahead"] = Processor.process_set_blend_ahead + self._processMap["switch_mode"] = Processor.process_switch_mode + self._processMap["read_encoder_count"] = Processor.process_read_encoder_count + self._processMap["set_kinematic_calibration_params"] = Processor.process_set_kinematic_calibration_params + self._processMap["get_pos_bias"] = Processor.process_get_pos_bias + self._processMap["get_system_value_lists"] = Processor.process_get_system_value_lists + self._processMap["get_origin_DH"] = Processor.process_get_origin_DH + self._processMap["get_calib_DH"] = Processor.process_get_calib_DH + self._processMap["get_robot_type"] = Processor.process_get_robot_type + self._processMap["get_ext_torque"] = Processor.process_get_ext_torque + self._processMap["set_dynamic_calibration_params"] = Processor.process_set_dynamic_calibration_params + self._processMap["get_dynamic_calibration_params"] = Processor.process_get_dynamic_calibration_params + self._processMap["upload_robot_param_to_toolboard"] = Processor.process_upload_robot_param_to_toolboard + self._processMap["set_kinematic_calibration_info"] = Processor.process_set_kinematic_calibration_info + self._processMap["set_dynamic_calibration_info"] = Processor.process_set_dynamic_calibration_info + self._processMap["set_vibration_calibration_info"] = Processor.process_set_vibration_calibration_info + self._processMap["get_axis_motor_rated_current"] = Processor.process_get_axis_motor_rated_current + self._processMap["get_axis_motor_kt"] = Processor.process_get_axis_motor_kt + self._processMap["abort"] = Processor.process_abort + self._processMap["get_vibration_calibration_params"] = Processor.process_get_vibration_calibration_params + self._processMap["save_kinematic_calibration_params"] = Processor.process_save_kinematic_calibration_params + self._processMap["save_dynamic_calibration_params"] = Processor.process_save_dynamic_calibration_params + self._processMap["get_flange_pose_cmd"] = Processor.process_get_flange_pose_cmd + self._processMap["get_flange_speed_cmd"] = Processor.process_get_flange_speed_cmd + self._processMap["get_flange_acceleration_cmd"] = Processor.process_get_flange_acceleration_cmd + self._processMap["get_tcp_pose_command"] = Processor.process_get_tcp_pose_command + self._processMap["get_tcp_speed_command"] = Processor.process_get_tcp_speed_command + self._processMap["get_tcp_acceleration_command"] = Processor.process_get_tcp_acceleration_command + self._processMap["get_tcp_pose_command_coord"] = Processor.process_get_tcp_pose_command_coord + self._processMap["get_tcp_speed_command_coord"] = Processor.process_get_tcp_speed_command_coord + self._processMap["get_tcp_acceleration_command_coord"] = Processor.process_get_tcp_acceleration_command_coord + self._processMap["get_tcp_speed_coord"] = Processor.process_get_tcp_speed_coord + self._processMap["get_tcp_acceleration_coord"] = Processor.process_get_tcp_acceleration_coord + self._processMap["clear_error_message"] = Processor.process_clear_error_message + self._processMap["cal_traj_time"] = Processor.process_cal_traj_time + self._processMap["get_hardware_clock"] = Processor.process_get_hardware_clock + self._processMap["get_tool_data"] = Processor.process_get_tool_data + self._processMap["get_tool_load"] = Processor.process_get_tool_load + self._processMap["get_wobj"] = Processor.process_get_wobj + self._processMap["get_base"] = Processor.process_get_base + self._processMap["get_home_pose"] = Processor.process_get_home_pose + self._processMap["set_tool_data_workcell"] = Processor.process_set_tool_data_workcell + self._processMap["set_wobj_workcell"] = Processor.process_set_wobj_workcell + self._processMap["get_simulation_state"] = Processor.process_get_simulation_state + self._processMap["save_stiffness_calibration_params"] = Processor.process_save_stiffness_calibration_params + self._processMap["get_stiffness_calibration_params"] = Processor.process_get_stiffness_calibration_params + self._processMap["set_stiffness_calibration_params"] = Processor.process_set_stiffness_calibration_params + self._processMap["get_joint_motion_params"] = Processor.process_get_joint_motion_params + self._processMap["move_fixed"] = Processor.process_move_fixed + self._processMap["track_enqueue_op_vel"] = Processor.process_track_enqueue_op_vel + self._processMap["track_cart_vel_motion"] = Processor.process_track_cart_vel_motion + self._processMap["path_offset_cal"] = Processor.process_path_offset_cal + self._processMap["set_installation"] = Processor.process_set_installation + self._processMap["get_installation"] = Processor.process_get_installation + self._processMap["enable_laser_track"] = Processor.process_enable_laser_track + self._processMap["set_laser_track_params"] = Processor.process_set_laser_track_params + self._processMap["disable_laser_track"] = Processor.process_disable_laser_track + self._processMap["reset_laser_track"] = Processor.process_reset_laser_track + self._processMap["write_laser_search_pos"] = Processor.process_write_laser_search_pos + self._processMap["set_arc_system_params"] = Processor.process_set_arc_system_params + self._processMap["set_arc_track_params"] = Processor.process_set_arc_track_params + self._processMap["enable_arc_track"] = Processor.process_enable_arc_track + self._processMap["disable_arc_track"] = Processor.process_disable_arc_track + self._processMap["get_arc_track_lag_time"] = Processor.process_get_arc_track_lag_time + self._processMap["reset_arc_track"] = Processor.process_reset_arc_track + self._processMap["write_arc_current_data"] = Processor.process_write_arc_current_data + self._processMap["clear_arc_current_data"] = Processor.process_clear_arc_current_data + self._processMap["start_load_identification"] = Processor.process_start_load_identification + self._processMap["get_eaxis_coord"] = Processor.process_get_eaxis_coord + self._processMap["set_eaxis_coord"] = Processor.process_set_eaxis_coord + self._processMap["set_remote_tool_data"] = Processor.process_set_remote_tool_data + self._processMap["set_remote_tool_data_workcell"] = Processor.process_set_remote_tool_data_workcell + self._processMap["get_eaxis_scheme_info"] = Processor.process_get_eaxis_scheme_info + self._on_message_begin = None + + def on_message_begin(self, func): + self._on_message_begin = func + + def process(self, iprot, oprot): + (name, type, seqid) = iprot.readMessageBegin() + if self._on_message_begin: + self._on_message_begin(name, type, seqid) + if name not in self._processMap: + iprot.skip(TType.STRUCT) + iprot.readMessageEnd() + x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) + oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) + x.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + return + else: + self._processMap[name](self, seqid, iprot, oprot) + return True + + def process_power_on(self, seqid, iprot, oprot): + args = power_on_args() + args.read(iprot) + iprot.readMessageEnd() + result = power_on_result() + try: + result.success = self._handler.power_on(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("power_on", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_power_off(self, seqid, iprot, oprot): + args = power_off_args() + args.read(iprot) + iprot.readMessageEnd() + result = power_off_result() + try: + result.success = self._handler.power_off(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("power_off", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable(self, seqid, iprot, oprot): + args = enable_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_result() + try: + result.success = self._handler.enable(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable(self, seqid, iprot, oprot): + args = disable_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_result() + try: + result.success = self._handler.disable(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_shutdown(self, seqid, iprot, oprot): + args = shutdown_args() + args.read(iprot) + iprot.readMessageEnd() + result = shutdown_result() + try: + result.success = self._handler.shutdown(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("shutdown", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_stop(self, seqid, iprot, oprot): + args = stop_args() + args.read(iprot) + iprot.readMessageEnd() + result = stop_result() + try: + result.success = self._handler.stop(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("stop", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_pause(self, seqid, iprot, oprot): + args = pause_args() + args.read(iprot) + iprot.readMessageEnd() + result = pause_result() + try: + result.success = self._handler.pause(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("pause", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_resume(self, seqid, iprot, oprot): + args = resume_args() + args.read(iprot) + iprot.readMessageEnd() + result = resume_result() + try: + result.success = self._handler.resume(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("resume", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_run_program(self, seqid, iprot, oprot): + args = run_program_args() + args.read(iprot) + iprot.readMessageEnd() + result = run_program_result() + try: + result.success = self._handler.run_program(args.name, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("run_program", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_tool_data(self, seqid, iprot, oprot): + args = set_tool_data_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_tool_data_result() + try: + result.success = self._handler.set_tool_data(args.name, args.tool_offset, args.payload, args.inertia_tensor) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_tool_data", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_offset(self, seqid, iprot, oprot): + args = get_tcp_offset_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_offset_result() + try: + result.success = self._handler.get_tcp_offset() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_offset", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_wobj(self, seqid, iprot, oprot): + args = set_wobj_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_wobj_result() + try: + result.success = self._handler.set_wobj(args.name, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_wobj", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_wobj_offset(self, seqid, iprot, oprot): + args = set_wobj_offset_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_wobj_offset_result() + try: + result.success = self._handler.set_wobj_offset(args.wobj, args.active) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_wobj_offset", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_cal_fkine(self, seqid, iprot, oprot): + args = cal_fkine_args() + args.read(iprot) + iprot.readMessageEnd() + result = cal_fkine_result() + try: + result.success = self._handler.cal_fkine(args.joints_position, args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("cal_fkine", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_cal_ikine(self, seqid, iprot, oprot): + args = cal_ikine_args() + args.read(iprot) + iprot.readMessageEnd() + result = cal_ikine_result() + try: + result.success = self._handler.cal_ikine(args.p, args.q_near, args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("cal_ikine", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_digital_output_mode(self, seqid, iprot, oprot): + args = set_digital_output_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_digital_output_mode_result() + try: + result.success = self._handler.set_digital_output_mode(args.num, args.type, args.freq, args.duty_cycle) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_digital_output_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_standard_digital_out(self, seqid, iprot, oprot): + args = set_standard_digital_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_standard_digital_out_result() + try: + result.success = self._handler.set_standard_digital_out(args.num, args.value, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_standard_digital_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_tool_digital_out(self, seqid, iprot, oprot): + args = set_tool_digital_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_tool_digital_out_result() + try: + result.success = self._handler.set_tool_digital_out(args.num, args.value, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_tool_digital_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_standard_digital_in(self, seqid, iprot, oprot): + args = get_standard_digital_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_standard_digital_in_result() + try: + result.success = self._handler.get_standard_digital_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_standard_digital_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_standard_digital_out(self, seqid, iprot, oprot): + args = get_standard_digital_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_standard_digital_out_result() + try: + result.success = self._handler.get_standard_digital_out(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_standard_digital_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tool_digital_in(self, seqid, iprot, oprot): + args = get_tool_digital_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tool_digital_in_result() + try: + result.success = self._handler.get_tool_digital_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tool_digital_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tool_digital_out(self, seqid, iprot, oprot): + args = get_tool_digital_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tool_digital_out_result() + try: + result.success = self._handler.get_tool_digital_out(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tool_digital_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_config_digital_in(self, seqid, iprot, oprot): + args = get_config_digital_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_config_digital_in_result() + try: + result.success = self._handler.get_config_digital_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_config_digital_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_standard_analog_voltage_in(self, seqid, iprot, oprot): + args = get_standard_analog_voltage_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_standard_analog_voltage_in_result() + try: + result.success = self._handler.get_standard_analog_voltage_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_standard_analog_voltage_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tool_analog_voltage_in(self, seqid, iprot, oprot): + args = get_tool_analog_voltage_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tool_analog_voltage_in_result() + try: + result.success = self._handler.get_tool_analog_voltage_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tool_analog_voltage_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_standard_analog_current_in(self, seqid, iprot, oprot): + args = get_standard_analog_current_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_standard_analog_current_in_result() + try: + result.success = self._handler.get_standard_analog_current_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_standard_analog_current_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_standard_analog_voltage_out(self, seqid, iprot, oprot): + args = set_standard_analog_voltage_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_standard_analog_voltage_out_result() + try: + result.success = self._handler.set_standard_analog_voltage_out(args.num, args.value, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_standard_analog_voltage_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_standard_analog_current_out(self, seqid, iprot, oprot): + args = set_standard_analog_current_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_standard_analog_current_out_result() + try: + result.success = self._handler.set_standard_analog_current_out(args.num, args.value, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_standard_analog_current_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_data_485(self, seqid, iprot, oprot): + args = read_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_data_485_result() + try: + result.success = self._handler.read_data_485() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_raw_data_485(self, seqid, iprot, oprot): + args = read_raw_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_raw_data_485_result() + try: + result.success = self._handler.read_raw_data_485(args.len) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_raw_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_raw_data_485_ht(self, seqid, iprot, oprot): + args = read_raw_data_485_ht_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_raw_data_485_ht_result() + try: + result.success = self._handler.read_raw_data_485_ht(args.head, args.tail) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_raw_data_485_ht", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_raw_data_485_h(self, seqid, iprot, oprot): + args = read_raw_data_485_h_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_raw_data_485_h_result() + try: + result.success = self._handler.read_raw_data_485_h(args.head, args.len) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_raw_data_485_h", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_data_485(self, seqid, iprot, oprot): + args = write_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_data_485_result() + try: + result.success = self._handler.write_data_485(args.data) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_raw_data_485(self, seqid, iprot, oprot): + args = write_raw_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_raw_data_485_result() + try: + result.success = self._handler.write_raw_data_485(args.data) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_raw_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_raw_data_485_h(self, seqid, iprot, oprot): + args = write_raw_data_485_h_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_raw_data_485_h_result() + try: + result.success = self._handler.write_raw_data_485_h(args.data, args.head) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_raw_data_485_h", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_raw_data_485_ht(self, seqid, iprot, oprot): + args = write_raw_data_485_ht_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_raw_data_485_ht_result() + try: + result.success = self._handler.write_raw_data_485_ht(args.data, args.head, args.tail) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_raw_data_485_ht", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_read_data_485(self, seqid, iprot, oprot): + args = tool_read_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_read_data_485_result() + try: + result.success = self._handler.tool_read_data_485() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_read_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_read_raw_data_485(self, seqid, iprot, oprot): + args = tool_read_raw_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_read_raw_data_485_result() + try: + result.success = self._handler.tool_read_raw_data_485(args.len) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_read_raw_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_read_raw_data_485_h(self, seqid, iprot, oprot): + args = tool_read_raw_data_485_h_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_read_raw_data_485_h_result() + try: + result.success = self._handler.tool_read_raw_data_485_h(args.head, args.len) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_read_raw_data_485_h", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_read_raw_data_485_ht(self, seqid, iprot, oprot): + args = tool_read_raw_data_485_ht_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_read_raw_data_485_ht_result() + try: + result.success = self._handler.tool_read_raw_data_485_ht(args.head, args.tail) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_read_raw_data_485_ht", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_write_data_485(self, seqid, iprot, oprot): + args = tool_write_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_write_data_485_result() + try: + result.success = self._handler.tool_write_data_485(args.data) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_write_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_write_raw_data_485(self, seqid, iprot, oprot): + args = tool_write_raw_data_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_write_raw_data_485_result() + try: + result.success = self._handler.tool_write_raw_data_485(args.data) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_write_raw_data_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_write_raw_data_485_h(self, seqid, iprot, oprot): + args = tool_write_raw_data_485_h_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_write_raw_data_485_h_result() + try: + result.success = self._handler.tool_write_raw_data_485_h(args.data, args.head) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_write_raw_data_485_h", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tool_write_raw_data_485_ht(self, seqid, iprot, oprot): + args = tool_write_raw_data_485_ht_args() + args.read(iprot) + iprot.readMessageEnd() + result = tool_write_raw_data_485_ht_result() + try: + result.success = self._handler.tool_write_raw_data_485_ht(args.data, args.head, args.tail) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tool_write_raw_data_485_ht", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_data_can(self, seqid, iprot, oprot): + args = read_data_can_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_data_can_result() + try: + result.success = self._handler.read_data_can() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_data_can", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_raw_data_can(self, seqid, iprot, oprot): + args = read_raw_data_can_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_raw_data_can_result() + try: + result.success = self._handler.read_raw_data_can() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_raw_data_can", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_data_can(self, seqid, iprot, oprot): + args = write_data_can_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_data_can_result() + try: + result.success = self._handler.write_data_can(args.id, args.data) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_data_can", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_raw_data_can(self, seqid, iprot, oprot): + args = write_raw_data_can_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_raw_data_can_result() + try: + result.success = self._handler.write_raw_data_can(args.id, args.data) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_raw_data_can", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_function_digital_in(self, seqid, iprot, oprot): + args = get_function_digital_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_function_digital_in_result() + try: + result.success = self._handler.get_function_digital_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_function_digital_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_function_digital_out(self, seqid, iprot, oprot): + args = get_function_digital_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_function_digital_out_result() + try: + result.success = self._handler.get_function_digital_out(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_function_digital_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_bool_reg(self, seqid, iprot, oprot): + args = read_bool_reg_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_bool_reg_result() + try: + result.success = self._handler.read_bool_reg(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_bool_reg", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_word_reg(self, seqid, iprot, oprot): + args = read_word_reg_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_word_reg_result() + try: + result.success = self._handler.read_word_reg(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_word_reg", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_float_reg(self, seqid, iprot, oprot): + args = read_float_reg_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_float_reg_result() + try: + result.success = self._handler.read_float_reg(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_float_reg", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_bool_reg(self, seqid, iprot, oprot): + args = write_bool_reg_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_bool_reg_result() + try: + result.success = self._handler.write_bool_reg(args.num, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_bool_reg", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_word_reg(self, seqid, iprot, oprot): + args = write_word_reg_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_word_reg_result() + try: + result.success = self._handler.write_word_reg(args.num, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_word_reg", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_float_reg(self, seqid, iprot, oprot): + args = write_float_reg_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_float_reg_result() + try: + result.success = self._handler.write_float_reg(args.num, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_float_reg", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_function_reg_in(self, seqid, iprot, oprot): + args = get_function_reg_in_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_function_reg_in_result() + try: + result.success = self._handler.get_function_reg_in(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_function_reg_in", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_function_reg_out(self, seqid, iprot, oprot): + args = get_function_reg_out_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_function_reg_out_result() + try: + result.success = self._handler.get_function_reg_out(args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_function_reg_out", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movej(self, seqid, iprot, oprot): + args = movej_args() + args.read(iprot) + iprot.readMessageEnd() + result = movej_result() + try: + result.success = self._handler.movej(args.joints_list, args.v, args.a, args.r, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movej", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movej_pose(self, seqid, iprot, oprot): + args = movej_pose_args() + args.read(iprot) + iprot.readMessageEnd() + result = movej_pose_result() + try: + result.success = self._handler.movej_pose(args.p, args.v, args.a, args.r, args.q_near, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movej_pose", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movej2(self, seqid, iprot, oprot): + args = movej2_args() + args.read(iprot) + iprot.readMessageEnd() + result = movej2_result() + try: + result.success = self._handler.movej2(args.joints_list, args.v, args.a, args.r, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movej2", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movej_pose2(self, seqid, iprot, oprot): + args = movej_pose2_args() + args.read(iprot) + iprot.readMessageEnd() + result = movej_pose2_result() + try: + result.success = self._handler.movej_pose2(args.p, args.v, args.a, args.r, args.q_near, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movej_pose2", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movel(self, seqid, iprot, oprot): + args = movel_args() + args.read(iprot) + iprot.readMessageEnd() + result = movel_result() + try: + result.success = self._handler.movel(args.p, args.v, args.a, args.r, args.q_near, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movel", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movec(self, seqid, iprot, oprot): + args = movec_args() + args.read(iprot) + iprot.readMessageEnd() + result = movec_result() + try: + result.success = self._handler.movec(args.p1, args.p2, args.v, args.a, args.r, args.mode, args.q_near, args.tool, args.wobj, args.block, args.op, args.def_acc, args.arc_rad) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movec", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_circle(self, seqid, iprot, oprot): + args = move_circle_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_circle_result() + try: + result.success = self._handler.move_circle(args.p1, args.p2, args.v, args.a, args.r, args.mode, args.q_near, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_circle", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tcp_move(self, seqid, iprot, oprot): + args = tcp_move_args() + args.read(iprot) + iprot.readMessageEnd() + result = tcp_move_result() + try: + result.success = self._handler.tcp_move(args.pose_offset, args.v, args.a, args.r, args.tool, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tcp_move", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_tcp_move_2p(self, seqid, iprot, oprot): + args = tcp_move_2p_args() + args.read(iprot) + iprot.readMessageEnd() + result = tcp_move_2p_result() + try: + result.success = self._handler.tcp_move_2p(args.p1, args.p2, args.v, args.a, args.r, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("tcp_move_2p", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_wobj_move(self, seqid, iprot, oprot): + args = wobj_move_args() + args.read(iprot) + iprot.readMessageEnd() + result = wobj_move_result() + try: + result.success = self._handler.wobj_move(args.pose_offset, args.v, args.a, args.r, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("wobj_move", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_wobj_move_2p(self, seqid, iprot, oprot): + args = wobj_move_2p_args() + args.read(iprot) + iprot.readMessageEnd() + result = wobj_move_2p_result() + try: + result.success = self._handler.wobj_move_2p(args.p1, args.p2, args.v, args.a, args.r, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("wobj_move_2p", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_spline(self, seqid, iprot, oprot): + args = spline_args() + args.read(iprot) + iprot.readMessageEnd() + result = spline_result() + try: + result.success = self._handler.spline(args.pose_list, args.v, args.a, args.tool, args.wobj, args.block, args.op, args.r, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("spline", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_spline_op(self, seqid, iprot, oprot): + args = spline_op_args() + args.read(iprot) + iprot.readMessageEnd() + result = spline_op_result() + try: + result.success = self._handler.spline_op(args.pose_list, args.v, args.a, args.tool, args.wobj, args.block, args.op, args.r, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("spline_op", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_speedj(self, seqid, iprot, oprot): + args = speedj_args() + args.read(iprot) + iprot.readMessageEnd() + result = speedj_result() + try: + result.success = self._handler.speedj(args.joints_list, args.a, args.time, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("speedj", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_speedl(self, seqid, iprot, oprot): + args = speedl_args() + args.read(iprot) + iprot.readMessageEnd() + result = speedl_result() + try: + result.success = self._handler.speedl(args.pose_list, args.a, args.time, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("speedl", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_speed_stop(self, seqid, iprot, oprot): + args = speed_stop_args() + args.read(iprot) + iprot.readMessageEnd() + result = speed_stop_result() + try: + result.success = self._handler.speed_stop(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("speed_stop", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_servoj(self, seqid, iprot, oprot): + args = servoj_args() + args.read(iprot) + iprot.readMessageEnd() + result = servoj_result() + try: + result.success = self._handler.servoj(args.joints_list, args.v, args.a, args.block, args.kp, args.kd, args.smooth_vel, args.smooth_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("servoj", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_servoj_pose(self, seqid, iprot, oprot): + args = servoj_pose_args() + args.read(iprot) + iprot.readMessageEnd() + result = servoj_pose_result() + try: + result.success = self._handler.servoj_pose(args.pose_list, args.v, args.a, args.q_near, args.tool, args.wobj, args.block, args.kp, args.kd, args.smooth_vel, args.smooth_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("servoj_pose", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_servo_tcp(self, seqid, iprot, oprot): + args = servo_tcp_args() + args.read(iprot) + iprot.readMessageEnd() + result = servo_tcp_result() + try: + result.success = self._handler.servo_tcp(args.pose_offset, args.v, args.a, args.tool, args.block, args.kp, args.kd, args.smooth_vel, args.smooth_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("servo_tcp", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_servol(self, seqid, iprot, oprot): + args = servol_args() + args.read(iprot) + iprot.readMessageEnd() + result = servol_result() + try: + result.success = self._handler.servol(args.pose_list, args.v, args.a, args.q_near, args.tool, args.wobj, args.block, args.kp, args.kd, args.smooth_vel, args.smooth_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("servol", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_teach_mode(self, seqid, iprot, oprot): + args = teach_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = teach_mode_result() + try: + result.success = self._handler.teach_mode(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("teach_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_end_teach_mode(self, seqid, iprot, oprot): + args = end_teach_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = end_teach_mode_result() + try: + result.success = self._handler.end_teach_mode(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("end_teach_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_add_signal(self, seqid, iprot, oprot): + args = modbus_add_signal_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_add_signal_result() + try: + result.success = self._handler.modbus_add_signal(args.ip, args.slave_number, args.signal_address, args.signal_type, args.signal_name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_add_signal", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_delete_signal(self, seqid, iprot, oprot): + args = modbus_delete_signal_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_delete_signal_result() + try: + result.success = self._handler.modbus_delete_signal(args.signal_name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_delete_signal", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_read(self, seqid, iprot, oprot): + args = modbus_read_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_read_result() + try: + result.success = self._handler.modbus_read(args.signal_name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_read", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_write(self, seqid, iprot, oprot): + args = modbus_write_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_write_result() + try: + result.success = self._handler.modbus_write(args.signal_name, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_write", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_set_frequency(self, seqid, iprot, oprot): + args = modbus_set_frequency_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_set_frequency_result() + try: + self._handler.modbus_set_frequency(args.signal_name, args.frequence) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_set_frequency", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_last_error(self, seqid, iprot, oprot): + args = get_last_error_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_last_error_result() + try: + result.success = self._handler.get_last_error() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_last_error", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_noneblock_taskstate(self, seqid, iprot, oprot): + args = get_noneblock_taskstate_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_noneblock_taskstate_result() + try: + result.success = self._handler.get_noneblock_taskstate(args.id) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_noneblock_taskstate", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_log_info(self, seqid, iprot, oprot): + args = log_info_args() + args.read(iprot) + iprot.readMessageEnd() + result = log_info_result() + try: + self._handler.log_info(args.message) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("log_info", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_log_error(self, seqid, iprot, oprot): + args = log_error_args() + args.read(iprot) + iprot.readMessageEnd() + result = log_error_result() + try: + self._handler.log_error(args.message) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("log_error", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_simulation(self, seqid, iprot, oprot): + args = simulation_args() + args.read(iprot) + iprot.readMessageEnd() + result = simulation_result() + try: + result.success = self._handler.simulation(args.sim, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("simulation", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_speed(self, seqid, iprot, oprot): + args = speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = speed_result() + try: + result.success = self._handler.speed(args.val) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_robot_state(self, seqid, iprot, oprot): + args = get_robot_state_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_robot_state_result() + try: + result.success = self._handler.get_robot_state() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_robot_state", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_flange_pose(self, seqid, iprot, oprot): + args = get_flange_pose_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_flange_pose_result() + try: + result.success = self._handler.get_flange_pose() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_flange_pose", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_flange_speed(self, seqid, iprot, oprot): + args = get_flange_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_flange_speed_result() + try: + result.success = self._handler.get_flange_speed() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_flange_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_flange_acceleration(self, seqid, iprot, oprot): + args = get_flange_acceleration_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_flange_acceleration_result() + try: + result.success = self._handler.get_flange_acceleration() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_flange_acceleration", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_pose(self, seqid, iprot, oprot): + args = get_tcp_pose_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_pose_result() + try: + result.success = self._handler.get_tcp_pose() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_pose", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_speed(self, seqid, iprot, oprot): + args = get_tcp_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_speed_result() + try: + result.success = self._handler.get_tcp_speed() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_acceleration(self, seqid, iprot, oprot): + args = get_tcp_acceleration_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_acceleration_result() + try: + result.success = self._handler.get_tcp_acceleration() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_acceleration", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_force(self, seqid, iprot, oprot): + args = get_tcp_force_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_force_result() + try: + result.success = self._handler.get_tcp_force() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_force", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_actual_joints_position(self, seqid, iprot, oprot): + args = get_actual_joints_position_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_actual_joints_position_result() + try: + result.success = self._handler.get_actual_joints_position() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_actual_joints_position", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_target_joints_position(self, seqid, iprot, oprot): + args = get_target_joints_position_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_target_joints_position_result() + try: + result.success = self._handler.get_target_joints_position() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_target_joints_position", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_actual_joints_speed(self, seqid, iprot, oprot): + args = get_actual_joints_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_actual_joints_speed_result() + try: + result.success = self._handler.get_actual_joints_speed() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_actual_joints_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_target_joints_speed(self, seqid, iprot, oprot): + args = get_target_joints_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_target_joints_speed_result() + try: + result.success = self._handler.get_target_joints_speed() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_target_joints_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_actual_joints_acceleration(self, seqid, iprot, oprot): + args = get_actual_joints_acceleration_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_actual_joints_acceleration_result() + try: + result.success = self._handler.get_actual_joints_acceleration() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_actual_joints_acceleration", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_target_joints_acceleration(self, seqid, iprot, oprot): + args = get_target_joints_acceleration_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_target_joints_acceleration_result() + try: + result.success = self._handler.get_target_joints_acceleration() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_target_joints_acceleration", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_actual_joints_torque(self, seqid, iprot, oprot): + args = get_actual_joints_torque_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_actual_joints_torque_result() + try: + result.success = self._handler.get_actual_joints_torque() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_actual_joints_torque", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_target_joints_torque(self, seqid, iprot, oprot): + args = get_target_joints_torque_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_target_joints_torque_result() + try: + result.success = self._handler.get_target_joints_torque() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_target_joints_torque", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_stop_record_track(self, seqid, iprot, oprot): + args = stop_record_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = stop_record_track_result() + try: + result.success = self._handler.stop_record_track() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("stop_record_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_start_record_track(self, seqid, iprot, oprot): + args = start_record_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = start_record_track_result() + try: + result.success = self._handler.start_record_track(args.name, args.mode, args.tool, args.wobj, args.interval) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("start_record_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_collision_detect(self, seqid, iprot, oprot): + args = collision_detect_args() + args.read(iprot) + iprot.readMessageEnd() + result = collision_detect_result() + try: + result.success = self._handler.collision_detect(args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("collision_detect", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_replay(self, seqid, iprot, oprot): + args = replay_args() + args.read(iprot) + iprot.readMessageEnd() + result = replay_result() + try: + result.success = self._handler.replay(args.name, args.value, args.mode) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("replay", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_load_data(self, seqid, iprot, oprot): + args = set_load_data_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_load_data_result() + try: + result.success = self._handler.set_load_data(args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_load_data", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_start(self, seqid, iprot, oprot): + args = fc_start_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_start_result() + try: + result.success = self._handler.fc_start() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_start", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_stop(self, seqid, iprot, oprot): + args = fc_stop_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_stop_result() + try: + result.success = self._handler.fc_stop() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_stop", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_config(self, seqid, iprot, oprot): + args = fc_config_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_config_result() + try: + result.success = self._handler.fc_config(args.direction, args.ref_ft, args.damp, args.max_vel, args.number_list, args.tool, args.wobj, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_config", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_move(self, seqid, iprot, oprot): + args = fc_move_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_move_result() + try: + result.success = self._handler.fc_move(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_move", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_guard_act(self, seqid, iprot, oprot): + args = fc_guard_act_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_guard_act_result() + try: + result.success = self._handler.fc_guard_act(args.direction, args.ref_ft, args.tool, args.wobj, args.type, args.force_property) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_guard_act", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_guard_deact(self, seqid, iprot, oprot): + args = fc_guard_deact_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_guard_deact_result() + try: + result.success = self._handler.fc_guard_deact() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_guard_deact", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_force_set_value(self, seqid, iprot, oprot): + args = fc_force_set_value_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_force_set_value_result() + try: + result.success = self._handler.fc_force_set_value(args.direction, args.ref_ft) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_force_set_value", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_wait_pos(self, seqid, iprot, oprot): + args = fc_wait_pos_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_wait_pos_result() + try: + result.success = self._handler.fc_wait_pos(args.middle, args.range, args.absolute, args.duration, args.timeout) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_wait_pos", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_wait_vel(self, seqid, iprot, oprot): + args = fc_wait_vel_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_wait_vel_result() + try: + result.success = self._handler.fc_wait_vel(args.middle, args.range, args.absolute, args.duration, args.timeout) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_wait_vel", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_wait_ft(self, seqid, iprot, oprot): + args = fc_wait_ft_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_wait_ft_result() + try: + result.success = self._handler.fc_wait_ft(args.middle, args.range, args.absolute, args.duration, args.timeout) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_wait_ft", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_wait_logic(self, seqid, iprot, oprot): + args = fc_wait_logic_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_wait_logic_result() + try: + result.success = self._handler.fc_wait_logic(args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_wait_logic", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_get_ft(self, seqid, iprot, oprot): + args = fc_get_ft_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_get_ft_result() + try: + result.success = self._handler.fc_get_ft() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_get_ft", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_fc_mode_is_active(self, seqid, iprot, oprot): + args = fc_mode_is_active_args() + args.read(iprot) + iprot.readMessageEnd() + result = fc_mode_is_active_result() + try: + result.success = self._handler.fc_mode_is_active() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("fc_mode_is_active", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_start_realtime_mode(self, seqid, iprot, oprot): + args = start_realtime_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = start_realtime_mode_result() + try: + result.success = self._handler.start_realtime_mode(args.mode, args.fileter_bandwidth, args.com_lost_time) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("start_realtime_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_end_realtime_mode(self, seqid, iprot, oprot): + args = end_realtime_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = end_realtime_mode_result() + try: + result.success = self._handler.end_realtime_mode() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("end_realtime_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_realtime_data_enqueue(self, seqid, iprot, oprot): + args = realtime_data_enqueue_args() + args.read(iprot) + iprot.readMessageEnd() + result = realtime_data_enqueue_result() + try: + result.success = self._handler.realtime_data_enqueue(args.realtime_data, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("realtime_data_enqueue", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_clear_realtime_data_queue(self, seqid, iprot, oprot): + args = clear_realtime_data_queue_args() + args.read(iprot) + iprot.readMessageEnd() + result = clear_realtime_data_queue_result() + try: + result.success = self._handler.clear_realtime_data_queue() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("clear_realtime_data_queue", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_realtime_data_queue_size(self, seqid, iprot, oprot): + args = get_realtime_data_queue_size_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_realtime_data_queue_size_result() + try: + result.success = self._handler.get_realtime_data_queue_size() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_realtime_data_queue_size", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_speed_optimization(self, seqid, iprot, oprot): + args = enable_speed_optimization_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_speed_optimization_result() + try: + result.success = self._handler.enable_speed_optimization() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_speed_optimization", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_speed_optimization(self, seqid, iprot, oprot): + args = disable_speed_optimization_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_speed_optimization_result() + try: + result.success = self._handler.disable_speed_optimization() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_speed_optimization", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_change_recipe(self, seqid, iprot, oprot): + args = change_recipe_args() + args.read(iprot) + iprot.readMessageEnd() + result = change_recipe_result() + try: + self._handler.change_recipe() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("change_recipe", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_system_value_bool(self, seqid, iprot, oprot): + args = set_system_value_bool_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_system_value_bool_result() + try: + result.success = self._handler.set_system_value_bool(args.name, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_system_value_bool", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_system_value_double(self, seqid, iprot, oprot): + args = set_system_value_double_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_system_value_double_result() + try: + result.success = self._handler.set_system_value_double(args.name, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_system_value_double", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_system_value_str(self, seqid, iprot, oprot): + args = set_system_value_str_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_system_value_str_result() + try: + result.success = self._handler.set_system_value_str(args.name, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_system_value_str", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_system_value_list(self, seqid, iprot, oprot): + args = set_system_value_list_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_system_value_list_result() + try: + result.success = self._handler.set_system_value_list(args.name, args.value) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_system_value_list", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_system_value_bool(self, seqid, iprot, oprot): + args = get_system_value_bool_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_system_value_bool_result() + try: + result.success = self._handler.get_system_value_bool(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_system_value_bool", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_system_value_double(self, seqid, iprot, oprot): + args = get_system_value_double_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_system_value_double_result() + try: + result.success = self._handler.get_system_value_double(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_system_value_double", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_system_value_str(self, seqid, iprot, oprot): + args = get_system_value_str_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_system_value_str_result() + try: + result.success = self._handler.get_system_value_str(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_system_value_str", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_system_value_list(self, seqid, iprot, oprot): + args = get_system_value_list_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_system_value_list_result() + try: + result.success = self._handler.get_system_value_list(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_system_value_list", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_trackEnqueue(self, seqid, iprot, oprot): + args = trackEnqueue_args() + args.read(iprot) + iprot.readMessageEnd() + result = trackEnqueue_result() + try: + result.success = self._handler.trackEnqueue(args.track, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("trackEnqueue", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_trackEnqueueOp(self, seqid, iprot, oprot): + args = trackEnqueueOp_args() + args.read(iprot) + iprot.readMessageEnd() + result = trackEnqueueOp_result() + try: + result.success = self._handler.trackEnqueueOp(args.track, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("trackEnqueueOp", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_trackClearQueue(self, seqid, iprot, oprot): + args = trackClearQueue_args() + args.read(iprot) + iprot.readMessageEnd() + result = trackClearQueue_result() + try: + result.success = self._handler.trackClearQueue() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("trackClearQueue", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_getQueueSize(self, seqid, iprot, oprot): + args = getQueueSize_args() + args.read(iprot) + iprot.readMessageEnd() + result = getQueueSize_result() + try: + result.success = self._handler.getQueueSize() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("getQueueSize", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_trackJointMotion(self, seqid, iprot, oprot): + args = trackJointMotion_args() + args.read(iprot) + iprot.readMessageEnd() + result = trackJointMotion_result() + try: + result.success = self._handler.trackJointMotion(args.speed, args.acc, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("trackJointMotion", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_trackCartMotion(self, seqid, iprot, oprot): + args = trackCartMotion_args() + args.read(iprot) + iprot.readMessageEnd() + result = trackCartMotion_result() + try: + result.success = self._handler.trackCartMotion(args.speed, args.acc, args.block, args.tool, args.wobj, args.radius) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("trackCartMotion", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_rpc_heartbeat(self, seqid, iprot, oprot): + args = rpc_heartbeat_args() + args.read(iprot) + iprot.readMessageEnd() + result = rpc_heartbeat_result() + try: + self._handler.rpc_heartbeat(args.time) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("rpc_heartbeat", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_spiral(self, seqid, iprot, oprot): + args = move_spiral_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_spiral_result() + try: + result.success = self._handler.move_spiral(args.p1, args.p2, args.rev, args.len, args.r, args.mode, args.v, args.a, args.q_near, args.tool, args.wobj, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_spiral", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_acc_optimization(self, seqid, iprot, oprot): + args = enable_acc_optimization_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_acc_optimization_result() + try: + result.success = self._handler.enable_acc_optimization() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_acc_optimization", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_acc_optimization(self, seqid, iprot, oprot): + args = disable_acc_optimization_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_acc_optimization_result() + try: + result.success = self._handler.disable_acc_optimization() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_acc_optimization", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_baudrate_485(self, seqid, iprot, oprot): + args = set_baudrate_485_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_baudrate_485_result() + try: + result.success = self._handler.set_baudrate_485(args.value, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_baudrate_485", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_baudrate_can(self, seqid, iprot, oprot): + args = set_baudrate_can_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_baudrate_can_result() + try: + result.success = self._handler.set_baudrate_can(args.value, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_baudrate_can", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_analog_output_mode(self, seqid, iprot, oprot): + args = set_analog_output_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_analog_output_mode_result() + try: + result.success = self._handler.set_analog_output_mode(args.num, args.mode, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_analog_output_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_robotmoving(self, seqid, iprot, oprot): + args = robotmoving_args() + args.read(iprot) + iprot.readMessageEnd() + result = robotmoving_result() + try: + result.success = self._handler.robotmoving() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("robotmoving", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_write_multiple_coils(self, seqid, iprot, oprot): + args = modbus_write_multiple_coils_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_write_multiple_coils_result() + try: + result.success = self._handler.modbus_write_multiple_coils(args.slave_num, args.name, args.len, args.byte_list) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_write_multiple_coils", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_modbus_write_multiple_regs(self, seqid, iprot, oprot): + args = modbus_write_multiple_regs_args() + args.read(iprot) + iprot.readMessageEnd() + result = modbus_write_multiple_regs_result() + try: + result.success = self._handler.modbus_write_multiple_regs(args.slave_num, args.name, args.len, args.word_list) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("modbus_write_multiple_regs", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_current_project(self, seqid, iprot, oprot): + args = get_current_project_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_current_project_result() + try: + result.success = self._handler.get_current_project() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_current_project", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_files_list(self, seqid, iprot, oprot): + args = get_files_list_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_files_list_result() + try: + result.success = self._handler.get_files_list(args.path) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_files_list", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_getRobotStatus(self, seqid, iprot, oprot): + args = getRobotStatus_args() + args.read(iprot) + iprot.readMessageEnd() + result = getRobotStatus_result() + try: + result.success = self._handler.getRobotStatus() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("getRobotStatus", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_getRobotIOStatus(self, seqid, iprot, oprot): + args = getRobotIOStatus_args() + args.read(iprot) + iprot.readMessageEnd() + result = getRobotIOStatus_result() + try: + result.success = self._handler.getRobotIOStatus() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("getRobotIOStatus", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_pose_coord(self, seqid, iprot, oprot): + args = get_tcp_pose_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_pose_coord_result() + try: + result.success = self._handler.get_tcp_pose_coord(args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_pose_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_force_tool(self, seqid, iprot, oprot): + args = get_tcp_force_tool_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_force_tool_result() + try: + result.success = self._handler.get_tcp_force_tool(args.tool) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_force_tool", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_restart(self, seqid, iprot, oprot): + args = restart_args() + args.read(iprot) + iprot.readMessageEnd() + result = restart_result() + try: + result.success = self._handler.restart(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("restart", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_servo_config(self, seqid, iprot, oprot): + args = set_servo_config_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_servo_config_result() + try: + result.success = self._handler.set_servo_config(args.axis_num, args.id, args.value, args.qfmt, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_servo_config", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_apply_servo_config(self, seqid, iprot, oprot): + args = apply_servo_config_args() + args.read(iprot) + iprot.readMessageEnd() + result = apply_servo_config_result() + try: + result.success = self._handler.apply_servo_config(args.axis_num, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("apply_servo_config", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_motor_pole_pair_number(self, seqid, iprot, oprot): + args = get_motor_pole_pair_number_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_motor_pole_pair_number_result() + try: + result.success = self._handler.get_motor_pole_pair_number() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_motor_pole_pair_number", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_motor_stator_slots(self, seqid, iprot, oprot): + args = get_motor_stator_slots_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_motor_stator_slots_result() + try: + result.success = self._handler.get_motor_stator_slots() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_motor_stator_slots", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_axis_ratio(self, seqid, iprot, oprot): + args = get_axis_ratio_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_axis_ratio_result() + try: + result.success = self._handler.get_axis_ratio() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_axis_ratio", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_collision_detection_reset(self, seqid, iprot, oprot): + args = collision_detection_reset_args() + args.read(iprot) + iprot.readMessageEnd() + result = collision_detection_reset_result() + try: + result.success = self._handler.collision_detection_reset() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("collision_detection_reset", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_servo_file_params(self, seqid, iprot, oprot): + args = set_servo_file_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_servo_file_params_result() + try: + result.success = self._handler.set_servo_file_params(args.axis_num, args.id, args.name, args.value, args.qfmt) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_servo_file_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_combine_motion_config(self, seqid, iprot, oprot): + args = combine_motion_config_args() + args.read(iprot) + iprot.readMessageEnd() + result = combine_motion_config_result() + try: + result.success = self._handler.combine_motion_config(args.type, args.ref_plane, args.fq, args.amp, args.el_offset, args.az_offset, args.up_height, args.time, args.path_dwell, args.pendulum_height, args.op_list) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("combine_motion_config", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_eaxis_param(self, seqid, iprot, oprot): + args = set_eaxis_param_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_eaxis_param_result() + try: + result.success = self._handler.set_eaxis_param(args.num, args.param, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_eaxis_param", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_add_eaxis_scheme(self, seqid, iprot, oprot): + args = add_eaxis_scheme_args() + args.read(iprot) + iprot.readMessageEnd() + result = add_eaxis_scheme_result() + try: + result.success = self._handler.add_eaxis_scheme(args.num, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("add_eaxis_scheme", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_delete_eaxis_scheme(self, seqid, iprot, oprot): + args = delete_eaxis_scheme_args() + args.read(iprot) + iprot.readMessageEnd() + result = delete_eaxis_scheme_result() + try: + result.success = self._handler.delete_eaxis_scheme(args.num, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("delete_eaxis_scheme", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_eaxis_scheme(self, seqid, iprot, oprot): + args = enable_eaxis_scheme_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_eaxis_scheme_result() + try: + result.success = self._handler.enable_eaxis_scheme(args.scheme_name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_eaxis_scheme", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_eaxis_scheme(self, seqid, iprot, oprot): + args = disable_eaxis_scheme_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_eaxis_scheme_result() + try: + result.success = self._handler.disable_eaxis_scheme(args.scheme_name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_eaxis_scheme", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_eaxiss_scheme_param(self, seqid, iprot, oprot): + args = set_eaxiss_scheme_param_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_eaxiss_scheme_param_result() + try: + result.success = self._handler.set_eaxiss_scheme_param(args.num, args.param, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_eaxiss_scheme_param", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_jog(self, seqid, iprot, oprot): + args = move_jog_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_jog_result() + try: + result.success = self._handler.move_jog(args.param, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_jog", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_stop_manual_move(self, seqid, iprot, oprot): + args = stop_manual_move_args() + args.read(iprot) + iprot.readMessageEnd() + result = stop_manual_move_result() + try: + result.success = self._handler.stop_manual_move(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("stop_manual_move", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_robot_version(self, seqid, iprot, oprot): + args = get_robot_version_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_robot_version_result() + try: + result.success = self._handler.get_robot_version() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_robot_version", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_teach_pendant(self, seqid, iprot, oprot): + args = set_teach_pendant_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_teach_pendant_result() + try: + result.success = self._handler.set_teach_pendant(args.enable) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_teach_pendant", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_teach_speed(self, seqid, iprot, oprot): + args = get_teach_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_teach_speed_result() + try: + result.success = self._handler.get_teach_speed() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_teach_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_global_speed(self, seqid, iprot, oprot): + args = get_global_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_global_speed_result() + try: + result.success = self._handler.get_global_speed() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_global_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_teach_speed(self, seqid, iprot, oprot): + args = set_teach_speed_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_teach_speed_result() + try: + result.success = self._handler.set_teach_speed(args.v) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_teach_speed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_combine_motion(self, seqid, iprot, oprot): + args = enable_combine_motion_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_combine_motion_result() + try: + result.success = self._handler.enable_combine_motion() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_combine_motion", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_combine_motion(self, seqid, iprot, oprot): + args = disable_combine_motion_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_combine_motion_result() + try: + result.success = self._handler.disable_combine_motion() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_combine_motion", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_singularity_control(self, seqid, iprot, oprot): + args = enable_singularity_control_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_singularity_control_result() + try: + result.success = self._handler.enable_singularity_control() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_singularity_control", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_singularity_control(self, seqid, iprot, oprot): + args = disable_singularity_control_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_singularity_control_result() + try: + result.success = self._handler.disable_singularity_control() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_singularity_control", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_vibration_control(self, seqid, iprot, oprot): + args = enable_vibration_control_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_vibration_control_result() + try: + result.success = self._handler.enable_vibration_control() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_vibration_control", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_vibration_control(self, seqid, iprot, oprot): + args = disable_vibration_control_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_vibration_control_result() + try: + result.success = self._handler.disable_vibration_control() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_vibration_control", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_eaxis(self, seqid, iprot, oprot): + args = move_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_eaxis_result() + try: + result.success = self._handler.move_eaxis(args.scheme_name, args.epose, args.v, args.block, args.op) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movej2_eaxis(self, seqid, iprot, oprot): + args = movej2_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = movej2_eaxis_result() + try: + result.success = self._handler.movej2_eaxis(args.joints_list, args.v, args.a, args.rad, args.scheme_name, args.epose, args.eaxis_v, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movej2_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movej2_pose_eaxis(self, seqid, iprot, oprot): + args = movej2_pose_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = movej2_pose_eaxis_result() + try: + result.success = self._handler.movej2_pose_eaxis(args.p, args.v, args.a, args.rad, args.qnear, args.tool, args.wobj, args.scheme_name, args.epose, args.eaxis_v, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movej2_pose_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movel_eaxis(self, seqid, iprot, oprot): + args = movel_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = movel_eaxis_result() + try: + result.success = self._handler.movel_eaxis(args.p, args.v, args.a, args.rad, args.qnear, args.tool, args.wobj, args.scheme_name, args.epose, args.eaxis_v, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movel_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_movec_eaxis(self, seqid, iprot, oprot): + args = movec_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = movec_eaxis_result() + try: + result.success = self._handler.movec_eaxis(args.p1, args.p2, args.v, args.a, args.rad, args.qnear, args.tool, args.wobj, args.scheme_name, args.epose, args.eaxis_v, args.block, args.op, args.def_acc, args.mode, args.arc_rad) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("movec_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_circle_eaxis(self, seqid, iprot, oprot): + args = move_circle_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_circle_eaxis_result() + try: + result.success = self._handler.move_circle_eaxis(args.p1, args.p2, args.v, args.a, args.rad, args.mode, args.qnear, args.tool, args.wobj, args.scheme_name, args.epose, args.eaxis_v, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_circle_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_reach_check(self, seqid, iprot, oprot): + args = reach_check_args() + args.read(iprot) + iprot.readMessageEnd() + result = reach_check_result() + try: + result.success = self._handler.reach_check(args.base, args.wobj, args.tool, args.ref_pos, args.check_points) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("reach_check", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_jog_eaxis(self, seqid, iprot, oprot): + args = move_jog_eaxis_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_jog_eaxis_result() + try: + result.success = self._handler.move_jog_eaxis(args.name, args.direction, args.vel, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_jog_eaxis", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_eaxis_info(self, seqid, iprot, oprot): + args = get_eaxis_info_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_eaxis_info_result() + try: + result.success = self._handler.get_eaxis_info() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_eaxis_info", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_hand_teach_parameter(self, seqid, iprot, oprot): + args = set_hand_teach_parameter_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_hand_teach_parameter_result() + try: + result.success = self._handler.set_hand_teach_parameter(args.space, args.joint_scale, args.cart_scale, args.coord_type, args.direction, args.global_scale) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_hand_teach_parameter", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_pendant_type(self, seqid, iprot, oprot): + args = set_pendant_type_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_pendant_type_result() + try: + result.success = self._handler.set_pendant_type(args.type) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_pendant_type", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_blend_ahead(self, seqid, iprot, oprot): + args = set_blend_ahead_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_blend_ahead_result() + try: + result.success = self._handler.set_blend_ahead(args.per, args.num) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_blend_ahead", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_switch_mode(self, seqid, iprot, oprot): + args = switch_mode_args() + args.read(iprot) + iprot.readMessageEnd() + result = switch_mode_result() + try: + result.success = self._handler.switch_mode(args.mode) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("switch_mode", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_read_encoder_count(self, seqid, iprot, oprot): + args = read_encoder_count_args() + args.read(iprot) + iprot.readMessageEnd() + result = read_encoder_count_result() + try: + result.success = self._handler.read_encoder_count() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("read_encoder_count", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_kinematic_calibration_params(self, seqid, iprot, oprot): + args = set_kinematic_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_kinematic_calibration_params_result() + try: + result.success = self._handler.set_kinematic_calibration_params(args.params) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_kinematic_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_pos_bias(self, seqid, iprot, oprot): + args = get_pos_bias_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_pos_bias_result() + try: + result.success = self._handler.get_pos_bias() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_pos_bias", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_system_value_lists(self, seqid, iprot, oprot): + args = get_system_value_lists_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_system_value_lists_result() + try: + result.success = self._handler.get_system_value_lists(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_system_value_lists", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_origin_DH(self, seqid, iprot, oprot): + args = get_origin_DH_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_origin_DH_result() + try: + result.success = self._handler.get_origin_DH() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_origin_DH", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_calib_DH(self, seqid, iprot, oprot): + args = get_calib_DH_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_calib_DH_result() + try: + result.success = self._handler.get_calib_DH() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_calib_DH", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_robot_type(self, seqid, iprot, oprot): + args = get_robot_type_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_robot_type_result() + try: + result.success = self._handler.get_robot_type() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_robot_type", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_ext_torque(self, seqid, iprot, oprot): + args = get_ext_torque_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_ext_torque_result() + try: + result.success = self._handler.get_ext_torque() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_ext_torque", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_dynamic_calibration_params(self, seqid, iprot, oprot): + args = set_dynamic_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_dynamic_calibration_params_result() + try: + result.success = self._handler.set_dynamic_calibration_params(args.params) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_dynamic_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_dynamic_calibration_params(self, seqid, iprot, oprot): + args = get_dynamic_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_dynamic_calibration_params_result() + try: + result.success = self._handler.get_dynamic_calibration_params() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_dynamic_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_upload_robot_param_to_toolboard(self, seqid, iprot, oprot): + args = upload_robot_param_to_toolboard_args() + args.read(iprot) + iprot.readMessageEnd() + result = upload_robot_param_to_toolboard_result() + try: + result.success = self._handler.upload_robot_param_to_toolboard(args.passwd) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("upload_robot_param_to_toolboard", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_kinematic_calibration_info(self, seqid, iprot, oprot): + args = set_kinematic_calibration_info_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_kinematic_calibration_info_result() + try: + result.success = self._handler.set_kinematic_calibration_info(args.passwd, args.info) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_kinematic_calibration_info", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_dynamic_calibration_info(self, seqid, iprot, oprot): + args = set_dynamic_calibration_info_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_dynamic_calibration_info_result() + try: + result.success = self._handler.set_dynamic_calibration_info(args.passwd, args.info) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_dynamic_calibration_info", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_vibration_calibration_info(self, seqid, iprot, oprot): + args = set_vibration_calibration_info_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_vibration_calibration_info_result() + try: + result.success = self._handler.set_vibration_calibration_info(args.passwd, args.info) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_vibration_calibration_info", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_axis_motor_rated_current(self, seqid, iprot, oprot): + args = get_axis_motor_rated_current_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_axis_motor_rated_current_result() + try: + result.success = self._handler.get_axis_motor_rated_current() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_axis_motor_rated_current", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_axis_motor_kt(self, seqid, iprot, oprot): + args = get_axis_motor_kt_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_axis_motor_kt_result() + try: + result.success = self._handler.get_axis_motor_kt() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_axis_motor_kt", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_abort(self, seqid, iprot, oprot): + args = abort_args() + args.read(iprot) + iprot.readMessageEnd() + result = abort_result() + try: + result.success = self._handler.abort(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("abort", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_vibration_calibration_params(self, seqid, iprot, oprot): + args = get_vibration_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_vibration_calibration_params_result() + try: + result.success = self._handler.get_vibration_calibration_params() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_vibration_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_save_kinematic_calibration_params(self, seqid, iprot, oprot): + args = save_kinematic_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = save_kinematic_calibration_params_result() + try: + result.success = self._handler.save_kinematic_calibration_params(args.passwd) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("save_kinematic_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_save_dynamic_calibration_params(self, seqid, iprot, oprot): + args = save_dynamic_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = save_dynamic_calibration_params_result() + try: + result.success = self._handler.save_dynamic_calibration_params(args.passwd) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("save_dynamic_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_flange_pose_cmd(self, seqid, iprot, oprot): + args = get_flange_pose_cmd_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_flange_pose_cmd_result() + try: + result.success = self._handler.get_flange_pose_cmd() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_flange_pose_cmd", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_flange_speed_cmd(self, seqid, iprot, oprot): + args = get_flange_speed_cmd_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_flange_speed_cmd_result() + try: + result.success = self._handler.get_flange_speed_cmd() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_flange_speed_cmd", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_flange_acceleration_cmd(self, seqid, iprot, oprot): + args = get_flange_acceleration_cmd_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_flange_acceleration_cmd_result() + try: + result.success = self._handler.get_flange_acceleration_cmd() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_flange_acceleration_cmd", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_pose_command(self, seqid, iprot, oprot): + args = get_tcp_pose_command_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_pose_command_result() + try: + result.success = self._handler.get_tcp_pose_command() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_pose_command", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_speed_command(self, seqid, iprot, oprot): + args = get_tcp_speed_command_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_speed_command_result() + try: + result.success = self._handler.get_tcp_speed_command() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_speed_command", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_acceleration_command(self, seqid, iprot, oprot): + args = get_tcp_acceleration_command_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_acceleration_command_result() + try: + result.success = self._handler.get_tcp_acceleration_command() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_acceleration_command", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_pose_command_coord(self, seqid, iprot, oprot): + args = get_tcp_pose_command_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_pose_command_coord_result() + try: + result.success = self._handler.get_tcp_pose_command_coord(args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_pose_command_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_speed_command_coord(self, seqid, iprot, oprot): + args = get_tcp_speed_command_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_speed_command_coord_result() + try: + result.success = self._handler.get_tcp_speed_command_coord(args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_speed_command_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_acceleration_command_coord(self, seqid, iprot, oprot): + args = get_tcp_acceleration_command_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_acceleration_command_coord_result() + try: + result.success = self._handler.get_tcp_acceleration_command_coord(args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_acceleration_command_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_speed_coord(self, seqid, iprot, oprot): + args = get_tcp_speed_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_speed_coord_result() + try: + result.success = self._handler.get_tcp_speed_coord(args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_speed_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tcp_acceleration_coord(self, seqid, iprot, oprot): + args = get_tcp_acceleration_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tcp_acceleration_coord_result() + try: + result.success = self._handler.get_tcp_acceleration_coord(args.tool, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tcp_acceleration_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_clear_error_message(self, seqid, iprot, oprot): + args = clear_error_message_args() + args.read(iprot) + iprot.readMessageEnd() + result = clear_error_message_result() + try: + self._handler.clear_error_message() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("clear_error_message", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_cal_traj_time(self, seqid, iprot, oprot): + args = cal_traj_time_args() + args.read(iprot) + iprot.readMessageEnd() + result = cal_traj_time_result() + try: + result.success = self._handler.cal_traj_time(args.param) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("cal_traj_time", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_hardware_clock(self, seqid, iprot, oprot): + args = get_hardware_clock_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_hardware_clock_result() + try: + result.success = self._handler.get_hardware_clock() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_hardware_clock", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tool_data(self, seqid, iprot, oprot): + args = get_tool_data_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tool_data_result() + try: + result.success = self._handler.get_tool_data(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tool_data", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_tool_load(self, seqid, iprot, oprot): + args = get_tool_load_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_tool_load_result() + try: + result.success = self._handler.get_tool_load(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_tool_load", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_wobj(self, seqid, iprot, oprot): + args = get_wobj_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_wobj_result() + try: + result.success = self._handler.get_wobj(args.name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_wobj", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_base(self, seqid, iprot, oprot): + args = get_base_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_base_result() + try: + result.success = self._handler.get_base() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_base", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_home_pose(self, seqid, iprot, oprot): + args = get_home_pose_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_home_pose_result() + try: + result.success = self._handler.get_home_pose() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_home_pose", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_tool_data_workcell(self, seqid, iprot, oprot): + args = set_tool_data_workcell_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_tool_data_workcell_result() + try: + result.success = self._handler.set_tool_data_workcell(args.name, args.tool, args.payload, args.inertia_tensor) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_tool_data_workcell", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_wobj_workcell(self, seqid, iprot, oprot): + args = set_wobj_workcell_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_wobj_workcell_result() + try: + result.success = self._handler.set_wobj_workcell(args.name, args.wobj) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_wobj_workcell", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_simulation_state(self, seqid, iprot, oprot): + args = get_simulation_state_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_simulation_state_result() + try: + result.success = self._handler.get_simulation_state() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_simulation_state", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_save_stiffness_calibration_params(self, seqid, iprot, oprot): + args = save_stiffness_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = save_stiffness_calibration_params_result() + try: + result.success = self._handler.save_stiffness_calibration_params(args.passwd) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("save_stiffness_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_stiffness_calibration_params(self, seqid, iprot, oprot): + args = get_stiffness_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_stiffness_calibration_params_result() + try: + result.success = self._handler.get_stiffness_calibration_params() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_stiffness_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_stiffness_calibration_params(self, seqid, iprot, oprot): + args = set_stiffness_calibration_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_stiffness_calibration_params_result() + try: + result.success = self._handler.set_stiffness_calibration_params(args.params) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_stiffness_calibration_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_joint_motion_params(self, seqid, iprot, oprot): + args = get_joint_motion_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_joint_motion_params_result() + try: + result.success = self._handler.get_joint_motion_params() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_joint_motion_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_move_fixed(self, seqid, iprot, oprot): + args = move_fixed_args() + args.read(iprot) + iprot.readMessageEnd() + result = move_fixed_result() + try: + result.success = self._handler.move_fixed(args.tool, args.wobj, args.axis, args.timer, args.scheme_name, args.epose, args.eaxis_v, args.block, args.op, args.def_acc) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("move_fixed", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_track_enqueue_op_vel(self, seqid, iprot, oprot): + args = track_enqueue_op_vel_args() + args.read(iprot) + iprot.readMessageEnd() + result = track_enqueue_op_vel_result() + try: + result.success = self._handler.track_enqueue_op_vel(args.track, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("track_enqueue_op_vel", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_track_cart_vel_motion(self, seqid, iprot, oprot): + args = track_cart_vel_motion_args() + args.read(iprot) + iprot.readMessageEnd() + result = track_cart_vel_motion_result() + try: + result.success = self._handler.track_cart_vel_motion(args.acc, args.tool, args.wobj, args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("track_cart_vel_motion", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_path_offset_cal(self, seqid, iprot, oprot): + args = path_offset_cal_args() + args.read(iprot) + iprot.readMessageEnd() + result = path_offset_cal_result() + try: + result.success = self._handler.path_offset_cal(args.path_type, args.path_point, args.tool, args.wobj, args.path_offset, args.path_retract, args.tool_rotation, args.path_rotation, args.weld_rotation, args.tool_z_offset, args.path_type_list, args.plane_normals, args.average_height) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("path_offset_cal", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_installation(self, seqid, iprot, oprot): + args = set_installation_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_installation_result() + try: + result.success = self._handler.set_installation(args.installation) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_installation", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_installation(self, seqid, iprot, oprot): + args = get_installation_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_installation_result() + try: + result.success = self._handler.get_installation() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_installation", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_laser_track(self, seqid, iprot, oprot): + args = enable_laser_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_laser_track_result() + try: + result.success = self._handler.enable_laser_track() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_laser_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_laser_track_params(self, seqid, iprot, oprot): + args = set_laser_track_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_laser_track_params_result() + try: + result.success = self._handler.set_laser_track_params(args.plane, args.freq, args.lead_len, args.active, args.kp, args.kd, args.offset, args.offset_vel, args.filter_coe, args.path_length_correction, args.track_type) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_laser_track_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_laser_track(self, seqid, iprot, oprot): + args = disable_laser_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_laser_track_result() + try: + result.success = self._handler.disable_laser_track() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_laser_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_reset_laser_track(self, seqid, iprot, oprot): + args = reset_laser_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = reset_laser_track_result() + try: + result.success = self._handler.reset_laser_track() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("reset_laser_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_laser_search_pos(self, seqid, iprot, oprot): + args = write_laser_search_pos_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_laser_search_pos_result() + try: + result.success = self._handler.write_laser_search_pos(args.p) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_laser_search_pos", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_arc_system_params(self, seqid, iprot, oprot): + args = set_arc_system_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_arc_system_params_result() + try: + result.success = self._handler.set_arc_system_params(args.coeff_K, args.lag_time, args.start_time, args.sensi_coeff, args.diff_constant, args.diff_value, args.voltage_track, args.cal_lag_time, args.arc_direction, args.ref_cycle_times) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_arc_system_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_arc_track_params(self, seqid, iprot, oprot): + args = set_arc_track_params_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_arc_track_params_result() + try: + result.success = self._handler.set_arc_track_params(args.freq, args.active, args.kp, args.kd, args.max_displacement, args.max_displacement_vel, args.base_current, args.track_type, args.max_displacement_cycle, args.constant_bias) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_arc_track_params", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_enable_arc_track(self, seqid, iprot, oprot): + args = enable_arc_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = enable_arc_track_result() + try: + result.success = self._handler.enable_arc_track(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("enable_arc_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_disable_arc_track(self, seqid, iprot, oprot): + args = disable_arc_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = disable_arc_track_result() + try: + result.success = self._handler.disable_arc_track(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("disable_arc_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_arc_track_lag_time(self, seqid, iprot, oprot): + args = get_arc_track_lag_time_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_arc_track_lag_time_result() + try: + result.success = self._handler.get_arc_track_lag_time() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_arc_track_lag_time", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_reset_arc_track(self, seqid, iprot, oprot): + args = reset_arc_track_args() + args.read(iprot) + iprot.readMessageEnd() + result = reset_arc_track_result() + try: + result.success = self._handler.reset_arc_track() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("reset_arc_track", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_write_arc_current_data(self, seqid, iprot, oprot): + args = write_arc_current_data_args() + args.read(iprot) + iprot.readMessageEnd() + result = write_arc_current_data_result() + try: + result.success = self._handler.write_arc_current_data(args.current) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("write_arc_current_data", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_clear_arc_current_data(self, seqid, iprot, oprot): + args = clear_arc_current_data_args() + args.read(iprot) + iprot.readMessageEnd() + result = clear_arc_current_data_result() + try: + result.success = self._handler.clear_arc_current_data() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("clear_arc_current_data", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_start_load_identification(self, seqid, iprot, oprot): + args = start_load_identification_args() + args.read(iprot) + iprot.readMessageEnd() + result = start_load_identification_result() + try: + result.success = self._handler.start_load_identification(args.block) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("start_load_identification", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_eaxis_coord(self, seqid, iprot, oprot): + args = get_eaxis_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_eaxis_coord_result() + try: + result.success = self._handler.get_eaxis_coord(args.eaxis_scheme_name) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_eaxis_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_eaxis_coord(self, seqid, iprot, oprot): + args = set_eaxis_coord_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_eaxis_coord_result() + try: + result.success = self._handler.set_eaxis_coord(args.eaxis_scheme_name, args.coord_list) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_eaxis_coord", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_remote_tool_data(self, seqid, iprot, oprot): + args = set_remote_tool_data_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_remote_tool_data_result() + try: + result.success = self._handler.set_remote_tool_data(args.name, args.tool_offset, args.payload, args.inertia_tensor) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_remote_tool_data", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_set_remote_tool_data_workcell(self, seqid, iprot, oprot): + args = set_remote_tool_data_workcell_args() + args.read(iprot) + iprot.readMessageEnd() + result = set_remote_tool_data_workcell_result() + try: + result.success = self._handler.set_remote_tool_data_workcell(args.name, args.tool_offset, args.payload, args.inertia_tensor) + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("set_remote_tool_data_workcell", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + + def process_get_eaxis_scheme_info(self, seqid, iprot, oprot): + args = get_eaxis_scheme_info_args() + args.read(iprot) + iprot.readMessageEnd() + result = get_eaxis_scheme_info_result() + try: + result.success = self._handler.get_eaxis_scheme_info() + msg_type = TMessageType.REPLY + except TTransport.TTransportException: + raise + except TApplicationException as ex: + logging.exception('TApplication exception in handler') + msg_type = TMessageType.EXCEPTION + result = ex + except Exception: + logging.exception('Unexpected exception in handler') + msg_type = TMessageType.EXCEPTION + result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') + oprot.writeMessageBegin("get_eaxis_scheme_info", msg_type, seqid) + result.write(oprot) + oprot.writeMessageEnd() + oprot.trans.flush() + +# HELPER FUNCTIONS AND STRUCTURES + + +class power_on_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('power_on_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(power_on_args) +power_on_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class power_on_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('power_on_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(power_on_result) +power_on_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class power_off_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('power_off_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(power_off_args) +power_off_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class power_off_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('power_off_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(power_off_result) +power_off_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_args) +enable_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class enable_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_result) +enable_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_args) +disable_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class disable_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_result) +disable_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class shutdown_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('shutdown_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(shutdown_args) +shutdown_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class shutdown_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('shutdown_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(shutdown_result) +shutdown_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class stop_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('stop_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(stop_args) +stop_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class stop_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('stop_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(stop_result) +stop_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class pause_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('pause_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(pause_args) +pause_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class pause_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('pause_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(pause_result) +pause_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class resume_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('resume_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(resume_args) +resume_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class resume_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('resume_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(resume_result) +resume_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class run_program_args(object): + """ + Attributes: + - name + - block + + """ + + + def __init__(self, name=None, block=None,): + self.name = name + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('run_program_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(run_program_args) +run_program_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class run_program_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('run_program_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(run_program_result) +run_program_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_tool_data_args(object): + """ + Attributes: + - name + - tool_offset + - payload + - inertia_tensor + + """ + + + def __init__(self, name=None, tool_offset=None, payload=None, inertia_tensor=None,): + self.name = name + self.tool_offset = tool_offset + self.payload = payload + self.inertia_tensor = inertia_tensor + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tool_offset = [] + (_etype416, _size413) = iprot.readListBegin() + for _i417 in range(_size413): + _elem418 = iprot.readDouble() + self.tool_offset.append(_elem418) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.payload = [] + (_etype422, _size419) = iprot.readListBegin() + for _i423 in range(_size419): + _elem424 = iprot.readDouble() + self.payload.append(_elem424) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.inertia_tensor = [] + (_etype428, _size425) = iprot.readListBegin() + for _i429 in range(_size425): + _elem430 = iprot.readDouble() + self.inertia_tensor.append(_elem430) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_tool_data_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.tool_offset is not None: + oprot.writeFieldBegin('tool_offset', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.tool_offset)) + for iter431 in self.tool_offset: + oprot.writeDouble(iter431) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.payload is not None: + oprot.writeFieldBegin('payload', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.payload)) + for iter432 in self.payload: + oprot.writeDouble(iter432) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.inertia_tensor is not None: + oprot.writeFieldBegin('inertia_tensor', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.inertia_tensor)) + for iter433 in self.inertia_tensor: + oprot.writeDouble(iter433) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_tool_data_args) +set_tool_data_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'tool_offset', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'payload', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'inertia_tensor', (TType.DOUBLE, None, False), None, ), # 4 +) + + +class set_tool_data_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_tool_data_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_tool_data_result) +set_tool_data_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_tcp_offset_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_offset_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_offset_args) +get_tcp_offset_args.thrift_spec = ( +) + + +class get_tcp_offset_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype437, _size434) = iprot.readListBegin() + for _i438 in range(_size434): + _elem439 = iprot.readDouble() + self.success.append(_elem439) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_offset_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter440 in self.success: + oprot.writeDouble(iter440) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_offset_result) +get_tcp_offset_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class set_wobj_args(object): + """ + Attributes: + - name + - wobj + + """ + + + def __init__(self, name=None, wobj=None,): + self.name = name + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.wobj = [] + (_etype444, _size441) = iprot.readListBegin() + for _i445 in range(_size441): + _elem446 = iprot.readDouble() + self.wobj.append(_elem446) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_wobj_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.wobj)) + for iter447 in self.wobj: + oprot.writeDouble(iter447) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_wobj_args) +set_wobj_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'wobj', (TType.DOUBLE, None, False), None, ), # 2 +) + + +class set_wobj_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_wobj_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_wobj_result) +set_wobj_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_wobj_offset_args(object): + """ + Attributes: + - wobj + - active + + """ + + + def __init__(self, wobj=None, active=None,): + self.wobj = wobj + self.active = active + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.wobj = [] + (_etype451, _size448) = iprot.readListBegin() + for _i452 in range(_size448): + _elem453 = iprot.readDouble() + self.wobj.append(_elem453) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.active = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_wobj_offset_args') + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.wobj)) + for iter454 in self.wobj: + oprot.writeDouble(iter454) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.active is not None: + oprot.writeFieldBegin('active', TType.BOOL, 2) + oprot.writeBool(self.active) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_wobj_offset_args) +set_wobj_offset_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'wobj', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.BOOL, 'active', None, None, ), # 2 +) + + +class set_wobj_offset_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_wobj_offset_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_wobj_offset_result) +set_wobj_offset_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class cal_fkine_args(object): + """ + Attributes: + - joints_position + - tool + - wobj + + """ + + + def __init__(self, joints_position=None, tool=None, wobj=None,): + self.joints_position = joints_position + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joints_position = [] + (_etype458, _size455) = iprot.readListBegin() + for _i459 in range(_size455): + _elem460 = iprot.readDouble() + self.joints_position.append(_elem460) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tool = [] + (_etype464, _size461) = iprot.readListBegin() + for _i465 in range(_size461): + _elem466 = iprot.readDouble() + self.tool.append(_elem466) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.wobj = [] + (_etype470, _size467) = iprot.readListBegin() + for _i471 in range(_size467): + _elem472 = iprot.readDouble() + self.wobj.append(_elem472) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('cal_fkine_args') + if self.joints_position is not None: + oprot.writeFieldBegin('joints_position', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joints_position)) + for iter473 in self.joints_position: + oprot.writeDouble(iter473) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.tool)) + for iter474 in self.tool: + oprot.writeDouble(iter474) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.wobj)) + for iter475 in self.wobj: + oprot.writeDouble(iter475) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(cal_fkine_args) +cal_fkine_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joints_position', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'tool', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'wobj', (TType.DOUBLE, None, False), None, ), # 3 +) + + +class cal_fkine_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype479, _size476) = iprot.readListBegin() + for _i480 in range(_size476): + _elem481 = iprot.readDouble() + self.success.append(_elem481) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('cal_fkine_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter482 in self.success: + oprot.writeDouble(iter482) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(cal_fkine_result) +cal_fkine_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class cal_ikine_args(object): + """ + Attributes: + - p + - q_near + - tool + - wobj + + """ + + + def __init__(self, p=None, q_near=None, tool=None, wobj=None,): + self.p = p + self.q_near = q_near + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype486, _size483) = iprot.readListBegin() + for _i487 in range(_size483): + _elem488 = iprot.readDouble() + self.p.append(_elem488) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.q_near = [] + (_etype492, _size489) = iprot.readListBegin() + for _i493 in range(_size489): + _elem494 = iprot.readDouble() + self.q_near.append(_elem494) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.tool = [] + (_etype498, _size495) = iprot.readListBegin() + for _i499 in range(_size495): + _elem500 = iprot.readDouble() + self.tool.append(_elem500) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.wobj = [] + (_etype504, _size501) = iprot.readListBegin() + for _i505 in range(_size501): + _elem506 = iprot.readDouble() + self.wobj.append(_elem506) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('cal_ikine_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter507 in self.p: + oprot.writeDouble(iter507) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter508 in self.q_near: + oprot.writeDouble(iter508) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.tool)) + for iter509 in self.tool: + oprot.writeDouble(iter509) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.wobj)) + for iter510 in self.wobj: + oprot.writeDouble(iter510) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(cal_ikine_args) +cal_ikine_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'tool', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'wobj', (TType.DOUBLE, None, False), None, ), # 4 +) + + +class cal_ikine_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype514, _size511) = iprot.readListBegin() + for _i515 in range(_size511): + _elem516 = iprot.readDouble() + self.success.append(_elem516) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('cal_ikine_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter517 in self.success: + oprot.writeDouble(iter517) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(cal_ikine_result) +cal_ikine_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class set_digital_output_mode_args(object): + """ + Attributes: + - num + - type + - freq + - duty_cycle + + """ + + + def __init__(self, num=None, type=None, freq=None, duty_cycle=None,): + self.num = num + self.type = type + self.freq = freq + self.duty_cycle = duty_cycle + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I16: + self.type = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.freq = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.duty_cycle = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_digital_output_mode_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.type is not None: + oprot.writeFieldBegin('type', TType.I16, 2) + oprot.writeI16(self.type) + oprot.writeFieldEnd() + if self.freq is not None: + oprot.writeFieldBegin('freq', TType.I32, 3) + oprot.writeI32(self.freq) + oprot.writeFieldEnd() + if self.duty_cycle is not None: + oprot.writeFieldBegin('duty_cycle', TType.I32, 4) + oprot.writeI32(self.duty_cycle) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_digital_output_mode_args) +set_digital_output_mode_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.I16, 'type', None, None, ), # 2 + (3, TType.I32, 'freq', None, None, ), # 3 + (4, TType.I32, 'duty_cycle', None, None, ), # 4 +) + + +class set_digital_output_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_digital_output_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_digital_output_mode_result) +set_digital_output_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_standard_digital_out_args(object): + """ + Attributes: + - num + - value + - block + + """ + + + def __init__(self, num=None, value=None, block=None,): + self.num = num + self.value = value + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.value = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_standard_digital_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.BOOL, 2) + oprot.writeBool(self.value) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_standard_digital_out_args) +set_standard_digital_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.BOOL, 'value', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_standard_digital_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_standard_digital_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_standard_digital_out_result) +set_standard_digital_out_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_tool_digital_out_args(object): + """ + Attributes: + - num + - value + - block + + """ + + + def __init__(self, num=None, value=None, block=None,): + self.num = num + self.value = value + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.value = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_tool_digital_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.BOOL, 2) + oprot.writeBool(self.value) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_tool_digital_out_args) +set_tool_digital_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.BOOL, 'value', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_tool_digital_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_tool_digital_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_tool_digital_out_result) +set_tool_digital_out_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_standard_digital_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_digital_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_digital_in_args) +get_standard_digital_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_standard_digital_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_digital_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_digital_in_result) +get_standard_digital_in_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_standard_digital_out_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_digital_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_digital_out_args) +get_standard_digital_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_standard_digital_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_digital_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_digital_out_result) +get_standard_digital_out_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_tool_digital_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_digital_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_digital_in_args) +get_tool_digital_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_tool_digital_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_digital_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_digital_in_result) +get_tool_digital_in_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_tool_digital_out_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_digital_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_digital_out_args) +get_tool_digital_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_tool_digital_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_digital_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_digital_out_result) +get_tool_digital_out_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_config_digital_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_config_digital_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_config_digital_in_args) +get_config_digital_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_config_digital_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_config_digital_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_config_digital_in_result) +get_config_digital_in_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_standard_analog_voltage_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_analog_voltage_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_analog_voltage_in_args) +get_standard_analog_voltage_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_standard_analog_voltage_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.DOUBLE: + self.success = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_analog_voltage_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.DOUBLE, 0) + oprot.writeDouble(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_analog_voltage_in_result) +get_standard_analog_voltage_in_result.thrift_spec = ( + (0, TType.DOUBLE, 'success', None, None, ), # 0 +) + + +class get_tool_analog_voltage_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_analog_voltage_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_analog_voltage_in_args) +get_tool_analog_voltage_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_tool_analog_voltage_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.DOUBLE: + self.success = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_analog_voltage_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.DOUBLE, 0) + oprot.writeDouble(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_analog_voltage_in_result) +get_tool_analog_voltage_in_result.thrift_spec = ( + (0, TType.DOUBLE, 'success', None, None, ), # 0 +) + + +class get_standard_analog_current_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_analog_current_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_analog_current_in_args) +get_standard_analog_current_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_standard_analog_current_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.DOUBLE: + self.success = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_standard_analog_current_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.DOUBLE, 0) + oprot.writeDouble(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_standard_analog_current_in_result) +get_standard_analog_current_in_result.thrift_spec = ( + (0, TType.DOUBLE, 'success', None, None, ), # 0 +) + + +class set_standard_analog_voltage_out_args(object): + """ + Attributes: + - num + - value + - block + + """ + + + def __init__(self, num=None, value=None, block=None,): + self.num = num + self.value = value + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.value = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_standard_analog_voltage_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.DOUBLE, 2) + oprot.writeDouble(self.value) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_standard_analog_voltage_out_args) +set_standard_analog_voltage_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.DOUBLE, 'value', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_standard_analog_voltage_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_standard_analog_voltage_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_standard_analog_voltage_out_result) +set_standard_analog_voltage_out_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_standard_analog_current_out_args(object): + """ + Attributes: + - num + - value + - block + + """ + + + def __init__(self, num=None, value=None, block=None,): + self.num = num + self.value = value + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.value = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_standard_analog_current_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.DOUBLE, 2) + oprot.writeDouble(self.value) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_standard_analog_current_out_args) +set_standard_analog_current_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.DOUBLE, 'value', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_standard_analog_current_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_standard_analog_current_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_standard_analog_current_out_result) +set_standard_analog_current_out_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class read_data_485_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_data_485_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_data_485_args) +read_data_485_args.thrift_spec = ( +) + + +class read_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype521, _size518) = iprot.readListBegin() + for _i522 in range(_size518): + _elem523 = iprot.readByte() + self.success.append(_elem523) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter524 in self.success: + oprot.writeByte(iter524) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_data_485_result) +read_data_485_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class read_raw_data_485_args(object): + """ + Attributes: + - len + + """ + + + def __init__(self, len=None,): + self.len = len + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.len = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_485_args') + if self.len is not None: + oprot.writeFieldBegin('len', TType.I32, 1) + oprot.writeI32(self.len) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_485_args) +read_raw_data_485_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'len', None, None, ), # 1 +) + + +class read_raw_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype528, _size525) = iprot.readListBegin() + for _i529 in range(_size525): + _elem530 = iprot.readByte() + self.success.append(_elem530) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter531 in self.success: + oprot.writeByte(iter531) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_485_result) +read_raw_data_485_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class read_raw_data_485_ht_args(object): + """ + Attributes: + - head + - tail + + """ + + + def __init__(self, head=None, tail=None,): + self.head = head + self.tail = tail + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.head = [] + (_etype535, _size532) = iprot.readListBegin() + for _i536 in range(_size532): + _elem537 = iprot.readByte() + self.head.append(_elem537) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tail = [] + (_etype541, _size538) = iprot.readListBegin() + for _i542 in range(_size538): + _elem543 = iprot.readByte() + self.tail.append(_elem543) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_485_ht_args') + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter544 in self.head: + oprot.writeByte(iter544) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tail is not None: + oprot.writeFieldBegin('tail', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.tail)) + for iter545 in self.tail: + oprot.writeByte(iter545) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_485_ht_args) +read_raw_data_485_ht_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 1 + (2, TType.LIST, 'tail', (TType.BYTE, None, False), None, ), # 2 +) + + +class read_raw_data_485_ht_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype549, _size546) = iprot.readListBegin() + for _i550 in range(_size546): + _elem551 = iprot.readByte() + self.success.append(_elem551) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_485_ht_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter552 in self.success: + oprot.writeByte(iter552) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_485_ht_result) +read_raw_data_485_ht_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class read_raw_data_485_h_args(object): + """ + Attributes: + - head + - len + + """ + + + def __init__(self, head=None, len=None,): + self.head = head + self.len = len + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.head = [] + (_etype556, _size553) = iprot.readListBegin() + for _i557 in range(_size553): + _elem558 = iprot.readByte() + self.head.append(_elem558) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.len = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_485_h_args') + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter559 in self.head: + oprot.writeByte(iter559) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.len is not None: + oprot.writeFieldBegin('len', TType.I32, 2) + oprot.writeI32(self.len) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_485_h_args) +read_raw_data_485_h_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 1 + (2, TType.I32, 'len', None, None, ), # 2 +) + + +class read_raw_data_485_h_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype563, _size560) = iprot.readListBegin() + for _i564 in range(_size560): + _elem565 = iprot.readByte() + self.success.append(_elem565) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_485_h_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter566 in self.success: + oprot.writeByte(iter566) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_485_h_result) +read_raw_data_485_h_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class write_data_485_args(object): + """ + Attributes: + - data + + """ + + + def __init__(self, data=None,): + self.data = data + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype570, _size567) = iprot.readListBegin() + for _i571 in range(_size567): + _elem572 = iprot.readByte() + self.data.append(_elem572) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_data_485_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter573 in self.data: + oprot.writeByte(iter573) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_data_485_args) +write_data_485_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 +) + + +class write_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_data_485_result) +write_data_485_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class write_raw_data_485_args(object): + """ + Attributes: + - data + + """ + + + def __init__(self, data=None,): + self.data = data + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype577, _size574) = iprot.readListBegin() + for _i578 in range(_size574): + _elem579 = iprot.readByte() + self.data.append(_elem579) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_485_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter580 in self.data: + oprot.writeByte(iter580) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_485_args) +write_raw_data_485_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 +) + + +class write_raw_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_485_result) +write_raw_data_485_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class write_raw_data_485_h_args(object): + """ + Attributes: + - data + - head + + """ + + + def __init__(self, data=None, head=None,): + self.data = data + self.head = head + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype584, _size581) = iprot.readListBegin() + for _i585 in range(_size581): + _elem586 = iprot.readByte() + self.data.append(_elem586) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.head = [] + (_etype590, _size587) = iprot.readListBegin() + for _i591 in range(_size587): + _elem592 = iprot.readByte() + self.head.append(_elem592) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_485_h_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter593 in self.data: + oprot.writeByte(iter593) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter594 in self.head: + oprot.writeByte(iter594) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_485_h_args) +write_raw_data_485_h_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 + (2, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 2 +) + + +class write_raw_data_485_h_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_485_h_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_485_h_result) +write_raw_data_485_h_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class write_raw_data_485_ht_args(object): + """ + Attributes: + - data + - head + - tail + + """ + + + def __init__(self, data=None, head=None, tail=None,): + self.data = data + self.head = head + self.tail = tail + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype598, _size595) = iprot.readListBegin() + for _i599 in range(_size595): + _elem600 = iprot.readByte() + self.data.append(_elem600) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.head = [] + (_etype604, _size601) = iprot.readListBegin() + for _i605 in range(_size601): + _elem606 = iprot.readByte() + self.head.append(_elem606) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.tail = [] + (_etype610, _size607) = iprot.readListBegin() + for _i611 in range(_size607): + _elem612 = iprot.readByte() + self.tail.append(_elem612) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_485_ht_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter613 in self.data: + oprot.writeByte(iter613) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter614 in self.head: + oprot.writeByte(iter614) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tail is not None: + oprot.writeFieldBegin('tail', TType.LIST, 3) + oprot.writeListBegin(TType.BYTE, len(self.tail)) + for iter615 in self.tail: + oprot.writeByte(iter615) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_485_ht_args) +write_raw_data_485_ht_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 + (2, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 2 + (3, TType.LIST, 'tail', (TType.BYTE, None, False), None, ), # 3 +) + + +class write_raw_data_485_ht_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_485_ht_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_485_ht_result) +write_raw_data_485_ht_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class tool_read_data_485_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_data_485_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_data_485_args) +tool_read_data_485_args.thrift_spec = ( +) + + +class tool_read_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype619, _size616) = iprot.readListBegin() + for _i620 in range(_size616): + _elem621 = iprot.readByte() + self.success.append(_elem621) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter622 in self.success: + oprot.writeByte(iter622) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_data_485_result) +tool_read_data_485_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class tool_read_raw_data_485_args(object): + """ + Attributes: + - len + + """ + + + def __init__(self, len=None,): + self.len = len + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.len = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_raw_data_485_args') + if self.len is not None: + oprot.writeFieldBegin('len', TType.I32, 1) + oprot.writeI32(self.len) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_raw_data_485_args) +tool_read_raw_data_485_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'len', None, None, ), # 1 +) + + +class tool_read_raw_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype626, _size623) = iprot.readListBegin() + for _i627 in range(_size623): + _elem628 = iprot.readByte() + self.success.append(_elem628) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_raw_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter629 in self.success: + oprot.writeByte(iter629) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_raw_data_485_result) +tool_read_raw_data_485_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class tool_read_raw_data_485_h_args(object): + """ + Attributes: + - head + - len + + """ + + + def __init__(self, head=None, len=None,): + self.head = head + self.len = len + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.head = [] + (_etype633, _size630) = iprot.readListBegin() + for _i634 in range(_size630): + _elem635 = iprot.readByte() + self.head.append(_elem635) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.len = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_raw_data_485_h_args') + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter636 in self.head: + oprot.writeByte(iter636) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.len is not None: + oprot.writeFieldBegin('len', TType.I32, 2) + oprot.writeI32(self.len) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_raw_data_485_h_args) +tool_read_raw_data_485_h_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 1 + (2, TType.I32, 'len', None, None, ), # 2 +) + + +class tool_read_raw_data_485_h_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype640, _size637) = iprot.readListBegin() + for _i641 in range(_size637): + _elem642 = iprot.readByte() + self.success.append(_elem642) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_raw_data_485_h_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter643 in self.success: + oprot.writeByte(iter643) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_raw_data_485_h_result) +tool_read_raw_data_485_h_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class tool_read_raw_data_485_ht_args(object): + """ + Attributes: + - head + - tail + + """ + + + def __init__(self, head=None, tail=None,): + self.head = head + self.tail = tail + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.head = [] + (_etype647, _size644) = iprot.readListBegin() + for _i648 in range(_size644): + _elem649 = iprot.readByte() + self.head.append(_elem649) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tail = [] + (_etype653, _size650) = iprot.readListBegin() + for _i654 in range(_size650): + _elem655 = iprot.readByte() + self.tail.append(_elem655) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_raw_data_485_ht_args') + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter656 in self.head: + oprot.writeByte(iter656) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tail is not None: + oprot.writeFieldBegin('tail', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.tail)) + for iter657 in self.tail: + oprot.writeByte(iter657) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_raw_data_485_ht_args) +tool_read_raw_data_485_ht_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 1 + (2, TType.LIST, 'tail', (TType.BYTE, None, False), None, ), # 2 +) + + +class tool_read_raw_data_485_ht_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype661, _size658) = iprot.readListBegin() + for _i662 in range(_size658): + _elem663 = iprot.readByte() + self.success.append(_elem663) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_read_raw_data_485_ht_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter664 in self.success: + oprot.writeByte(iter664) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_read_raw_data_485_ht_result) +tool_read_raw_data_485_ht_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class tool_write_data_485_args(object): + """ + Attributes: + - data + + """ + + + def __init__(self, data=None,): + self.data = data + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype668, _size665) = iprot.readListBegin() + for _i669 in range(_size665): + _elem670 = iprot.readByte() + self.data.append(_elem670) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_data_485_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter671 in self.data: + oprot.writeByte(iter671) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_data_485_args) +tool_write_data_485_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 +) + + +class tool_write_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_data_485_result) +tool_write_data_485_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class tool_write_raw_data_485_args(object): + """ + Attributes: + - data + + """ + + + def __init__(self, data=None,): + self.data = data + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype675, _size672) = iprot.readListBegin() + for _i676 in range(_size672): + _elem677 = iprot.readByte() + self.data.append(_elem677) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_raw_data_485_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter678 in self.data: + oprot.writeByte(iter678) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_raw_data_485_args) +tool_write_raw_data_485_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 +) + + +class tool_write_raw_data_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_raw_data_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_raw_data_485_result) +tool_write_raw_data_485_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class tool_write_raw_data_485_h_args(object): + """ + Attributes: + - data + - head + + """ + + + def __init__(self, data=None, head=None,): + self.data = data + self.head = head + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype682, _size679) = iprot.readListBegin() + for _i683 in range(_size679): + _elem684 = iprot.readByte() + self.data.append(_elem684) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.head = [] + (_etype688, _size685) = iprot.readListBegin() + for _i689 in range(_size685): + _elem690 = iprot.readByte() + self.head.append(_elem690) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_raw_data_485_h_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter691 in self.data: + oprot.writeByte(iter691) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter692 in self.head: + oprot.writeByte(iter692) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_raw_data_485_h_args) +tool_write_raw_data_485_h_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 + (2, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 2 +) + + +class tool_write_raw_data_485_h_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_raw_data_485_h_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_raw_data_485_h_result) +tool_write_raw_data_485_h_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class tool_write_raw_data_485_ht_args(object): + """ + Attributes: + - data + - head + - tail + + """ + + + def __init__(self, data=None, head=None, tail=None,): + self.data = data + self.head = head + self.tail = tail + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.data = [] + (_etype696, _size693) = iprot.readListBegin() + for _i697 in range(_size693): + _elem698 = iprot.readByte() + self.data.append(_elem698) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.head = [] + (_etype702, _size699) = iprot.readListBegin() + for _i703 in range(_size699): + _elem704 = iprot.readByte() + self.head.append(_elem704) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.tail = [] + (_etype708, _size705) = iprot.readListBegin() + for _i709 in range(_size705): + _elem710 = iprot.readByte() + self.tail.append(_elem710) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_raw_data_485_ht_args') + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 1) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter711 in self.data: + oprot.writeByte(iter711) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.head is not None: + oprot.writeFieldBegin('head', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.head)) + for iter712 in self.head: + oprot.writeByte(iter712) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tail is not None: + oprot.writeFieldBegin('tail', TType.LIST, 3) + oprot.writeListBegin(TType.BYTE, len(self.tail)) + for iter713 in self.tail: + oprot.writeByte(iter713) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_raw_data_485_ht_args) +tool_write_raw_data_485_ht_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 1 + (2, TType.LIST, 'head', (TType.BYTE, None, False), None, ), # 2 + (3, TType.LIST, 'tail', (TType.BYTE, None, False), None, ), # 3 +) + + +class tool_write_raw_data_485_ht_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tool_write_raw_data_485_ht_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tool_write_raw_data_485_ht_result) +tool_write_raw_data_485_ht_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class read_data_can_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_data_can_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_data_can_args) +read_data_can_args.thrift_spec = ( +) + + +class read_data_can_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype717, _size714) = iprot.readListBegin() + for _i718 in range(_size714): + _elem719 = iprot.readByte() + self.success.append(_elem719) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_data_can_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter720 in self.success: + oprot.writeByte(iter720) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_data_can_result) +read_data_can_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class read_raw_data_can_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_can_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_can_args) +read_raw_data_can_args.thrift_spec = ( +) + + +class read_raw_data_can_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype724, _size721) = iprot.readListBegin() + for _i725 in range(_size721): + _elem726 = iprot.readByte() + self.success.append(_elem726) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_raw_data_can_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter727 in self.success: + oprot.writeByte(iter727) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_raw_data_can_result) +read_raw_data_can_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class write_data_can_args(object): + """ + Attributes: + - id + - data + + """ + + + def __init__(self, id=None, data=None,): + self.id = id + self.data = data + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.id = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.data = [] + (_etype731, _size728) = iprot.readListBegin() + for _i732 in range(_size728): + _elem733 = iprot.readByte() + self.data.append(_elem733) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_data_can_args') + if self.id is not None: + oprot.writeFieldBegin('id', TType.I32, 1) + oprot.writeI32(self.id) + oprot.writeFieldEnd() + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter734 in self.data: + oprot.writeByte(iter734) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_data_can_args) +write_data_can_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'id', None, None, ), # 1 + (2, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 2 +) + + +class write_data_can_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_data_can_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_data_can_result) +write_data_can_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class write_raw_data_can_args(object): + """ + Attributes: + - id + - data + + """ + + + def __init__(self, id=None, data=None,): + self.id = id + self.data = data + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.id = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.data = [] + (_etype738, _size735) = iprot.readListBegin() + for _i739 in range(_size735): + _elem740 = iprot.readByte() + self.data.append(_elem740) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_can_args') + if self.id is not None: + oprot.writeFieldBegin('id', TType.I32, 1) + oprot.writeI32(self.id) + oprot.writeFieldEnd() + if self.data is not None: + oprot.writeFieldBegin('data', TType.LIST, 2) + oprot.writeListBegin(TType.BYTE, len(self.data)) + for iter741 in self.data: + oprot.writeByte(iter741) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_can_args) +write_raw_data_can_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'id', None, None, ), # 1 + (2, TType.LIST, 'data', (TType.BYTE, None, False), None, ), # 2 +) + + +class write_raw_data_can_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_raw_data_can_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_raw_data_can_result) +write_raw_data_can_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_function_digital_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_digital_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_digital_in_args) +get_function_digital_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_function_digital_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_digital_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_digital_in_result) +get_function_digital_in_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_function_digital_out_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_digital_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_digital_out_args) +get_function_digital_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_function_digital_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_digital_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_digital_out_result) +get_function_digital_out_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class read_bool_reg_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_bool_reg_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_bool_reg_args) +read_bool_reg_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class read_bool_reg_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_bool_reg_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_bool_reg_result) +read_bool_reg_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class read_word_reg_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_word_reg_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_word_reg_args) +read_word_reg_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class read_word_reg_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_word_reg_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_word_reg_result) +read_word_reg_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class read_float_reg_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_float_reg_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_float_reg_args) +read_float_reg_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class read_float_reg_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.DOUBLE: + self.success = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_float_reg_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.DOUBLE, 0) + oprot.writeDouble(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_float_reg_result) +read_float_reg_result.thrift_spec = ( + (0, TType.DOUBLE, 'success', None, None, ), # 0 +) + + +class write_bool_reg_args(object): + """ + Attributes: + - num + - value + + """ + + + def __init__(self, num=None, value=None,): + self.num = num + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.value = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_bool_reg_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.BOOL, 2) + oprot.writeBool(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_bool_reg_args) +write_bool_reg_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.BOOL, 'value', None, None, ), # 2 +) + + +class write_bool_reg_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_bool_reg_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_bool_reg_result) +write_bool_reg_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class write_word_reg_args(object): + """ + Attributes: + - num + - value + + """ + + + def __init__(self, num=None, value=None,): + self.num = num + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_word_reg_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 2) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_word_reg_args) +write_word_reg_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.I32, 'value', None, None, ), # 2 +) + + +class write_word_reg_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_word_reg_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_word_reg_result) +write_word_reg_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class write_float_reg_args(object): + """ + Attributes: + - num + - value + + """ + + + def __init__(self, num=None, value=None,): + self.num = num + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.value = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_float_reg_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.DOUBLE, 2) + oprot.writeDouble(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_float_reg_args) +write_float_reg_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.DOUBLE, 'value', None, None, ), # 2 +) + + +class write_float_reg_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_float_reg_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_float_reg_result) +write_float_reg_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_function_reg_in_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_reg_in_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_reg_in_args) +get_function_reg_in_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_function_reg_in_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_reg_in_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_reg_in_result) +get_function_reg_in_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_function_reg_out_args(object): + """ + Attributes: + - num + + """ + + + def __init__(self, num=None,): + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_reg_out_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_reg_out_args) +get_function_reg_out_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 +) + + +class get_function_reg_out_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_function_reg_out_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_function_reg_out_result) +get_function_reg_out_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class movej_args(object): + """ + Attributes: + - joints_list + - v + - a + - r + - block + - op + - def_acc + + """ + + + def __init__(self, joints_list=None, v=None, a=None, r=None, block=None, op=None, def_acc=None,): + self.joints_list = joints_list + self.v = v + self.a = a + self.r = r + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joints_list = [] + (_etype745, _size742) = iprot.readListBegin() + for _i746 in range(_size742): + _elem747 = iprot.readDouble() + self.joints_list.append(_elem747) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej_args') + if self.joints_list is not None: + oprot.writeFieldBegin('joints_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joints_list)) + for iter748 in self.joints_list: + oprot.writeDouble(iter748) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 5) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 6) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 7) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej_args) +movej_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joints_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.BOOL, 'block', None, None, ), # 5 + (6, TType.STRUCT, 'op', [Op, None], None, ), # 6 + (7, TType.BOOL, 'def_acc', None, None, ), # 7 +) + + +class movej_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej_result) +movej_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movej_pose_args(object): + """ + Attributes: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p=None, v=None, a=None, r=None, q_near=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p = p + self.v = v + self.a = a + self.r = r + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype752, _size749) = iprot.readListBegin() + for _i753 in range(_size749): + _elem754 = iprot.readDouble() + self.p.append(_elem754) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.q_near = [] + (_etype758, _size755) = iprot.readListBegin() + for _i759 in range(_size755): + _elem760 = iprot.readDouble() + self.q_near.append(_elem760) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej_pose_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter761 in self.p: + oprot.writeDouble(iter761) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter762 in self.q_near: + oprot.writeDouble(iter762) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej_pose_args) +movej_pose_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class movej_pose_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej_pose_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej_pose_result) +movej_pose_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movej2_args(object): + """ + Attributes: + - joints_list + - v + - a + - r + - block + - op + - def_acc + + """ + + + def __init__(self, joints_list=None, v=None, a=None, r=None, block=None, op=None, def_acc=None,): + self.joints_list = joints_list + self.v = v + self.a = a + self.r = r + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joints_list = [] + (_etype766, _size763) = iprot.readListBegin() + for _i767 in range(_size763): + _elem768 = iprot.readDouble() + self.joints_list.append(_elem768) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej2_args') + if self.joints_list is not None: + oprot.writeFieldBegin('joints_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joints_list)) + for iter769 in self.joints_list: + oprot.writeDouble(iter769) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 5) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 6) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 7) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej2_args) +movej2_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joints_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.BOOL, 'block', None, None, ), # 5 + (6, TType.STRUCT, 'op', [Op, None], None, ), # 6 + (7, TType.BOOL, 'def_acc', None, None, ), # 7 +) + + +class movej2_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej2_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej2_result) +movej2_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movej_pose2_args(object): + """ + Attributes: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p=None, v=None, a=None, r=None, q_near=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p = p + self.v = v + self.a = a + self.r = r + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype773, _size770) = iprot.readListBegin() + for _i774 in range(_size770): + _elem775 = iprot.readDouble() + self.p.append(_elem775) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.q_near = [] + (_etype779, _size776) = iprot.readListBegin() + for _i780 in range(_size776): + _elem781 = iprot.readDouble() + self.q_near.append(_elem781) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej_pose2_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter782 in self.p: + oprot.writeDouble(iter782) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter783 in self.q_near: + oprot.writeDouble(iter783) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej_pose2_args) +movej_pose2_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class movej_pose2_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej_pose2_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej_pose2_result) +movej_pose2_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movel_args(object): + """ + Attributes: + - p + - v + - a + - r + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p=None, v=None, a=None, r=None, q_near=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p = p + self.v = v + self.a = a + self.r = r + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype787, _size784) = iprot.readListBegin() + for _i788 in range(_size784): + _elem789 = iprot.readDouble() + self.p.append(_elem789) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.q_near = [] + (_etype793, _size790) = iprot.readListBegin() + for _i794 in range(_size790): + _elem795 = iprot.readDouble() + self.q_near.append(_elem795) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movel_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter796 in self.p: + oprot.writeDouble(iter796) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter797 in self.q_near: + oprot.writeDouble(iter797) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movel_args) +movel_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class movel_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movel_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movel_result) +movel_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movec_args(object): + """ + Attributes: + - p1 + - p2 + - v + - a + - r + - mode + - q_near + - tool + - wobj + - block + - op + - def_acc + - arc_rad + + """ + + + def __init__(self, p1=None, p2=None, v=None, a=None, r=None, mode=None, q_near=None, tool=None, wobj=None, block=None, op=None, def_acc=None, arc_rad=None,): + self.p1 = p1 + self.p2 = p2 + self.v = v + self.a = a + self.r = r + self.mode = mode + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + self.arc_rad = arc_rad + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype801, _size798) = iprot.readListBegin() + for _i802 in range(_size798): + _elem803 = iprot.readDouble() + self.p1.append(_elem803) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype807, _size804) = iprot.readListBegin() + for _i808 in range(_size804): + _elem809 = iprot.readDouble() + self.p2.append(_elem809) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.q_near = [] + (_etype813, _size810) = iprot.readListBegin() + for _i814 in range(_size810): + _elem815 = iprot.readDouble() + self.q_near.append(_elem815) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.DOUBLE: + self.arc_rad = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movec_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter816 in self.p1: + oprot.writeDouble(iter816) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter817 in self.p2: + oprot.writeDouble(iter817) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 3) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 4) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 5) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 6) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter818 in self.q_near: + oprot.writeDouble(iter818) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 8) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 9) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 10) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 11) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 12) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + if self.arc_rad is not None: + oprot.writeFieldBegin('arc_rad', TType.DOUBLE, 13) + oprot.writeDouble(self.arc_rad) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movec_args) +movec_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'v', None, None, ), # 3 + (4, TType.DOUBLE, 'a', None, None, ), # 4 + (5, TType.DOUBLE, 'r', None, None, ), # 5 + (6, TType.I32, 'mode', None, None, ), # 6 + (7, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.STRING, 'tool', 'UTF8', None, ), # 8 + (9, TType.STRING, 'wobj', 'UTF8', None, ), # 9 + (10, TType.BOOL, 'block', None, None, ), # 10 + (11, TType.STRUCT, 'op', [Op, None], None, ), # 11 + (12, TType.BOOL, 'def_acc', None, None, ), # 12 + (13, TType.DOUBLE, 'arc_rad', None, None, ), # 13 +) + + +class movec_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movec_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movec_result) +movec_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class move_circle_args(object): + """ + Attributes: + - p1 + - p2 + - v + - a + - r + - mode + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p1=None, p2=None, v=None, a=None, r=None, mode=None, q_near=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p1 = p1 + self.p2 = p2 + self.v = v + self.a = a + self.r = r + self.mode = mode + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype822, _size819) = iprot.readListBegin() + for _i823 in range(_size819): + _elem824 = iprot.readDouble() + self.p1.append(_elem824) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype828, _size825) = iprot.readListBegin() + for _i829 in range(_size825): + _elem830 = iprot.readDouble() + self.p2.append(_elem830) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.q_near = [] + (_etype834, _size831) = iprot.readListBegin() + for _i835 in range(_size831): + _elem836 = iprot.readDouble() + self.q_near.append(_elem836) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_circle_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter837 in self.p1: + oprot.writeDouble(iter837) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter838 in self.p2: + oprot.writeDouble(iter838) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 3) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 4) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 5) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 6) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter839 in self.q_near: + oprot.writeDouble(iter839) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 8) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 9) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 10) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 11) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 12) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_circle_args) +move_circle_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'v', None, None, ), # 3 + (4, TType.DOUBLE, 'a', None, None, ), # 4 + (5, TType.DOUBLE, 'r', None, None, ), # 5 + (6, TType.I32, 'mode', None, None, ), # 6 + (7, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.STRING, 'tool', 'UTF8', None, ), # 8 + (9, TType.STRING, 'wobj', 'UTF8', None, ), # 9 + (10, TType.BOOL, 'block', None, None, ), # 10 + (11, TType.STRUCT, 'op', [Op, None], None, ), # 11 + (12, TType.BOOL, 'def_acc', None, None, ), # 12 +) + + +class move_circle_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_circle_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_circle_result) +move_circle_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class tcp_move_args(object): + """ + Attributes: + - pose_offset + - v + - a + - r + - tool + - block + - op + - def_acc + + """ + + + def __init__(self, pose_offset=None, v=None, a=None, r=None, tool=None, block=None, op=None, def_acc=None,): + self.pose_offset = pose_offset + self.v = v + self.a = a + self.r = r + self.tool = tool + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_offset = [] + (_etype843, _size840) = iprot.readListBegin() + for _i844 in range(_size840): + _elem845 = iprot.readDouble() + self.pose_offset.append(_elem845) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tcp_move_args') + if self.pose_offset is not None: + oprot.writeFieldBegin('pose_offset', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pose_offset)) + for iter846 in self.pose_offset: + oprot.writeDouble(iter846) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 5) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 6) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 7) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 8) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tcp_move_args) +tcp_move_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_offset', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.STRING, 'tool', 'UTF8', None, ), # 5 + (6, TType.BOOL, 'block', None, None, ), # 6 + (7, TType.STRUCT, 'op', [Op, None], None, ), # 7 + (8, TType.BOOL, 'def_acc', None, None, ), # 8 +) + + +class tcp_move_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tcp_move_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tcp_move_result) +tcp_move_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class tcp_move_2p_args(object): + """ + Attributes: + - p1 + - p2 + - v + - a + - r + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p1=None, p2=None, v=None, a=None, r=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p1 = p1 + self.p2 = p2 + self.v = v + self.a = a + self.r = r + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype850, _size847) = iprot.readListBegin() + for _i851 in range(_size847): + _elem852 = iprot.readDouble() + self.p1.append(_elem852) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype856, _size853) = iprot.readListBegin() + for _i857 in range(_size853): + _elem858 = iprot.readDouble() + self.p2.append(_elem858) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tcp_move_2p_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter859 in self.p1: + oprot.writeDouble(iter859) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter860 in self.p2: + oprot.writeDouble(iter860) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 3) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 4) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 5) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tcp_move_2p_args) +tcp_move_2p_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'v', None, None, ), # 3 + (4, TType.DOUBLE, 'a', None, None, ), # 4 + (5, TType.DOUBLE, 'r', None, None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class tcp_move_2p_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('tcp_move_2p_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(tcp_move_2p_result) +tcp_move_2p_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class wobj_move_args(object): + """ + Attributes: + - pose_offset + - v + - a + - r + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, pose_offset=None, v=None, a=None, r=None, wobj=None, block=None, op=None, def_acc=None,): + self.pose_offset = pose_offset + self.v = v + self.a = a + self.r = r + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_offset = [] + (_etype864, _size861) = iprot.readListBegin() + for _i865 in range(_size861): + _elem866 = iprot.readDouble() + self.pose_offset.append(_elem866) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('wobj_move_args') + if self.pose_offset is not None: + oprot.writeFieldBegin('pose_offset', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pose_offset)) + for iter867 in self.pose_offset: + oprot.writeDouble(iter867) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 4) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 5) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 6) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 7) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 8) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(wobj_move_args) +wobj_move_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_offset', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'r', None, None, ), # 4 + (5, TType.STRING, 'wobj', 'UTF8', None, ), # 5 + (6, TType.BOOL, 'block', None, None, ), # 6 + (7, TType.STRUCT, 'op', [Op, None], None, ), # 7 + (8, TType.BOOL, 'def_acc', None, None, ), # 8 +) + + +class wobj_move_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('wobj_move_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(wobj_move_result) +wobj_move_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class wobj_move_2p_args(object): + """ + Attributes: + - p1 + - p2 + - v + - a + - r + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p1=None, p2=None, v=None, a=None, r=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p1 = p1 + self.p2 = p2 + self.v = v + self.a = a + self.r = r + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype871, _size868) = iprot.readListBegin() + for _i872 in range(_size868): + _elem873 = iprot.readDouble() + self.p1.append(_elem873) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype877, _size874) = iprot.readListBegin() + for _i878 in range(_size874): + _elem879 = iprot.readDouble() + self.p2.append(_elem879) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('wobj_move_2p_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter880 in self.p1: + oprot.writeDouble(iter880) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter881 in self.p2: + oprot.writeDouble(iter881) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 3) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 4) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 5) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(wobj_move_2p_args) +wobj_move_2p_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'v', None, None, ), # 3 + (4, TType.DOUBLE, 'a', None, None, ), # 4 + (5, TType.DOUBLE, 'r', None, None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class wobj_move_2p_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('wobj_move_2p_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(wobj_move_2p_result) +wobj_move_2p_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class spline_args(object): + """ + Attributes: + - pose_list + - v + - a + - tool + - wobj + - block + - op + - r + - def_acc + + """ + + + def __init__(self, pose_list=None, v=None, a=None, tool=None, wobj=None, block=None, op=None, r=None, def_acc=None,): + self.pose_list = pose_list + self.v = v + self.a = a + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.r = r + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_list = [] + (_etype885, _size882) = iprot.readListBegin() + for _i886 in range(_size882): + _elem887 = [] + (_etype891, _size888) = iprot.readListBegin() + for _i892 in range(_size888): + _elem893 = iprot.readDouble() + _elem887.append(_elem893) + iprot.readListEnd() + self.pose_list.append(_elem887) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('spline_args') + if self.pose_list is not None: + oprot.writeFieldBegin('pose_list', TType.LIST, 1) + oprot.writeListBegin(TType.LIST, len(self.pose_list)) + for iter894 in self.pose_list: + oprot.writeListBegin(TType.DOUBLE, len(iter894)) + for iter895 in iter894: + oprot.writeDouble(iter895) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 4) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 5) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 6) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 7) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 8) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 9) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(spline_args) +spline_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_list', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.STRING, 'tool', 'UTF8', None, ), # 4 + (5, TType.STRING, 'wobj', 'UTF8', None, ), # 5 + (6, TType.BOOL, 'block', None, None, ), # 6 + (7, TType.STRUCT, 'op', [Op, None], None, ), # 7 + (8, TType.DOUBLE, 'r', None, None, ), # 8 + (9, TType.BOOL, 'def_acc', None, None, ), # 9 +) + + +class spline_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('spline_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(spline_result) +spline_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class spline_op_args(object): + """ + Attributes: + - pose_list + - v + - a + - tool + - wobj + - block + - op + - r + - def_acc + + """ + + + def __init__(self, pose_list=None, v=None, a=None, tool=None, wobj=None, block=None, op=None, r=None, def_acc=None,): + self.pose_list = pose_list + self.v = v + self.a = a + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.r = r + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_list = [] + (_etype899, _size896) = iprot.readListBegin() + for _i900 in range(_size896): + _elem901 = PointOp() + _elem901.read(iprot) + self.pose_list.append(_elem901) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('spline_op_args') + if self.pose_list is not None: + oprot.writeFieldBegin('pose_list', TType.LIST, 1) + oprot.writeListBegin(TType.STRUCT, len(self.pose_list)) + for iter902 in self.pose_list: + iter902.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 4) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 5) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 6) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 7) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 8) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 9) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(spline_op_args) +spline_op_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_list', (TType.STRUCT, [PointOp, None], False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.STRING, 'tool', 'UTF8', None, ), # 4 + (5, TType.STRING, 'wobj', 'UTF8', None, ), # 5 + (6, TType.BOOL, 'block', None, None, ), # 6 + (7, TType.STRUCT, 'op', [Op, None], None, ), # 7 + (8, TType.DOUBLE, 'r', None, None, ), # 8 + (9, TType.BOOL, 'def_acc', None, None, ), # 9 +) + + +class spline_op_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('spline_op_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(spline_op_result) +spline_op_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class speedj_args(object): + """ + Attributes: + - joints_list + - a + - time + - block + + """ + + + def __init__(self, joints_list=None, a=None, time=None, block=None,): + self.joints_list = joints_list + self.a = a + self.time = time + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joints_list = [] + (_etype906, _size903) = iprot.readListBegin() + for _i907 in range(_size903): + _elem908 = iprot.readDouble() + self.joints_list.append(_elem908) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.time = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speedj_args') + if self.joints_list is not None: + oprot.writeFieldBegin('joints_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joints_list)) + for iter909 in self.joints_list: + oprot.writeDouble(iter909) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 2) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.time is not None: + oprot.writeFieldBegin('time', TType.I32, 3) + oprot.writeI32(self.time) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 4) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speedj_args) +speedj_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joints_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'a', None, None, ), # 2 + (3, TType.I32, 'time', None, None, ), # 3 + (4, TType.BOOL, 'block', None, None, ), # 4 +) + + +class speedj_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speedj_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speedj_result) +speedj_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class speedl_args(object): + """ + Attributes: + - pose_list + - a + - time + - block + + """ + + + def __init__(self, pose_list=None, a=None, time=None, block=None,): + self.pose_list = pose_list + self.a = a + self.time = time + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_list = [] + (_etype913, _size910) = iprot.readListBegin() + for _i914 in range(_size910): + _elem915 = iprot.readDouble() + self.pose_list.append(_elem915) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.time = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speedl_args') + if self.pose_list is not None: + oprot.writeFieldBegin('pose_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pose_list)) + for iter916 in self.pose_list: + oprot.writeDouble(iter916) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 2) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.time is not None: + oprot.writeFieldBegin('time', TType.I32, 3) + oprot.writeI32(self.time) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 4) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speedl_args) +speedl_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'a', None, None, ), # 2 + (3, TType.I32, 'time', None, None, ), # 3 + (4, TType.BOOL, 'block', None, None, ), # 4 +) + + +class speedl_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speedl_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speedl_result) +speedl_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class speed_stop_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speed_stop_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speed_stop_args) +speed_stop_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class speed_stop_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speed_stop_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speed_stop_result) +speed_stop_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class servoj_args(object): + """ + Attributes: + - joints_list + - v + - a + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + + + def __init__(self, joints_list=None, v=None, a=None, block=None, kp=None, kd=None, smooth_vel=None, smooth_acc=None,): + self.joints_list = joints_list + self.v = v + self.a = a + self.block = block + self.kp = kp + self.kd = kd + self.smooth_vel = smooth_vel + self.smooth_acc = smooth_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joints_list = [] + (_etype920, _size917) = iprot.readListBegin() + for _i921 in range(_size917): + _elem922 = iprot.readDouble() + self.joints_list.append(_elem922) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.kp = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.kd = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.smooth_vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.smooth_acc = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servoj_args') + if self.joints_list is not None: + oprot.writeFieldBegin('joints_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joints_list)) + for iter923 in self.joints_list: + oprot.writeDouble(iter923) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 4) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.kp is not None: + oprot.writeFieldBegin('kp', TType.DOUBLE, 5) + oprot.writeDouble(self.kp) + oprot.writeFieldEnd() + if self.kd is not None: + oprot.writeFieldBegin('kd', TType.DOUBLE, 6) + oprot.writeDouble(self.kd) + oprot.writeFieldEnd() + if self.smooth_vel is not None: + oprot.writeFieldBegin('smooth_vel', TType.DOUBLE, 7) + oprot.writeDouble(self.smooth_vel) + oprot.writeFieldEnd() + if self.smooth_acc is not None: + oprot.writeFieldBegin('smooth_acc', TType.DOUBLE, 8) + oprot.writeDouble(self.smooth_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servoj_args) +servoj_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joints_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.BOOL, 'block', None, None, ), # 4 + (5, TType.DOUBLE, 'kp', None, None, ), # 5 + (6, TType.DOUBLE, 'kd', None, None, ), # 6 + (7, TType.DOUBLE, 'smooth_vel', None, None, ), # 7 + (8, TType.DOUBLE, 'smooth_acc', None, None, ), # 8 +) + + +class servoj_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servoj_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servoj_result) +servoj_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class servoj_pose_args(object): + """ + Attributes: + - pose_list + - v + - a + - q_near + - tool + - wobj + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + + + def __init__(self, pose_list=None, v=None, a=None, q_near=None, tool=None, wobj=None, block=None, kp=None, kd=None, smooth_vel=None, smooth_acc=None,): + self.pose_list = pose_list + self.v = v + self.a = a + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.kp = kp + self.kd = kd + self.smooth_vel = smooth_vel + self.smooth_acc = smooth_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_list = [] + (_etype927, _size924) = iprot.readListBegin() + for _i928 in range(_size924): + _elem929 = iprot.readDouble() + self.pose_list.append(_elem929) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.q_near = [] + (_etype933, _size930) = iprot.readListBegin() + for _i934 in range(_size930): + _elem935 = iprot.readDouble() + self.q_near.append(_elem935) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.kp = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.DOUBLE: + self.kd = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.DOUBLE: + self.smooth_vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.DOUBLE: + self.smooth_acc = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servoj_pose_args') + if self.pose_list is not None: + oprot.writeFieldBegin('pose_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pose_list)) + for iter936 in self.pose_list: + oprot.writeDouble(iter936) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter937 in self.q_near: + oprot.writeDouble(iter937) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 5) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 6) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 7) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.kp is not None: + oprot.writeFieldBegin('kp', TType.DOUBLE, 8) + oprot.writeDouble(self.kp) + oprot.writeFieldEnd() + if self.kd is not None: + oprot.writeFieldBegin('kd', TType.DOUBLE, 9) + oprot.writeDouble(self.kd) + oprot.writeFieldEnd() + if self.smooth_vel is not None: + oprot.writeFieldBegin('smooth_vel', TType.DOUBLE, 10) + oprot.writeDouble(self.smooth_vel) + oprot.writeFieldEnd() + if self.smooth_acc is not None: + oprot.writeFieldBegin('smooth_acc', TType.DOUBLE, 11) + oprot.writeDouble(self.smooth_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servoj_pose_args) +servoj_pose_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.STRING, 'tool', 'UTF8', None, ), # 5 + (6, TType.STRING, 'wobj', 'UTF8', None, ), # 6 + (7, TType.BOOL, 'block', None, None, ), # 7 + (8, TType.DOUBLE, 'kp', None, None, ), # 8 + (9, TType.DOUBLE, 'kd', None, None, ), # 9 + (10, TType.DOUBLE, 'smooth_vel', None, None, ), # 10 + (11, TType.DOUBLE, 'smooth_acc', None, None, ), # 11 +) + + +class servoj_pose_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servoj_pose_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servoj_pose_result) +servoj_pose_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class servo_tcp_args(object): + """ + Attributes: + - pose_offset + - v + - a + - tool + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + + + def __init__(self, pose_offset=None, v=None, a=None, tool=None, block=None, kp=None, kd=None, smooth_vel=None, smooth_acc=None,): + self.pose_offset = pose_offset + self.v = v + self.a = a + self.tool = tool + self.block = block + self.kp = kp + self.kd = kd + self.smooth_vel = smooth_vel + self.smooth_acc = smooth_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_offset = [] + (_etype941, _size938) = iprot.readListBegin() + for _i942 in range(_size938): + _elem943 = iprot.readDouble() + self.pose_offset.append(_elem943) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.kp = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.kd = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.smooth_vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.DOUBLE: + self.smooth_acc = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servo_tcp_args') + if self.pose_offset is not None: + oprot.writeFieldBegin('pose_offset', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pose_offset)) + for iter944 in self.pose_offset: + oprot.writeDouble(iter944) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 4) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 5) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.kp is not None: + oprot.writeFieldBegin('kp', TType.DOUBLE, 6) + oprot.writeDouble(self.kp) + oprot.writeFieldEnd() + if self.kd is not None: + oprot.writeFieldBegin('kd', TType.DOUBLE, 7) + oprot.writeDouble(self.kd) + oprot.writeFieldEnd() + if self.smooth_vel is not None: + oprot.writeFieldBegin('smooth_vel', TType.DOUBLE, 8) + oprot.writeDouble(self.smooth_vel) + oprot.writeFieldEnd() + if self.smooth_acc is not None: + oprot.writeFieldBegin('smooth_acc', TType.DOUBLE, 9) + oprot.writeDouble(self.smooth_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servo_tcp_args) +servo_tcp_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_offset', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.STRING, 'tool', 'UTF8', None, ), # 4 + (5, TType.BOOL, 'block', None, None, ), # 5 + (6, TType.DOUBLE, 'kp', None, None, ), # 6 + (7, TType.DOUBLE, 'kd', None, None, ), # 7 + (8, TType.DOUBLE, 'smooth_vel', None, None, ), # 8 + (9, TType.DOUBLE, 'smooth_acc', None, None, ), # 9 +) + + +class servo_tcp_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servo_tcp_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servo_tcp_result) +servo_tcp_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class servol_args(object): + """ + Attributes: + - pose_list + - v + - a + - q_near + - tool + - wobj + - block + - kp + - kd + - smooth_vel + - smooth_acc + + """ + + + def __init__(self, pose_list=None, v=None, a=None, q_near=None, tool=None, wobj=None, block=None, kp=None, kd=None, smooth_vel=None, smooth_acc=None,): + self.pose_list = pose_list + self.v = v + self.a = a + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.kp = kp + self.kd = kd + self.smooth_vel = smooth_vel + self.smooth_acc = smooth_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pose_list = [] + (_etype948, _size945) = iprot.readListBegin() + for _i949 in range(_size945): + _elem950 = iprot.readDouble() + self.pose_list.append(_elem950) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.q_near = [] + (_etype954, _size951) = iprot.readListBegin() + for _i955 in range(_size951): + _elem956 = iprot.readDouble() + self.q_near.append(_elem956) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.kp = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.DOUBLE: + self.kd = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.DOUBLE: + self.smooth_vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.DOUBLE: + self.smooth_acc = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servol_args') + if self.pose_list is not None: + oprot.writeFieldBegin('pose_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pose_list)) + for iter957 in self.pose_list: + oprot.writeDouble(iter957) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter958 in self.q_near: + oprot.writeDouble(iter958) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 5) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 6) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 7) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.kp is not None: + oprot.writeFieldBegin('kp', TType.DOUBLE, 8) + oprot.writeDouble(self.kp) + oprot.writeFieldEnd() + if self.kd is not None: + oprot.writeFieldBegin('kd', TType.DOUBLE, 9) + oprot.writeDouble(self.kd) + oprot.writeFieldEnd() + if self.smooth_vel is not None: + oprot.writeFieldBegin('smooth_vel', TType.DOUBLE, 10) + oprot.writeDouble(self.smooth_vel) + oprot.writeFieldEnd() + if self.smooth_acc is not None: + oprot.writeFieldBegin('smooth_acc', TType.DOUBLE, 11) + oprot.writeDouble(self.smooth_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servol_args) +servol_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pose_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.STRING, 'tool', 'UTF8', None, ), # 5 + (6, TType.STRING, 'wobj', 'UTF8', None, ), # 6 + (7, TType.BOOL, 'block', None, None, ), # 7 + (8, TType.DOUBLE, 'kp', None, None, ), # 8 + (9, TType.DOUBLE, 'kd', None, None, ), # 9 + (10, TType.DOUBLE, 'smooth_vel', None, None, ), # 10 + (11, TType.DOUBLE, 'smooth_acc', None, None, ), # 11 +) + + +class servol_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('servol_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(servol_result) +servol_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class teach_mode_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('teach_mode_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(teach_mode_args) +teach_mode_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class teach_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('teach_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(teach_mode_result) +teach_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class end_teach_mode_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('end_teach_mode_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(end_teach_mode_args) +end_teach_mode_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class end_teach_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('end_teach_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(end_teach_mode_result) +end_teach_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class modbus_add_signal_args(object): + """ + Attributes: + - ip + - slave_number + - signal_address + - signal_type + - signal_name + + """ + + + def __init__(self, ip=None, slave_number=None, signal_address=None, signal_type=None, signal_name=None,): + self.ip = ip + self.slave_number = slave_number + self.signal_address = signal_address + self.signal_type = signal_type + self.signal_name = signal_name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.ip = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.slave_number = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.signal_address = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.signal_type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.signal_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_add_signal_args') + if self.ip is not None: + oprot.writeFieldBegin('ip', TType.STRING, 1) + oprot.writeString(self.ip.encode('utf-8') if sys.version_info[0] == 2 else self.ip) + oprot.writeFieldEnd() + if self.slave_number is not None: + oprot.writeFieldBegin('slave_number', TType.I32, 2) + oprot.writeI32(self.slave_number) + oprot.writeFieldEnd() + if self.signal_address is not None: + oprot.writeFieldBegin('signal_address', TType.I32, 3) + oprot.writeI32(self.signal_address) + oprot.writeFieldEnd() + if self.signal_type is not None: + oprot.writeFieldBegin('signal_type', TType.I32, 4) + oprot.writeI32(self.signal_type) + oprot.writeFieldEnd() + if self.signal_name is not None: + oprot.writeFieldBegin('signal_name', TType.STRING, 5) + oprot.writeString(self.signal_name.encode('utf-8') if sys.version_info[0] == 2 else self.signal_name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_add_signal_args) +modbus_add_signal_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'ip', 'UTF8', None, ), # 1 + (2, TType.I32, 'slave_number', None, None, ), # 2 + (3, TType.I32, 'signal_address', None, None, ), # 3 + (4, TType.I32, 'signal_type', None, None, ), # 4 + (5, TType.STRING, 'signal_name', 'UTF8', None, ), # 5 +) + + +class modbus_add_signal_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_add_signal_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_add_signal_result) +modbus_add_signal_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class modbus_delete_signal_args(object): + """ + Attributes: + - signal_name + + """ + + + def __init__(self, signal_name=None,): + self.signal_name = signal_name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.signal_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_delete_signal_args') + if self.signal_name is not None: + oprot.writeFieldBegin('signal_name', TType.STRING, 1) + oprot.writeString(self.signal_name.encode('utf-8') if sys.version_info[0] == 2 else self.signal_name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_delete_signal_args) +modbus_delete_signal_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'signal_name', 'UTF8', None, ), # 1 +) + + +class modbus_delete_signal_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_delete_signal_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_delete_signal_result) +modbus_delete_signal_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class modbus_read_args(object): + """ + Attributes: + - signal_name + + """ + + + def __init__(self, signal_name=None,): + self.signal_name = signal_name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.signal_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_read_args') + if self.signal_name is not None: + oprot.writeFieldBegin('signal_name', TType.STRING, 1) + oprot.writeString(self.signal_name.encode('utf-8') if sys.version_info[0] == 2 else self.signal_name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_read_args) +modbus_read_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'signal_name', 'UTF8', None, ), # 1 +) + + +class modbus_read_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_read_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_read_result) +modbus_read_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class modbus_write_args(object): + """ + Attributes: + - signal_name + - value + + """ + + + def __init__(self, signal_name=None, value=None,): + self.signal_name = signal_name + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.signal_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_write_args') + if self.signal_name is not None: + oprot.writeFieldBegin('signal_name', TType.STRING, 1) + oprot.writeString(self.signal_name.encode('utf-8') if sys.version_info[0] == 2 else self.signal_name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 2) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_write_args) +modbus_write_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'signal_name', 'UTF8', None, ), # 1 + (2, TType.I32, 'value', None, None, ), # 2 +) + + +class modbus_write_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_write_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_write_result) +modbus_write_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class modbus_set_frequency_args(object): + """ + Attributes: + - signal_name + - frequence + + """ + + + def __init__(self, signal_name=None, frequence=None,): + self.signal_name = signal_name + self.frequence = frequence + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.signal_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.frequence = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_set_frequency_args') + if self.signal_name is not None: + oprot.writeFieldBegin('signal_name', TType.STRING, 1) + oprot.writeString(self.signal_name.encode('utf-8') if sys.version_info[0] == 2 else self.signal_name) + oprot.writeFieldEnd() + if self.frequence is not None: + oprot.writeFieldBegin('frequence', TType.I32, 2) + oprot.writeI32(self.frequence) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_set_frequency_args) +modbus_set_frequency_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'signal_name', 'UTF8', None, ), # 1 + (2, TType.I32, 'frequence', None, None, ), # 2 +) + + +class modbus_set_frequency_result(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_set_frequency_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_set_frequency_result) +modbus_set_frequency_result.thrift_spec = ( +) + + +class get_last_error_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_last_error_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_last_error_args) +get_last_error_args.thrift_spec = ( +) + + +class get_last_error_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype962, _size959) = iprot.readListBegin() + for _i963 in range(_size959): + _elem964 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.success.append(_elem964) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_last_error_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.STRING, len(self.success)) + for iter965 in self.success: + oprot.writeString(iter965.encode('utf-8') if sys.version_info[0] == 2 else iter965) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_last_error_result) +get_last_error_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.STRING, 'UTF8', False), None, ), # 0 +) + + +class get_noneblock_taskstate_args(object): + """ + Attributes: + - id + + """ + + + def __init__(self, id=None,): + self.id = id + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.id = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_noneblock_taskstate_args') + if self.id is not None: + oprot.writeFieldBegin('id', TType.I32, 1) + oprot.writeI32(self.id) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_noneblock_taskstate_args) +get_noneblock_taskstate_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'id', None, None, ), # 1 +) + + +class get_noneblock_taskstate_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_noneblock_taskstate_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_noneblock_taskstate_result) +get_noneblock_taskstate_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class log_info_args(object): + """ + Attributes: + - message + + """ + + + def __init__(self, message=None,): + self.message = message + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.message = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('log_info_args') + if self.message is not None: + oprot.writeFieldBegin('message', TType.STRING, 1) + oprot.writeString(self.message.encode('utf-8') if sys.version_info[0] == 2 else self.message) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(log_info_args) +log_info_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'message', 'UTF8', None, ), # 1 +) + + +class log_info_result(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('log_info_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(log_info_result) +log_info_result.thrift_spec = ( +) + + +class log_error_args(object): + """ + Attributes: + - message + + """ + + + def __init__(self, message=None,): + self.message = message + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.message = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('log_error_args') + if self.message is not None: + oprot.writeFieldBegin('message', TType.STRING, 1) + oprot.writeString(self.message.encode('utf-8') if sys.version_info[0] == 2 else self.message) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(log_error_args) +log_error_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'message', 'UTF8', None, ), # 1 +) + + +class log_error_result(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('log_error_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(log_error_result) +log_error_result.thrift_spec = ( +) + + +class simulation_args(object): + """ + Attributes: + - sim + - block + + """ + + + def __init__(self, sim=None, block=None,): + self.sim = sim + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.sim = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('simulation_args') + if self.sim is not None: + oprot.writeFieldBegin('sim', TType.BOOL, 1) + oprot.writeBool(self.sim) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(simulation_args) +simulation_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'sim', None, None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class simulation_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('simulation_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(simulation_result) +simulation_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class speed_args(object): + """ + Attributes: + - val + + """ + + + def __init__(self, val=None,): + self.val = val + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.DOUBLE: + self.val = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speed_args') + if self.val is not None: + oprot.writeFieldBegin('val', TType.DOUBLE, 1) + oprot.writeDouble(self.val) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speed_args) +speed_args.thrift_spec = ( + None, # 0 + (1, TType.DOUBLE, 'val', None, None, ), # 1 +) + + +class speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(speed_result) +speed_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_robot_state_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_robot_state_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_robot_state_args) +get_robot_state_args.thrift_spec = ( +) + + +class get_robot_state_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype969, _size966) = iprot.readListBegin() + for _i970 in range(_size966): + _elem971 = iprot.readByte() + self.success.append(_elem971) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_robot_state_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.BYTE, len(self.success)) + for iter972 in self.success: + oprot.writeByte(iter972) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_robot_state_result) +get_robot_state_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.BYTE, None, False), None, ), # 0 +) + + +class get_flange_pose_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_pose_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_pose_args) +get_flange_pose_args.thrift_spec = ( +) + + +class get_flange_pose_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype976, _size973) = iprot.readListBegin() + for _i977 in range(_size973): + _elem978 = iprot.readDouble() + self.success.append(_elem978) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_pose_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter979 in self.success: + oprot.writeDouble(iter979) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_pose_result) +get_flange_pose_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_flange_speed_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_speed_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_speed_args) +get_flange_speed_args.thrift_spec = ( +) + + +class get_flange_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype983, _size980) = iprot.readListBegin() + for _i984 in range(_size980): + _elem985 = iprot.readDouble() + self.success.append(_elem985) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter986 in self.success: + oprot.writeDouble(iter986) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_speed_result) +get_flange_speed_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_flange_acceleration_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_acceleration_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_acceleration_args) +get_flange_acceleration_args.thrift_spec = ( +) + + +class get_flange_acceleration_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype990, _size987) = iprot.readListBegin() + for _i991 in range(_size987): + _elem992 = iprot.readDouble() + self.success.append(_elem992) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_acceleration_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter993 in self.success: + oprot.writeDouble(iter993) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_acceleration_result) +get_flange_acceleration_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_pose_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_args) +get_tcp_pose_args.thrift_spec = ( +) + + +class get_tcp_pose_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype997, _size994) = iprot.readListBegin() + for _i998 in range(_size994): + _elem999 = iprot.readDouble() + self.success.append(_elem999) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1000 in self.success: + oprot.writeDouble(iter1000) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_result) +get_tcp_pose_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_speed_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_args) +get_tcp_speed_args.thrift_spec = ( +) + + +class get_tcp_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1004, _size1001) = iprot.readListBegin() + for _i1005 in range(_size1001): + _elem1006 = iprot.readDouble() + self.success.append(_elem1006) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1007 in self.success: + oprot.writeDouble(iter1007) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_result) +get_tcp_speed_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_acceleration_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_args) +get_tcp_acceleration_args.thrift_spec = ( +) + + +class get_tcp_acceleration_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1011, _size1008) = iprot.readListBegin() + for _i1012 in range(_size1008): + _elem1013 = iprot.readDouble() + self.success.append(_elem1013) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1014 in self.success: + oprot.writeDouble(iter1014) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_result) +get_tcp_acceleration_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_force_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_force_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_force_args) +get_tcp_force_args.thrift_spec = ( +) + + +class get_tcp_force_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1018, _size1015) = iprot.readListBegin() + for _i1019 in range(_size1015): + _elem1020 = iprot.readDouble() + self.success.append(_elem1020) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_force_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1021 in self.success: + oprot.writeDouble(iter1021) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_force_result) +get_tcp_force_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_actual_joints_position_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_position_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_position_args) +get_actual_joints_position_args.thrift_spec = ( +) + + +class get_actual_joints_position_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1025, _size1022) = iprot.readListBegin() + for _i1026 in range(_size1022): + _elem1027 = iprot.readDouble() + self.success.append(_elem1027) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_position_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1028 in self.success: + oprot.writeDouble(iter1028) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_position_result) +get_actual_joints_position_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_target_joints_position_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_position_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_position_args) +get_target_joints_position_args.thrift_spec = ( +) + + +class get_target_joints_position_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1032, _size1029) = iprot.readListBegin() + for _i1033 in range(_size1029): + _elem1034 = iprot.readDouble() + self.success.append(_elem1034) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_position_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1035 in self.success: + oprot.writeDouble(iter1035) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_position_result) +get_target_joints_position_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_actual_joints_speed_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_speed_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_speed_args) +get_actual_joints_speed_args.thrift_spec = ( +) + + +class get_actual_joints_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1039, _size1036) = iprot.readListBegin() + for _i1040 in range(_size1036): + _elem1041 = iprot.readDouble() + self.success.append(_elem1041) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1042 in self.success: + oprot.writeDouble(iter1042) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_speed_result) +get_actual_joints_speed_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_target_joints_speed_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_speed_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_speed_args) +get_target_joints_speed_args.thrift_spec = ( +) + + +class get_target_joints_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1046, _size1043) = iprot.readListBegin() + for _i1047 in range(_size1043): + _elem1048 = iprot.readDouble() + self.success.append(_elem1048) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1049 in self.success: + oprot.writeDouble(iter1049) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_speed_result) +get_target_joints_speed_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_actual_joints_acceleration_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_acceleration_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_acceleration_args) +get_actual_joints_acceleration_args.thrift_spec = ( +) + + +class get_actual_joints_acceleration_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1053, _size1050) = iprot.readListBegin() + for _i1054 in range(_size1050): + _elem1055 = iprot.readDouble() + self.success.append(_elem1055) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_acceleration_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1056 in self.success: + oprot.writeDouble(iter1056) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_acceleration_result) +get_actual_joints_acceleration_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_target_joints_acceleration_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_acceleration_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_acceleration_args) +get_target_joints_acceleration_args.thrift_spec = ( +) + + +class get_target_joints_acceleration_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1060, _size1057) = iprot.readListBegin() + for _i1061 in range(_size1057): + _elem1062 = iprot.readDouble() + self.success.append(_elem1062) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_acceleration_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1063 in self.success: + oprot.writeDouble(iter1063) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_acceleration_result) +get_target_joints_acceleration_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_actual_joints_torque_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_torque_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_torque_args) +get_actual_joints_torque_args.thrift_spec = ( +) + + +class get_actual_joints_torque_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1067, _size1064) = iprot.readListBegin() + for _i1068 in range(_size1064): + _elem1069 = iprot.readDouble() + self.success.append(_elem1069) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_actual_joints_torque_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1070 in self.success: + oprot.writeDouble(iter1070) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_actual_joints_torque_result) +get_actual_joints_torque_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_target_joints_torque_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_torque_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_torque_args) +get_target_joints_torque_args.thrift_spec = ( +) + + +class get_target_joints_torque_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1074, _size1071) = iprot.readListBegin() + for _i1075 in range(_size1071): + _elem1076 = iprot.readDouble() + self.success.append(_elem1076) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_target_joints_torque_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1077 in self.success: + oprot.writeDouble(iter1077) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_target_joints_torque_result) +get_target_joints_torque_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class stop_record_track_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('stop_record_track_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(stop_record_track_args) +stop_record_track_args.thrift_spec = ( +) + + +class stop_record_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('stop_record_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(stop_record_track_result) +stop_record_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class start_record_track_args(object): + """ + Attributes: + - name + - mode + - tool + - wobj + - interval + + """ + + + def __init__(self, name=None, mode=None, tool=None, wobj=None, interval=None,): + self.name = name + self.mode = mode + self.tool = tool + self.wobj = wobj + self.interval = interval + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.interval = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('start_record_track_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 2) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 3) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 4) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.interval is not None: + oprot.writeFieldBegin('interval', TType.DOUBLE, 5) + oprot.writeDouble(self.interval) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(start_record_track_args) +start_record_track_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.I32, 'mode', None, None, ), # 2 + (3, TType.STRING, 'tool', 'UTF8', None, ), # 3 + (4, TType.STRING, 'wobj', 'UTF8', None, ), # 4 + (5, TType.DOUBLE, 'interval', None, None, ), # 5 +) + + +class start_record_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('start_record_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(start_record_track_result) +start_record_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class collision_detect_args(object): + """ + Attributes: + - value + + """ + + + def __init__(self, value=None,): + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('collision_detect_args') + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 1) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(collision_detect_args) +collision_detect_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'value', None, None, ), # 1 +) + + +class collision_detect_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('collision_detect_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(collision_detect_result) +collision_detect_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class replay_args(object): + """ + Attributes: + - name + - value + - mode + + """ + + + def __init__(self, name=None, value=None, mode=None,): + self.name = name + self.value = value + self.mode = mode + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('replay_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 2) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 3) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(replay_args) +replay_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.I32, 'value', None, None, ), # 2 + (3, TType.I32, 'mode', None, None, ), # 3 +) + + +class replay_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('replay_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(replay_result) +replay_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_load_data_args(object): + """ + Attributes: + - value + + """ + + + def __init__(self, value=None,): + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.value = [] + (_etype1081, _size1078) = iprot.readListBegin() + for _i1082 in range(_size1078): + _elem1083 = iprot.readDouble() + self.value.append(_elem1083) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_load_data_args') + if self.value is not None: + oprot.writeFieldBegin('value', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.value)) + for iter1084 in self.value: + oprot.writeDouble(iter1084) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_load_data_args) +set_load_data_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'value', (TType.DOUBLE, None, False), None, ), # 1 +) + + +class set_load_data_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_load_data_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_load_data_result) +set_load_data_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_start_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_start_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_start_args) +fc_start_args.thrift_spec = ( +) + + +class fc_start_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_start_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_start_result) +fc_start_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_stop_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_stop_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_stop_args) +fc_stop_args.thrift_spec = ( +) + + +class fc_stop_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_stop_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_stop_result) +fc_stop_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_config_args(object): + """ + Attributes: + - direction + - ref_ft + - damp + - max_vel + - number_list + - tool + - wobj + - value + + """ + + + def __init__(self, direction=None, ref_ft=None, damp=None, max_vel=None, number_list=None, tool=None, wobj=None, value=None,): + self.direction = direction + self.ref_ft = ref_ft + self.damp = damp + self.max_vel = max_vel + self.number_list = number_list + self.tool = tool + self.wobj = wobj + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.direction = [] + (_etype1088, _size1085) = iprot.readListBegin() + for _i1089 in range(_size1085): + _elem1090 = iprot.readBool() + self.direction.append(_elem1090) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.ref_ft = [] + (_etype1094, _size1091) = iprot.readListBegin() + for _i1095 in range(_size1091): + _elem1096 = iprot.readDouble() + self.ref_ft.append(_elem1096) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.damp = [] + (_etype1100, _size1097) = iprot.readListBegin() + for _i1101 in range(_size1097): + _elem1102 = iprot.readDouble() + self.damp.append(_elem1102) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.max_vel = [] + (_etype1106, _size1103) = iprot.readListBegin() + for _i1107 in range(_size1103): + _elem1108 = iprot.readDouble() + self.max_vel.append(_elem1108) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.number_list = [] + (_etype1112, _size1109) = iprot.readListBegin() + for _i1113 in range(_size1109): + _elem1114 = iprot.readDouble() + self.number_list.append(_elem1114) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_config_args') + if self.direction is not None: + oprot.writeFieldBegin('direction', TType.LIST, 1) + oprot.writeListBegin(TType.BOOL, len(self.direction)) + for iter1115 in self.direction: + oprot.writeBool(iter1115) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.ref_ft is not None: + oprot.writeFieldBegin('ref_ft', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.ref_ft)) + for iter1116 in self.ref_ft: + oprot.writeDouble(iter1116) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.damp is not None: + oprot.writeFieldBegin('damp', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.damp)) + for iter1117 in self.damp: + oprot.writeDouble(iter1117) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.max_vel is not None: + oprot.writeFieldBegin('max_vel', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.max_vel)) + for iter1118 in self.max_vel: + oprot.writeDouble(iter1118) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.number_list is not None: + oprot.writeFieldBegin('number_list', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.number_list)) + for iter1119 in self.number_list: + oprot.writeDouble(iter1119) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 8) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_config_args) +fc_config_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'direction', (TType.BOOL, None, False), None, ), # 1 + (2, TType.LIST, 'ref_ft', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'damp', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'max_vel', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'number_list', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.I32, 'value', None, None, ), # 8 +) + + +class fc_config_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_config_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_config_result) +fc_config_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_move_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_move_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_move_args) +fc_move_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class fc_move_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_move_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_move_result) +fc_move_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_guard_act_args(object): + """ + Attributes: + - direction + - ref_ft + - tool + - wobj + - type + - force_property + + """ + + + def __init__(self, direction=None, ref_ft=None, tool=None, wobj=None, type=None, force_property=None,): + self.direction = direction + self.ref_ft = ref_ft + self.tool = tool + self.wobj = wobj + self.type = type + self.force_property = force_property + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.direction = [] + (_etype1123, _size1120) = iprot.readListBegin() + for _i1124 in range(_size1120): + _elem1125 = iprot.readBool() + self.direction.append(_elem1125) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.ref_ft = [] + (_etype1129, _size1126) = iprot.readListBegin() + for _i1130 in range(_size1126): + _elem1131 = iprot.readDouble() + self.ref_ft.append(_elem1131) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.I32: + self.force_property = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_guard_act_args') + if self.direction is not None: + oprot.writeFieldBegin('direction', TType.LIST, 1) + oprot.writeListBegin(TType.BOOL, len(self.direction)) + for iter1132 in self.direction: + oprot.writeBool(iter1132) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.ref_ft is not None: + oprot.writeFieldBegin('ref_ft', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.ref_ft)) + for iter1133 in self.ref_ft: + oprot.writeDouble(iter1133) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 3) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 4) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 5) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + if self.force_property is not None: + oprot.writeFieldBegin('force_property', TType.I32, 6) + oprot.writeI32(self.force_property) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_guard_act_args) +fc_guard_act_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'direction', (TType.BOOL, None, False), None, ), # 1 + (2, TType.LIST, 'ref_ft', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.STRING, 'tool', 'UTF8', None, ), # 3 + (4, TType.STRING, 'wobj', 'UTF8', None, ), # 4 + (5, TType.I32, 'type', None, None, ), # 5 + (6, TType.I32, 'force_property', None, None, ), # 6 +) + + +class fc_guard_act_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_guard_act_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_guard_act_result) +fc_guard_act_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_guard_deact_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_guard_deact_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_guard_deact_args) +fc_guard_deact_args.thrift_spec = ( +) + + +class fc_guard_deact_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_guard_deact_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_guard_deact_result) +fc_guard_deact_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_force_set_value_args(object): + """ + Attributes: + - direction + - ref_ft + + """ + + + def __init__(self, direction=None, ref_ft=None,): + self.direction = direction + self.ref_ft = ref_ft + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.direction = [] + (_etype1137, _size1134) = iprot.readListBegin() + for _i1138 in range(_size1134): + _elem1139 = iprot.readBool() + self.direction.append(_elem1139) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.ref_ft = [] + (_etype1143, _size1140) = iprot.readListBegin() + for _i1144 in range(_size1140): + _elem1145 = iprot.readDouble() + self.ref_ft.append(_elem1145) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_force_set_value_args') + if self.direction is not None: + oprot.writeFieldBegin('direction', TType.LIST, 1) + oprot.writeListBegin(TType.BOOL, len(self.direction)) + for iter1146 in self.direction: + oprot.writeBool(iter1146) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.ref_ft is not None: + oprot.writeFieldBegin('ref_ft', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.ref_ft)) + for iter1147 in self.ref_ft: + oprot.writeDouble(iter1147) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_force_set_value_args) +fc_force_set_value_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'direction', (TType.BOOL, None, False), None, ), # 1 + (2, TType.LIST, 'ref_ft', (TType.DOUBLE, None, False), None, ), # 2 +) + + +class fc_force_set_value_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_force_set_value_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_force_set_value_result) +fc_force_set_value_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_wait_pos_args(object): + """ + Attributes: + - middle + - range + - absolute + - duration + - timeout + + """ + + + def __init__(self, middle=None, range=None, absolute=None, duration=None, timeout=None,): + self.middle = middle + self.range = range + self.absolute = absolute + self.duration = duration + self.timeout = timeout + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.middle = [] + (_etype1151, _size1148) = iprot.readListBegin() + for _i1152 in range(_size1148): + _elem1153 = iprot.readDouble() + self.middle.append(_elem1153) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.range = [] + (_etype1157, _size1154) = iprot.readListBegin() + for _i1158 in range(_size1154): + _elem1159 = iprot.readDouble() + self.range.append(_elem1159) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.absolute = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.duration = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.timeout = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_pos_args') + if self.middle is not None: + oprot.writeFieldBegin('middle', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.middle)) + for iter1160 in self.middle: + oprot.writeDouble(iter1160) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.range is not None: + oprot.writeFieldBegin('range', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.range)) + for iter1161 in self.range: + oprot.writeDouble(iter1161) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.absolute is not None: + oprot.writeFieldBegin('absolute', TType.BOOL, 3) + oprot.writeBool(self.absolute) + oprot.writeFieldEnd() + if self.duration is not None: + oprot.writeFieldBegin('duration', TType.I32, 4) + oprot.writeI32(self.duration) + oprot.writeFieldEnd() + if self.timeout is not None: + oprot.writeFieldBegin('timeout', TType.I32, 5) + oprot.writeI32(self.timeout) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_pos_args) +fc_wait_pos_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'middle', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'range', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.BOOL, 'absolute', None, None, ), # 3 + (4, TType.I32, 'duration', None, None, ), # 4 + (5, TType.I32, 'timeout', None, None, ), # 5 +) + + +class fc_wait_pos_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_pos_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_pos_result) +fc_wait_pos_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_wait_vel_args(object): + """ + Attributes: + - middle + - range + - absolute + - duration + - timeout + + """ + + + def __init__(self, middle=None, range=None, absolute=None, duration=None, timeout=None,): + self.middle = middle + self.range = range + self.absolute = absolute + self.duration = duration + self.timeout = timeout + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.middle = [] + (_etype1165, _size1162) = iprot.readListBegin() + for _i1166 in range(_size1162): + _elem1167 = iprot.readDouble() + self.middle.append(_elem1167) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.range = [] + (_etype1171, _size1168) = iprot.readListBegin() + for _i1172 in range(_size1168): + _elem1173 = iprot.readDouble() + self.range.append(_elem1173) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.absolute = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.duration = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.timeout = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_vel_args') + if self.middle is not None: + oprot.writeFieldBegin('middle', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.middle)) + for iter1174 in self.middle: + oprot.writeDouble(iter1174) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.range is not None: + oprot.writeFieldBegin('range', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.range)) + for iter1175 in self.range: + oprot.writeDouble(iter1175) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.absolute is not None: + oprot.writeFieldBegin('absolute', TType.BOOL, 3) + oprot.writeBool(self.absolute) + oprot.writeFieldEnd() + if self.duration is not None: + oprot.writeFieldBegin('duration', TType.I32, 4) + oprot.writeI32(self.duration) + oprot.writeFieldEnd() + if self.timeout is not None: + oprot.writeFieldBegin('timeout', TType.I32, 5) + oprot.writeI32(self.timeout) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_vel_args) +fc_wait_vel_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'middle', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'range', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.BOOL, 'absolute', None, None, ), # 3 + (4, TType.I32, 'duration', None, None, ), # 4 + (5, TType.I32, 'timeout', None, None, ), # 5 +) + + +class fc_wait_vel_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_vel_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_vel_result) +fc_wait_vel_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_wait_ft_args(object): + """ + Attributes: + - middle + - range + - absolute + - duration + - timeout + + """ + + + def __init__(self, middle=None, range=None, absolute=None, duration=None, timeout=None,): + self.middle = middle + self.range = range + self.absolute = absolute + self.duration = duration + self.timeout = timeout + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.middle = [] + (_etype1179, _size1176) = iprot.readListBegin() + for _i1180 in range(_size1176): + _elem1181 = iprot.readDouble() + self.middle.append(_elem1181) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.range = [] + (_etype1185, _size1182) = iprot.readListBegin() + for _i1186 in range(_size1182): + _elem1187 = iprot.readDouble() + self.range.append(_elem1187) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.absolute = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.duration = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.timeout = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_ft_args') + if self.middle is not None: + oprot.writeFieldBegin('middle', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.middle)) + for iter1188 in self.middle: + oprot.writeDouble(iter1188) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.range is not None: + oprot.writeFieldBegin('range', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.range)) + for iter1189 in self.range: + oprot.writeDouble(iter1189) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.absolute is not None: + oprot.writeFieldBegin('absolute', TType.BOOL, 3) + oprot.writeBool(self.absolute) + oprot.writeFieldEnd() + if self.duration is not None: + oprot.writeFieldBegin('duration', TType.I32, 4) + oprot.writeI32(self.duration) + oprot.writeFieldEnd() + if self.timeout is not None: + oprot.writeFieldBegin('timeout', TType.I32, 5) + oprot.writeI32(self.timeout) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_ft_args) +fc_wait_ft_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'middle', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'range', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.BOOL, 'absolute', None, None, ), # 3 + (4, TType.I32, 'duration', None, None, ), # 4 + (5, TType.I32, 'timeout', None, None, ), # 5 +) + + +class fc_wait_ft_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_ft_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_ft_result) +fc_wait_ft_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_wait_logic_args(object): + """ + Attributes: + - value + + """ + + + def __init__(self, value=None,): + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.value = [] + (_etype1193, _size1190) = iprot.readListBegin() + for _i1194 in range(_size1190): + _elem1195 = iprot.readI32() + self.value.append(_elem1195) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_logic_args') + if self.value is not None: + oprot.writeFieldBegin('value', TType.LIST, 1) + oprot.writeListBegin(TType.I32, len(self.value)) + for iter1196 in self.value: + oprot.writeI32(iter1196) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_logic_args) +fc_wait_logic_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'value', (TType.I32, None, False), None, ), # 1 +) + + +class fc_wait_logic_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_wait_logic_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_wait_logic_result) +fc_wait_logic_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class fc_get_ft_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_get_ft_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_get_ft_args) +fc_get_ft_args.thrift_spec = ( +) + + +class fc_get_ft_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1200, _size1197) = iprot.readListBegin() + for _i1201 in range(_size1197): + _elem1202 = iprot.readDouble() + self.success.append(_elem1202) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_get_ft_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1203 in self.success: + oprot.writeDouble(iter1203) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_get_ft_result) +fc_get_ft_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class fc_mode_is_active_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_mode_is_active_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_mode_is_active_args) +fc_mode_is_active_args.thrift_spec = ( +) + + +class fc_mode_is_active_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('fc_mode_is_active_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(fc_mode_is_active_result) +fc_mode_is_active_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class start_realtime_mode_args(object): + """ + Attributes: + - mode + - fileter_bandwidth + - com_lost_time + + """ + + + def __init__(self, mode=None, fileter_bandwidth=None, com_lost_time=None,): + self.mode = mode + self.fileter_bandwidth = fileter_bandwidth + self.com_lost_time = com_lost_time + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.fileter_bandwidth = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.com_lost_time = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('start_realtime_mode_args') + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 1) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.fileter_bandwidth is not None: + oprot.writeFieldBegin('fileter_bandwidth', TType.DOUBLE, 2) + oprot.writeDouble(self.fileter_bandwidth) + oprot.writeFieldEnd() + if self.com_lost_time is not None: + oprot.writeFieldBegin('com_lost_time', TType.DOUBLE, 3) + oprot.writeDouble(self.com_lost_time) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(start_realtime_mode_args) +start_realtime_mode_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'mode', None, None, ), # 1 + (2, TType.DOUBLE, 'fileter_bandwidth', None, None, ), # 2 + (3, TType.DOUBLE, 'com_lost_time', None, None, ), # 3 +) + + +class start_realtime_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('start_realtime_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(start_realtime_mode_result) +start_realtime_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class end_realtime_mode_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('end_realtime_mode_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(end_realtime_mode_args) +end_realtime_mode_args.thrift_spec = ( +) + + +class end_realtime_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('end_realtime_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(end_realtime_mode_result) +end_realtime_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class realtime_data_enqueue_args(object): + """ + Attributes: + - realtime_data + - block + + """ + + + def __init__(self, realtime_data=None, block=None,): + self.realtime_data = realtime_data + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.realtime_data = [] + (_etype1207, _size1204) = iprot.readListBegin() + for _i1208 in range(_size1204): + _elem1209 = RealTimeControlData() + _elem1209.read(iprot) + self.realtime_data.append(_elem1209) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('realtime_data_enqueue_args') + if self.realtime_data is not None: + oprot.writeFieldBegin('realtime_data', TType.LIST, 1) + oprot.writeListBegin(TType.STRUCT, len(self.realtime_data)) + for iter1210 in self.realtime_data: + iter1210.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(realtime_data_enqueue_args) +realtime_data_enqueue_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'realtime_data', (TType.STRUCT, [RealTimeControlData, None], False), None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class realtime_data_enqueue_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('realtime_data_enqueue_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(realtime_data_enqueue_result) +realtime_data_enqueue_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class clear_realtime_data_queue_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('clear_realtime_data_queue_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(clear_realtime_data_queue_args) +clear_realtime_data_queue_args.thrift_spec = ( +) + + +class clear_realtime_data_queue_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('clear_realtime_data_queue_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(clear_realtime_data_queue_result) +clear_realtime_data_queue_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_realtime_data_queue_size_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_realtime_data_queue_size_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_realtime_data_queue_size_args) +get_realtime_data_queue_size_args.thrift_spec = ( +) + + +class get_realtime_data_queue_size_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_realtime_data_queue_size_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_realtime_data_queue_size_result) +get_realtime_data_queue_size_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_speed_optimization_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_speed_optimization_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_speed_optimization_args) +enable_speed_optimization_args.thrift_spec = ( +) + + +class enable_speed_optimization_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_speed_optimization_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_speed_optimization_result) +enable_speed_optimization_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_speed_optimization_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_speed_optimization_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_speed_optimization_args) +disable_speed_optimization_args.thrift_spec = ( +) + + +class disable_speed_optimization_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_speed_optimization_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_speed_optimization_result) +disable_speed_optimization_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class change_recipe_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('change_recipe_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(change_recipe_args) +change_recipe_args.thrift_spec = ( +) + + +class change_recipe_result(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('change_recipe_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(change_recipe_result) +change_recipe_result.thrift_spec = ( +) + + +class set_system_value_bool_args(object): + """ + Attributes: + - name + - value + + """ + + + def __init__(self, name=None, value=None,): + self.name = name + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.value = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_bool_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.BOOL, 2) + oprot.writeBool(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_bool_args) +set_system_value_bool_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.BOOL, 'value', None, None, ), # 2 +) + + +class set_system_value_bool_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_bool_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_bool_result) +set_system_value_bool_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_system_value_double_args(object): + """ + Attributes: + - name + - value + + """ + + + def __init__(self, name=None, value=None,): + self.name = name + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.value = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_double_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.DOUBLE, 2) + oprot.writeDouble(self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_double_args) +set_system_value_double_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.DOUBLE, 'value', None, None, ), # 2 +) + + +class set_system_value_double_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_double_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_double_result) +set_system_value_double_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_system_value_str_args(object): + """ + Attributes: + - name + - value + + """ + + + def __init__(self, name=None, value=None,): + self.name = name + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.value = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_str_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.STRING, 2) + oprot.writeString(self.value.encode('utf-8') if sys.version_info[0] == 2 else self.value) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_str_args) +set_system_value_str_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.STRING, 'value', 'UTF8', None, ), # 2 +) + + +class set_system_value_str_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_str_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_str_result) +set_system_value_str_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_system_value_list_args(object): + """ + Attributes: + - name + - value + + """ + + + def __init__(self, name=None, value=None,): + self.name = name + self.value = value + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.value = [] + (_etype1214, _size1211) = iprot.readListBegin() + for _i1215 in range(_size1211): + _elem1216 = iprot.readDouble() + self.value.append(_elem1216) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_list_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.value)) + for iter1217 in self.value: + oprot.writeDouble(iter1217) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_list_args) +set_system_value_list_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'value', (TType.DOUBLE, None, False), None, ), # 2 +) + + +class set_system_value_list_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_system_value_list_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_system_value_list_result) +set_system_value_list_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_system_value_bool_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_bool_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_bool_args) +get_system_value_bool_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_system_value_bool_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_bool_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_bool_result) +get_system_value_bool_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class get_system_value_double_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_double_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_double_args) +get_system_value_double_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_system_value_double_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.DOUBLE: + self.success = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_double_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.DOUBLE, 0) + oprot.writeDouble(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_double_result) +get_system_value_double_result.thrift_spec = ( + (0, TType.DOUBLE, 'success', None, None, ), # 0 +) + + +class get_system_value_str_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_str_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_str_args) +get_system_value_str_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_system_value_str_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRING: + self.success = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_str_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRING, 0) + oprot.writeString(self.success.encode('utf-8') if sys.version_info[0] == 2 else self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_str_result) +get_system_value_str_result.thrift_spec = ( + (0, TType.STRING, 'success', 'UTF8', None, ), # 0 +) + + +class get_system_value_list_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_list_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_list_args) +get_system_value_list_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_system_value_list_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1221, _size1218) = iprot.readListBegin() + for _i1222 in range(_size1218): + _elem1223 = iprot.readDouble() + self.success.append(_elem1223) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_list_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1224 in self.success: + oprot.writeDouble(iter1224) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_list_result) +get_system_value_list_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class trackEnqueue_args(object): + """ + Attributes: + - track + - block + + """ + + + def __init__(self, track=None, block=None,): + self.track = track + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.track = [] + (_etype1228, _size1225) = iprot.readListBegin() + for _i1229 in range(_size1225): + _elem1230 = [] + (_etype1234, _size1231) = iprot.readListBegin() + for _i1235 in range(_size1231): + _elem1236 = iprot.readDouble() + _elem1230.append(_elem1236) + iprot.readListEnd() + self.track.append(_elem1230) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackEnqueue_args') + if self.track is not None: + oprot.writeFieldBegin('track', TType.LIST, 1) + oprot.writeListBegin(TType.LIST, len(self.track)) + for iter1237 in self.track: + oprot.writeListBegin(TType.DOUBLE, len(iter1237)) + for iter1238 in iter1237: + oprot.writeDouble(iter1238) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackEnqueue_args) +trackEnqueue_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'track', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class trackEnqueue_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackEnqueue_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackEnqueue_result) +trackEnqueue_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class trackEnqueueOp_args(object): + """ + Attributes: + - track + - block + + """ + + + def __init__(self, track=None, block=None,): + self.track = track + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.track = [] + (_etype1242, _size1239) = iprot.readListBegin() + for _i1243 in range(_size1239): + _elem1244 = PointOp() + _elem1244.read(iprot) + self.track.append(_elem1244) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackEnqueueOp_args') + if self.track is not None: + oprot.writeFieldBegin('track', TType.LIST, 1) + oprot.writeListBegin(TType.STRUCT, len(self.track)) + for iter1245 in self.track: + iter1245.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackEnqueueOp_args) +trackEnqueueOp_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'track', (TType.STRUCT, [PointOp, None], False), None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class trackEnqueueOp_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackEnqueueOp_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackEnqueueOp_result) +trackEnqueueOp_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class trackClearQueue_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackClearQueue_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackClearQueue_args) +trackClearQueue_args.thrift_spec = ( +) + + +class trackClearQueue_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackClearQueue_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackClearQueue_result) +trackClearQueue_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class getQueueSize_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('getQueueSize_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(getQueueSize_args) +getQueueSize_args.thrift_spec = ( +) + + +class getQueueSize_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('getQueueSize_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(getQueueSize_result) +getQueueSize_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class trackJointMotion_args(object): + """ + Attributes: + - speed + - acc + - block + + """ + + + def __init__(self, speed=None, acc=None, block=None,): + self.speed = speed + self.acc = acc + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.DOUBLE: + self.speed = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.acc = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackJointMotion_args') + if self.speed is not None: + oprot.writeFieldBegin('speed', TType.DOUBLE, 1) + oprot.writeDouble(self.speed) + oprot.writeFieldEnd() + if self.acc is not None: + oprot.writeFieldBegin('acc', TType.DOUBLE, 2) + oprot.writeDouble(self.acc) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackJointMotion_args) +trackJointMotion_args.thrift_spec = ( + None, # 0 + (1, TType.DOUBLE, 'speed', None, None, ), # 1 + (2, TType.DOUBLE, 'acc', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class trackJointMotion_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackJointMotion_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackJointMotion_result) +trackJointMotion_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class trackCartMotion_args(object): + """ + Attributes: + - speed + - acc + - block + - tool + - wobj + - radius + + """ + + + def __init__(self, speed=None, acc=None, block=None, tool=None, wobj=None, radius=None,): + self.speed = speed + self.acc = acc + self.block = block + self.tool = tool + self.wobj = wobj + self.radius = radius + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.DOUBLE: + self.speed = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.acc = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.radius = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackCartMotion_args') + if self.speed is not None: + oprot.writeFieldBegin('speed', TType.DOUBLE, 1) + oprot.writeDouble(self.speed) + oprot.writeFieldEnd() + if self.acc is not None: + oprot.writeFieldBegin('acc', TType.DOUBLE, 2) + oprot.writeDouble(self.acc) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 4) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 5) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.radius is not None: + oprot.writeFieldBegin('radius', TType.DOUBLE, 6) + oprot.writeDouble(self.radius) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackCartMotion_args) +trackCartMotion_args.thrift_spec = ( + None, # 0 + (1, TType.DOUBLE, 'speed', None, None, ), # 1 + (2, TType.DOUBLE, 'acc', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 + (4, TType.STRING, 'tool', 'UTF8', None, ), # 4 + (5, TType.STRING, 'wobj', 'UTF8', None, ), # 5 + (6, TType.DOUBLE, 'radius', None, None, ), # 6 +) + + +class trackCartMotion_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('trackCartMotion_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(trackCartMotion_result) +trackCartMotion_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class rpc_heartbeat_args(object): + """ + Attributes: + - time + + """ + + + def __init__(self, time=None,): + self.time = time + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.time = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('rpc_heartbeat_args') + if self.time is not None: + oprot.writeFieldBegin('time', TType.I32, 1) + oprot.writeI32(self.time) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(rpc_heartbeat_args) +rpc_heartbeat_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'time', None, None, ), # 1 +) + + +class rpc_heartbeat_result(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('rpc_heartbeat_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(rpc_heartbeat_result) +rpc_heartbeat_result.thrift_spec = ( +) + + +class move_spiral_args(object): + """ + Attributes: + - p1 + - p2 + - rev + - len + - r + - mode + - v + - a + - q_near + - tool + - wobj + - block + - op + - def_acc + + """ + + + def __init__(self, p1=None, p2=None, rev=None, len=None, r=None, mode=None, v=None, a=None, q_near=None, tool=None, wobj=None, block=None, op=None, def_acc=None,): + self.p1 = p1 + self.p2 = p2 + self.rev = rev + self.len = len + self.r = r + self.mode = mode + self.v = v + self.a = a + self.q_near = q_near + self.tool = tool + self.wobj = wobj + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype1249, _size1246) = iprot.readListBegin() + for _i1250 in range(_size1246): + _elem1251 = iprot.readDouble() + self.p1.append(_elem1251) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype1255, _size1252) = iprot.readListBegin() + for _i1256 in range(_size1252): + _elem1257 = iprot.readDouble() + self.p2.append(_elem1257) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.rev = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.len = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.r = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.q_near = [] + (_etype1261, _size1258) = iprot.readListBegin() + for _i1262 in range(_size1258): + _elem1263 = iprot.readDouble() + self.q_near.append(_elem1263) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 14: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_spiral_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter1264 in self.p1: + oprot.writeDouble(iter1264) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter1265 in self.p2: + oprot.writeDouble(iter1265) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.rev is not None: + oprot.writeFieldBegin('rev', TType.DOUBLE, 3) + oprot.writeDouble(self.rev) + oprot.writeFieldEnd() + if self.len is not None: + oprot.writeFieldBegin('len', TType.DOUBLE, 4) + oprot.writeDouble(self.len) + oprot.writeFieldEnd() + if self.r is not None: + oprot.writeFieldBegin('r', TType.DOUBLE, 5) + oprot.writeDouble(self.r) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 6) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 7) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 8) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.q_near is not None: + oprot.writeFieldBegin('q_near', TType.LIST, 9) + oprot.writeListBegin(TType.DOUBLE, len(self.q_near)) + for iter1266 in self.q_near: + oprot.writeDouble(iter1266) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 10) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 11) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 12) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 13) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 14) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_spiral_args) +move_spiral_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'rev', None, None, ), # 3 + (4, TType.DOUBLE, 'len', None, None, ), # 4 + (5, TType.DOUBLE, 'r', None, None, ), # 5 + (6, TType.I32, 'mode', None, None, ), # 6 + (7, TType.DOUBLE, 'v', None, None, ), # 7 + (8, TType.DOUBLE, 'a', None, None, ), # 8 + (9, TType.LIST, 'q_near', (TType.DOUBLE, None, False), None, ), # 9 + (10, TType.STRING, 'tool', 'UTF8', None, ), # 10 + (11, TType.STRING, 'wobj', 'UTF8', None, ), # 11 + (12, TType.BOOL, 'block', None, None, ), # 12 + (13, TType.STRUCT, 'op', [Op, None], None, ), # 13 + (14, TType.BOOL, 'def_acc', None, None, ), # 14 +) + + +class move_spiral_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_spiral_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_spiral_result) +move_spiral_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_acc_optimization_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_acc_optimization_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_acc_optimization_args) +enable_acc_optimization_args.thrift_spec = ( +) + + +class enable_acc_optimization_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_acc_optimization_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_acc_optimization_result) +enable_acc_optimization_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_acc_optimization_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_acc_optimization_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_acc_optimization_args) +disable_acc_optimization_args.thrift_spec = ( +) + + +class disable_acc_optimization_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_acc_optimization_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_acc_optimization_result) +disable_acc_optimization_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_baudrate_485_args(object): + """ + Attributes: + - value + - block + + """ + + + def __init__(self, value=None, block=None,): + self.value = value + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_baudrate_485_args') + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 1) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_baudrate_485_args) +set_baudrate_485_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'value', None, None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class set_baudrate_485_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_baudrate_485_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_baudrate_485_result) +set_baudrate_485_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_baudrate_can_args(object): + """ + Attributes: + - value + - block + + """ + + + def __init__(self, value=None, block=None,): + self.value = value + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_baudrate_can_args') + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 1) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_baudrate_can_args) +set_baudrate_can_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'value', None, None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class set_baudrate_can_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_baudrate_can_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_baudrate_can_result) +set_baudrate_can_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_analog_output_mode_args(object): + """ + Attributes: + - num + - mode + - block + + """ + + + def __init__(self, num=None, mode=None, block=None,): + self.num = num + self.mode = mode + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I16: + self.num = iprot.readI16() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_analog_output_mode_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I16, 1) + oprot.writeI16(self.num) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 2) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_analog_output_mode_args) +set_analog_output_mode_args.thrift_spec = ( + None, # 0 + (1, TType.I16, 'num', None, None, ), # 1 + (2, TType.I32, 'mode', None, None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_analog_output_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_analog_output_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_analog_output_mode_result) +set_analog_output_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class robotmoving_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('robotmoving_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(robotmoving_args) +robotmoving_args.thrift_spec = ( +) + + +class robotmoving_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('robotmoving_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(robotmoving_result) +robotmoving_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class modbus_write_multiple_coils_args(object): + """ + Attributes: + - slave_num + - name + - len + - byte_list + + """ + + + def __init__(self, slave_num=None, name=None, len=None, byte_list=None,): + self.slave_num = slave_num + self.name = name + self.len = len + self.byte_list = byte_list + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.slave_num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.len = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.byte_list = [] + (_etype1270, _size1267) = iprot.readListBegin() + for _i1271 in range(_size1267): + _elem1272 = iprot.readByte() + self.byte_list.append(_elem1272) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_write_multiple_coils_args') + if self.slave_num is not None: + oprot.writeFieldBegin('slave_num', TType.I32, 1) + oprot.writeI32(self.slave_num) + oprot.writeFieldEnd() + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 2) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.len is not None: + oprot.writeFieldBegin('len', TType.I32, 3) + oprot.writeI32(self.len) + oprot.writeFieldEnd() + if self.byte_list is not None: + oprot.writeFieldBegin('byte_list', TType.LIST, 4) + oprot.writeListBegin(TType.BYTE, len(self.byte_list)) + for iter1273 in self.byte_list: + oprot.writeByte(iter1273) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_write_multiple_coils_args) +modbus_write_multiple_coils_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'slave_num', None, None, ), # 1 + (2, TType.STRING, 'name', 'UTF8', None, ), # 2 + (3, TType.I32, 'len', None, None, ), # 3 + (4, TType.LIST, 'byte_list', (TType.BYTE, None, False), None, ), # 4 +) + + +class modbus_write_multiple_coils_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_write_multiple_coils_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_write_multiple_coils_result) +modbus_write_multiple_coils_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class modbus_write_multiple_regs_args(object): + """ + Attributes: + - slave_num + - name + - len + - word_list + + """ + + + def __init__(self, slave_num=None, name=None, len=None, word_list=None,): + self.slave_num = slave_num + self.name = name + self.len = len + self.word_list = word_list + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.slave_num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.len = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.word_list = [] + (_etype1277, _size1274) = iprot.readListBegin() + for _i1278 in range(_size1274): + _elem1279 = iprot.readI16() + self.word_list.append(_elem1279) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_write_multiple_regs_args') + if self.slave_num is not None: + oprot.writeFieldBegin('slave_num', TType.I32, 1) + oprot.writeI32(self.slave_num) + oprot.writeFieldEnd() + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 2) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.len is not None: + oprot.writeFieldBegin('len', TType.I32, 3) + oprot.writeI32(self.len) + oprot.writeFieldEnd() + if self.word_list is not None: + oprot.writeFieldBegin('word_list', TType.LIST, 4) + oprot.writeListBegin(TType.I16, len(self.word_list)) + for iter1280 in self.word_list: + oprot.writeI16(iter1280) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_write_multiple_regs_args) +modbus_write_multiple_regs_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'slave_num', None, None, ), # 1 + (2, TType.STRING, 'name', 'UTF8', None, ), # 2 + (3, TType.I32, 'len', None, None, ), # 3 + (4, TType.LIST, 'word_list', (TType.I16, None, False), None, ), # 4 +) + + +class modbus_write_multiple_regs_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('modbus_write_multiple_regs_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(modbus_write_multiple_regs_result) +modbus_write_multiple_regs_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_current_project_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_current_project_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_current_project_args) +get_current_project_args.thrift_spec = ( +) + + +class get_current_project_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRING: + self.success = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_current_project_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRING, 0) + oprot.writeString(self.success.encode('utf-8') if sys.version_info[0] == 2 else self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_current_project_result) +get_current_project_result.thrift_spec = ( + (0, TType.STRING, 'success', 'UTF8', None, ), # 0 +) + + +class get_files_list_args(object): + """ + Attributes: + - path + + """ + + + def __init__(self, path=None,): + self.path = path + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.path = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_files_list_args') + if self.path is not None: + oprot.writeFieldBegin('path', TType.STRING, 1) + oprot.writeString(self.path.encode('utf-8') if sys.version_info[0] == 2 else self.path) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_files_list_args) +get_files_list_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'path', 'UTF8', None, ), # 1 +) + + +class get_files_list_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.MAP: + self.success = {} + (_ktype1282, _vtype1283, _size1281) = iprot.readMapBegin() + for _i1285 in range(_size1281): + _key1286 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + _val1287 = iprot.readI32() + self.success[_key1286] = _val1287 + iprot.readMapEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_files_list_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.MAP, 0) + oprot.writeMapBegin(TType.STRING, TType.I32, len(self.success)) + for kiter1288, viter1289 in self.success.items(): + oprot.writeString(kiter1288.encode('utf-8') if sys.version_info[0] == 2 else kiter1288) + oprot.writeI32(viter1289) + oprot.writeMapEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_files_list_result) +get_files_list_result.thrift_spec = ( + (0, TType.MAP, 'success', (TType.STRING, 'UTF8', TType.I32, None, False), None, ), # 0 +) + + +class getRobotStatus_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('getRobotStatus_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(getRobotStatus_args) +getRobotStatus_args.thrift_spec = ( +) + + +class getRobotStatus_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRUCT: + self.success = RobotStatus() + self.success.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('getRobotStatus_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRUCT, 0) + self.success.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(getRobotStatus_result) +getRobotStatus_result.thrift_spec = ( + (0, TType.STRUCT, 'success', [RobotStatus, None], None, ), # 0 +) + + +class getRobotIOStatus_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('getRobotIOStatus_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(getRobotIOStatus_args) +getRobotIOStatus_args.thrift_spec = ( +) + + +class getRobotIOStatus_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRUCT: + self.success = IOStatus() + self.success.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('getRobotIOStatus_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRUCT, 0) + self.success.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(getRobotIOStatus_result) +getRobotIOStatus_result.thrift_spec = ( + (0, TType.STRUCT, 'success', [IOStatus, None], None, ), # 0 +) + + +class get_tcp_pose_coord_args(object): + """ + Attributes: + - tool + - wobj + + """ + + + def __init__(self, tool=None, wobj=None,): + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_coord_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_coord_args) +get_tcp_pose_coord_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 +) + + +class get_tcp_pose_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1293, _size1290) = iprot.readListBegin() + for _i1294 in range(_size1290): + _elem1295 = iprot.readDouble() + self.success.append(_elem1295) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1296 in self.success: + oprot.writeDouble(iter1296) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_coord_result) +get_tcp_pose_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_force_tool_args(object): + """ + Attributes: + - tool + + """ + + + def __init__(self, tool=None,): + self.tool = tool + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_force_tool_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_force_tool_args) +get_tcp_force_tool_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 +) + + +class get_tcp_force_tool_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1300, _size1297) = iprot.readListBegin() + for _i1301 in range(_size1297): + _elem1302 = iprot.readDouble() + self.success.append(_elem1302) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_force_tool_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1303 in self.success: + oprot.writeDouble(iter1303) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_force_tool_result) +get_tcp_force_tool_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class restart_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('restart_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(restart_args) +restart_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class restart_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('restart_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(restart_result) +restart_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_servo_config_args(object): + """ + Attributes: + - axis_num + - id + - value + - qfmt + - block + + """ + + + def __init__(self, axis_num=None, id=None, value=None, qfmt=None, block=None,): + self.axis_num = axis_num + self.id = id + self.value = value + self.qfmt = qfmt + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.axis_num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.id = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.value = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.qfmt = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_servo_config_args') + if self.axis_num is not None: + oprot.writeFieldBegin('axis_num', TType.I32, 1) + oprot.writeI32(self.axis_num) + oprot.writeFieldEnd() + if self.id is not None: + oprot.writeFieldBegin('id', TType.I32, 2) + oprot.writeI32(self.id) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.I32, 3) + oprot.writeI32(self.value) + oprot.writeFieldEnd() + if self.qfmt is not None: + oprot.writeFieldBegin('qfmt', TType.I32, 4) + oprot.writeI32(self.qfmt) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 5) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_servo_config_args) +set_servo_config_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'axis_num', None, None, ), # 1 + (2, TType.I32, 'id', None, None, ), # 2 + (3, TType.I32, 'value', None, None, ), # 3 + (4, TType.I32, 'qfmt', None, None, ), # 4 + (5, TType.BOOL, 'block', None, None, ), # 5 +) + + +class set_servo_config_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_servo_config_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_servo_config_result) +set_servo_config_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class apply_servo_config_args(object): + """ + Attributes: + - axis_num + - block + + """ + + + def __init__(self, axis_num=None, block=None,): + self.axis_num = axis_num + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.axis_num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('apply_servo_config_args') + if self.axis_num is not None: + oprot.writeFieldBegin('axis_num', TType.I32, 1) + oprot.writeI32(self.axis_num) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(apply_servo_config_args) +apply_servo_config_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'axis_num', None, None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class apply_servo_config_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('apply_servo_config_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(apply_servo_config_result) +apply_servo_config_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_motor_pole_pair_number_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_motor_pole_pair_number_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_motor_pole_pair_number_args) +get_motor_pole_pair_number_args.thrift_spec = ( +) + + +class get_motor_pole_pair_number_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1307, _size1304) = iprot.readListBegin() + for _i1308 in range(_size1304): + _elem1309 = iprot.readI16() + self.success.append(_elem1309) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_motor_pole_pair_number_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.I16, len(self.success)) + for iter1310 in self.success: + oprot.writeI16(iter1310) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_motor_pole_pair_number_result) +get_motor_pole_pair_number_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.I16, None, False), None, ), # 0 +) + + +class get_motor_stator_slots_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_motor_stator_slots_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_motor_stator_slots_args) +get_motor_stator_slots_args.thrift_spec = ( +) + + +class get_motor_stator_slots_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1314, _size1311) = iprot.readListBegin() + for _i1315 in range(_size1311): + _elem1316 = iprot.readI16() + self.success.append(_elem1316) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_motor_stator_slots_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.I16, len(self.success)) + for iter1317 in self.success: + oprot.writeI16(iter1317) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_motor_stator_slots_result) +get_motor_stator_slots_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.I16, None, False), None, ), # 0 +) + + +class get_axis_ratio_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_axis_ratio_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_axis_ratio_args) +get_axis_ratio_args.thrift_spec = ( +) + + +class get_axis_ratio_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1321, _size1318) = iprot.readListBegin() + for _i1322 in range(_size1318): + _elem1323 = iprot.readI16() + self.success.append(_elem1323) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_axis_ratio_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.I16, len(self.success)) + for iter1324 in self.success: + oprot.writeI16(iter1324) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_axis_ratio_result) +get_axis_ratio_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.I16, None, False), None, ), # 0 +) + + +class collision_detection_reset_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('collision_detection_reset_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(collision_detection_reset_args) +collision_detection_reset_args.thrift_spec = ( +) + + +class collision_detection_reset_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('collision_detection_reset_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(collision_detection_reset_result) +collision_detection_reset_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_servo_file_params_args(object): + """ + Attributes: + - axis_num + - id + - name + - value + - qfmt + + """ + + + def __init__(self, axis_num=None, id=None, name=None, value=None, qfmt=None,): + self.axis_num = axis_num + self.id = id + self.name = name + self.value = value + self.qfmt = qfmt + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.axis_num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.id = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.value = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.qfmt = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_servo_file_params_args') + if self.axis_num is not None: + oprot.writeFieldBegin('axis_num', TType.I32, 1) + oprot.writeI32(self.axis_num) + oprot.writeFieldEnd() + if self.id is not None: + oprot.writeFieldBegin('id', TType.I32, 2) + oprot.writeI32(self.id) + oprot.writeFieldEnd() + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 3) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.value is not None: + oprot.writeFieldBegin('value', TType.DOUBLE, 4) + oprot.writeDouble(self.value) + oprot.writeFieldEnd() + if self.qfmt is not None: + oprot.writeFieldBegin('qfmt', TType.DOUBLE, 5) + oprot.writeDouble(self.qfmt) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_servo_file_params_args) +set_servo_file_params_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'axis_num', None, None, ), # 1 + (2, TType.I32, 'id', None, None, ), # 2 + (3, TType.STRING, 'name', 'UTF8', None, ), # 3 + (4, TType.DOUBLE, 'value', None, None, ), # 4 + (5, TType.DOUBLE, 'qfmt', None, None, ), # 5 +) + + +class set_servo_file_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_servo_file_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_servo_file_params_result) +set_servo_file_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class combine_motion_config_args(object): + """ + Attributes: + - type + - ref_plane + - fq + - amp + - el_offset + - az_offset + - up_height + - time + - path_dwell + - pendulum_height + - op_list + + """ + + + def __init__(self, type=None, ref_plane=None, fq=None, amp=None, el_offset=None, az_offset=None, up_height=None, time=None, path_dwell=None, pendulum_height=None, op_list=None,): + self.type = type + self.ref_plane = ref_plane + self.fq = fq + self.amp = amp + self.el_offset = el_offset + self.az_offset = az_offset + self.up_height = up_height + self.time = time + self.path_dwell = path_dwell + self.pendulum_height = pendulum_height + self.op_list = op_list + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.ref_plane = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.fq = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.amp = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.el_offset = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.az_offset = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.up_height = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.LIST: + self.time = [] + (_etype1328, _size1325) = iprot.readListBegin() + for _i1329 in range(_size1325): + _elem1330 = iprot.readI32() + self.time.append(_elem1330) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.BOOL: + self.path_dwell = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.DOUBLE: + self.pendulum_height = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.LIST: + self.op_list = [] + (_etype1334, _size1331) = iprot.readListBegin() + for _i1335 in range(_size1331): + _elem1336 = Op() + _elem1336.read(iprot) + self.op_list.append(_elem1336) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('combine_motion_config_args') + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 1) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + if self.ref_plane is not None: + oprot.writeFieldBegin('ref_plane', TType.I32, 2) + oprot.writeI32(self.ref_plane) + oprot.writeFieldEnd() + if self.fq is not None: + oprot.writeFieldBegin('fq', TType.DOUBLE, 3) + oprot.writeDouble(self.fq) + oprot.writeFieldEnd() + if self.amp is not None: + oprot.writeFieldBegin('amp', TType.DOUBLE, 4) + oprot.writeDouble(self.amp) + oprot.writeFieldEnd() + if self.el_offset is not None: + oprot.writeFieldBegin('el_offset', TType.DOUBLE, 5) + oprot.writeDouble(self.el_offset) + oprot.writeFieldEnd() + if self.az_offset is not None: + oprot.writeFieldBegin('az_offset', TType.DOUBLE, 6) + oprot.writeDouble(self.az_offset) + oprot.writeFieldEnd() + if self.up_height is not None: + oprot.writeFieldBegin('up_height', TType.DOUBLE, 7) + oprot.writeDouble(self.up_height) + oprot.writeFieldEnd() + if self.time is not None: + oprot.writeFieldBegin('time', TType.LIST, 8) + oprot.writeListBegin(TType.I32, len(self.time)) + for iter1337 in self.time: + oprot.writeI32(iter1337) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.path_dwell is not None: + oprot.writeFieldBegin('path_dwell', TType.BOOL, 9) + oprot.writeBool(self.path_dwell) + oprot.writeFieldEnd() + if self.pendulum_height is not None: + oprot.writeFieldBegin('pendulum_height', TType.DOUBLE, 10) + oprot.writeDouble(self.pendulum_height) + oprot.writeFieldEnd() + if self.op_list is not None: + oprot.writeFieldBegin('op_list', TType.LIST, 11) + oprot.writeListBegin(TType.STRUCT, len(self.op_list)) + for iter1338 in self.op_list: + iter1338.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(combine_motion_config_args) +combine_motion_config_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'type', None, None, ), # 1 + (2, TType.I32, 'ref_plane', None, None, ), # 2 + (3, TType.DOUBLE, 'fq', None, None, ), # 3 + (4, TType.DOUBLE, 'amp', None, None, ), # 4 + (5, TType.DOUBLE, 'el_offset', None, None, ), # 5 + (6, TType.DOUBLE, 'az_offset', None, None, ), # 6 + (7, TType.DOUBLE, 'up_height', None, None, ), # 7 + (8, TType.LIST, 'time', (TType.I32, None, False), None, ), # 8 + (9, TType.BOOL, 'path_dwell', None, None, ), # 9 + (10, TType.DOUBLE, 'pendulum_height', None, None, ), # 10 + (11, TType.LIST, 'op_list', (TType.STRUCT, [Op, None], False), None, ), # 11 +) + + +class combine_motion_config_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('combine_motion_config_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(combine_motion_config_result) +combine_motion_config_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_eaxis_param_args(object): + """ + Attributes: + - num + - param + - block + + """ + + + def __init__(self, num=None, param=None, block=None,): + self.num = num + self.param = param + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRUCT: + self.param = EAxisParam() + self.param.read(iprot) + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_eaxis_param_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I32, 1) + oprot.writeI32(self.num) + oprot.writeFieldEnd() + if self.param is not None: + oprot.writeFieldBegin('param', TType.STRUCT, 2) + self.param.write(oprot) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_eaxis_param_args) +set_eaxis_param_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'num', None, None, ), # 1 + (2, TType.STRUCT, 'param', [EAxisParam, None], None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_eaxis_param_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_eaxis_param_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_eaxis_param_result) +set_eaxis_param_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class add_eaxis_scheme_args(object): + """ + Attributes: + - num + - block + + """ + + + def __init__(self, num=None, block=None,): + self.num = num + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('add_eaxis_scheme_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I32, 1) + oprot.writeI32(self.num) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(add_eaxis_scheme_args) +add_eaxis_scheme_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'num', None, None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class add_eaxis_scheme_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('add_eaxis_scheme_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(add_eaxis_scheme_result) +add_eaxis_scheme_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class delete_eaxis_scheme_args(object): + """ + Attributes: + - num + - block + + """ + + + def __init__(self, num=None, block=None,): + self.num = num + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('delete_eaxis_scheme_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I32, 1) + oprot.writeI32(self.num) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(delete_eaxis_scheme_args) +delete_eaxis_scheme_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'num', None, None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class delete_eaxis_scheme_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('delete_eaxis_scheme_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(delete_eaxis_scheme_result) +delete_eaxis_scheme_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_eaxis_scheme_args(object): + """ + Attributes: + - scheme_name + + """ + + + def __init__(self, scheme_name=None,): + self.scheme_name = scheme_name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.scheme_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_eaxis_scheme_args') + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.STRING, 1) + oprot.writeString(self.scheme_name.encode('utf-8') if sys.version_info[0] == 2 else self.scheme_name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_eaxis_scheme_args) +enable_eaxis_scheme_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'scheme_name', 'UTF8', None, ), # 1 +) + + +class enable_eaxis_scheme_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_eaxis_scheme_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_eaxis_scheme_result) +enable_eaxis_scheme_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_eaxis_scheme_args(object): + """ + Attributes: + - scheme_name + + """ + + + def __init__(self, scheme_name=None,): + self.scheme_name = scheme_name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.scheme_name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_eaxis_scheme_args') + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.STRING, 1) + oprot.writeString(self.scheme_name.encode('utf-8') if sys.version_info[0] == 2 else self.scheme_name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_eaxis_scheme_args) +disable_eaxis_scheme_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'scheme_name', 'UTF8', None, ), # 1 +) + + +class disable_eaxis_scheme_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_eaxis_scheme_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_eaxis_scheme_result) +disable_eaxis_scheme_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_eaxiss_scheme_param_args(object): + """ + Attributes: + - num + - param + - block + + """ + + + def __init__(self, num=None, param=None, block=None,): + self.num = num + self.param = param + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRUCT: + self.param = EAxisSchemeParam() + self.param.read(iprot) + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_eaxiss_scheme_param_args') + if self.num is not None: + oprot.writeFieldBegin('num', TType.I32, 1) + oprot.writeI32(self.num) + oprot.writeFieldEnd() + if self.param is not None: + oprot.writeFieldBegin('param', TType.STRUCT, 2) + self.param.write(oprot) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 3) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_eaxiss_scheme_param_args) +set_eaxiss_scheme_param_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'num', None, None, ), # 1 + (2, TType.STRUCT, 'param', [EAxisSchemeParam, None], None, ), # 2 + (3, TType.BOOL, 'block', None, None, ), # 3 +) + + +class set_eaxiss_scheme_param_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_eaxiss_scheme_param_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_eaxiss_scheme_param_result) +set_eaxiss_scheme_param_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class move_jog_args(object): + """ + Attributes: + - param + - block + + """ + + + def __init__(self, param=None, block=None,): + self.param = param + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRUCT: + self.param = MoveJogTaskParam() + self.param.read(iprot) + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_jog_args') + if self.param is not None: + oprot.writeFieldBegin('param', TType.STRUCT, 1) + self.param.write(oprot) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_jog_args) +move_jog_args.thrift_spec = ( + None, # 0 + (1, TType.STRUCT, 'param', [MoveJogTaskParam, None], None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class move_jog_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_jog_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_jog_result) +move_jog_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class stop_manual_move_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('stop_manual_move_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(stop_manual_move_args) +stop_manual_move_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class stop_manual_move_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('stop_manual_move_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(stop_manual_move_result) +stop_manual_move_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_robot_version_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_robot_version_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_robot_version_args) +get_robot_version_args.thrift_spec = ( +) + + +class get_robot_version_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRING: + self.success = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_robot_version_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRING, 0) + oprot.writeString(self.success.encode('utf-8') if sys.version_info[0] == 2 else self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_robot_version_result) +get_robot_version_result.thrift_spec = ( + (0, TType.STRING, 'success', 'UTF8', None, ), # 0 +) + + +class set_teach_pendant_args(object): + """ + Attributes: + - enable + + """ + + + def __init__(self, enable=None,): + self.enable = enable + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.enable = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_teach_pendant_args') + if self.enable is not None: + oprot.writeFieldBegin('enable', TType.BOOL, 1) + oprot.writeBool(self.enable) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_teach_pendant_args) +set_teach_pendant_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'enable', None, None, ), # 1 +) + + +class set_teach_pendant_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_teach_pendant_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_teach_pendant_result) +set_teach_pendant_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_teach_speed_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_teach_speed_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_teach_speed_args) +get_teach_speed_args.thrift_spec = ( +) + + +class get_teach_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_teach_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_teach_speed_result) +get_teach_speed_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_global_speed_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_global_speed_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_global_speed_args) +get_global_speed_args.thrift_spec = ( +) + + +class get_global_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_global_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_global_speed_result) +get_global_speed_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_teach_speed_args(object): + """ + Attributes: + - v + + """ + + + def __init__(self, v=None,): + self.v = v + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.v = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_teach_speed_args') + if self.v is not None: + oprot.writeFieldBegin('v', TType.I32, 1) + oprot.writeI32(self.v) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_teach_speed_args) +set_teach_speed_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'v', None, None, ), # 1 +) + + +class set_teach_speed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_teach_speed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_teach_speed_result) +set_teach_speed_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_combine_motion_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_combine_motion_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_combine_motion_args) +enable_combine_motion_args.thrift_spec = ( +) + + +class enable_combine_motion_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_combine_motion_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_combine_motion_result) +enable_combine_motion_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_combine_motion_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_combine_motion_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_combine_motion_args) +disable_combine_motion_args.thrift_spec = ( +) + + +class disable_combine_motion_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_combine_motion_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_combine_motion_result) +disable_combine_motion_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_singularity_control_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_singularity_control_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_singularity_control_args) +enable_singularity_control_args.thrift_spec = ( +) + + +class enable_singularity_control_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_singularity_control_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_singularity_control_result) +enable_singularity_control_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_singularity_control_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_singularity_control_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_singularity_control_args) +disable_singularity_control_args.thrift_spec = ( +) + + +class disable_singularity_control_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_singularity_control_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_singularity_control_result) +disable_singularity_control_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_vibration_control_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_vibration_control_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_vibration_control_args) +enable_vibration_control_args.thrift_spec = ( +) + + +class enable_vibration_control_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_vibration_control_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_vibration_control_result) +enable_vibration_control_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_vibration_control_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_vibration_control_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_vibration_control_args) +disable_vibration_control_args.thrift_spec = ( +) + + +class disable_vibration_control_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_vibration_control_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_vibration_control_result) +disable_vibration_control_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class move_eaxis_args(object): + """ + Attributes: + - scheme_name + - epose + - v + - block + - op + + """ + + + def __init__(self, scheme_name=None, epose=None, v=None, block=None, op=None,): + self.scheme_name = scheme_name + self.epose = epose + self.v = v + self.block = block + self.op = op + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1342, _size1339) = iprot.readListBegin() + for _i1343 in range(_size1339): + _elem1344 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1344) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.epose = [] + (_etype1348, _size1345) = iprot.readListBegin() + for _i1349 in range(_size1345): + _elem1350 = [] + (_etype1354, _size1351) = iprot.readListBegin() + for _i1355 in range(_size1351): + _elem1356 = iprot.readDouble() + _elem1350.append(_elem1356) + iprot.readListEnd() + self.epose.append(_elem1350) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.v = [] + (_etype1360, _size1357) = iprot.readListBegin() + for _i1361 in range(_size1357): + _elem1362 = iprot.readDouble() + self.v.append(_elem1362) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_eaxis_args') + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 1) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1363 in self.scheme_name: + oprot.writeString(iter1363.encode('utf-8') if sys.version_info[0] == 2 else iter1363) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 2) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1364 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1364)) + for iter1365 in iter1364: + oprot.writeDouble(iter1365) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.v)) + for iter1366 in self.v: + oprot.writeDouble(iter1366) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 4) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 5) + self.op.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_eaxis_args) +move_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 1 + (2, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 2 + (3, TType.LIST, 'v', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.BOOL, 'block', None, None, ), # 4 + (5, TType.STRUCT, 'op', [Op, None], None, ), # 5 +) + + +class move_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_eaxis_result) +move_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movej2_eaxis_args(object): + """ + Attributes: + - joints_list + - v + - a + - rad + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + + + def __init__(self, joints_list=None, v=None, a=None, rad=None, scheme_name=None, epose=None, eaxis_v=None, block=None, op=None, def_acc=None,): + self.joints_list = joints_list + self.v = v + self.a = a + self.rad = rad + self.scheme_name = scheme_name + self.epose = epose + self.eaxis_v = eaxis_v + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joints_list = [] + (_etype1370, _size1367) = iprot.readListBegin() + for _i1371 in range(_size1367): + _elem1372 = iprot.readDouble() + self.joints_list.append(_elem1372) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.rad = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1376, _size1373) = iprot.readListBegin() + for _i1377 in range(_size1373): + _elem1378 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1378) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.epose = [] + (_etype1382, _size1379) = iprot.readListBegin() + for _i1383 in range(_size1379): + _elem1384 = [] + (_etype1388, _size1385) = iprot.readListBegin() + for _i1389 in range(_size1385): + _elem1390 = iprot.readDouble() + _elem1384.append(_elem1390) + iprot.readListEnd() + self.epose.append(_elem1384) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.eaxis_v = [] + (_etype1394, _size1391) = iprot.readListBegin() + for _i1395 in range(_size1391): + _elem1396 = iprot.readDouble() + self.eaxis_v.append(_elem1396) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej2_eaxis_args') + if self.joints_list is not None: + oprot.writeFieldBegin('joints_list', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joints_list)) + for iter1397 in self.joints_list: + oprot.writeDouble(iter1397) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.rad is not None: + oprot.writeFieldBegin('rad', TType.DOUBLE, 4) + oprot.writeDouble(self.rad) + oprot.writeFieldEnd() + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 5) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1398 in self.scheme_name: + oprot.writeString(iter1398.encode('utf-8') if sys.version_info[0] == 2 else iter1398) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 6) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1399 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1399)) + for iter1400 in iter1399: + oprot.writeDouble(iter1400) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_v is not None: + oprot.writeFieldBegin('eaxis_v', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_v)) + for iter1401 in self.eaxis_v: + oprot.writeDouble(iter1401) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej2_eaxis_args) +movej2_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joints_list', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'rad', None, None, ), # 4 + (5, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 5 + (6, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 6 + (7, TType.LIST, 'eaxis_v', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class movej2_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej2_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej2_eaxis_result) +movej2_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movej2_pose_eaxis_args(object): + """ + Attributes: + - p + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + + + def __init__(self, p=None, v=None, a=None, rad=None, qnear=None, tool=None, wobj=None, scheme_name=None, epose=None, eaxis_v=None, block=None, op=None, def_acc=None,): + self.p = p + self.v = v + self.a = a + self.rad = rad + self.qnear = qnear + self.tool = tool + self.wobj = wobj + self.scheme_name = scheme_name + self.epose = epose + self.eaxis_v = eaxis_v + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype1405, _size1402) = iprot.readListBegin() + for _i1406 in range(_size1402): + _elem1407 = iprot.readDouble() + self.p.append(_elem1407) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.rad = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.qnear = [] + (_etype1411, _size1408) = iprot.readListBegin() + for _i1412 in range(_size1408): + _elem1413 = iprot.readDouble() + self.qnear.append(_elem1413) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1417, _size1414) = iprot.readListBegin() + for _i1418 in range(_size1414): + _elem1419 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1419) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.epose = [] + (_etype1423, _size1420) = iprot.readListBegin() + for _i1424 in range(_size1420): + _elem1425 = [] + (_etype1429, _size1426) = iprot.readListBegin() + for _i1430 in range(_size1426): + _elem1431 = iprot.readDouble() + _elem1425.append(_elem1431) + iprot.readListEnd() + self.epose.append(_elem1425) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.LIST: + self.eaxis_v = [] + (_etype1435, _size1432) = iprot.readListBegin() + for _i1436 in range(_size1432): + _elem1437 = iprot.readDouble() + self.eaxis_v.append(_elem1437) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej2_pose_eaxis_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter1438 in self.p: + oprot.writeDouble(iter1438) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.rad is not None: + oprot.writeFieldBegin('rad', TType.DOUBLE, 4) + oprot.writeDouble(self.rad) + oprot.writeFieldEnd() + if self.qnear is not None: + oprot.writeFieldBegin('qnear', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.qnear)) + for iter1439 in self.qnear: + oprot.writeDouble(iter1439) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 8) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1440 in self.scheme_name: + oprot.writeString(iter1440.encode('utf-8') if sys.version_info[0] == 2 else iter1440) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 9) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1441 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1441)) + for iter1442 in iter1441: + oprot.writeDouble(iter1442) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_v is not None: + oprot.writeFieldBegin('eaxis_v', TType.LIST, 10) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_v)) + for iter1443 in self.eaxis_v: + oprot.writeDouble(iter1443) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 11) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 12) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 13) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej2_pose_eaxis_args) +movej2_pose_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'rad', None, None, ), # 4 + (5, TType.LIST, 'qnear', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 8 + (9, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 9 + (10, TType.LIST, 'eaxis_v', (TType.DOUBLE, None, False), None, ), # 10 + (11, TType.BOOL, 'block', None, None, ), # 11 + (12, TType.STRUCT, 'op', [Op, None], None, ), # 12 + (13, TType.BOOL, 'def_acc', None, None, ), # 13 +) + + +class movej2_pose_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movej2_pose_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movej2_pose_eaxis_result) +movej2_pose_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movel_eaxis_args(object): + """ + Attributes: + - p + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + + + def __init__(self, p=None, v=None, a=None, rad=None, qnear=None, tool=None, wobj=None, scheme_name=None, epose=None, eaxis_v=None, block=None, op=None, def_acc=None,): + self.p = p + self.v = v + self.a = a + self.rad = rad + self.qnear = qnear + self.tool = tool + self.wobj = wobj + self.scheme_name = scheme_name + self.epose = epose + self.eaxis_v = eaxis_v + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype1447, _size1444) = iprot.readListBegin() + for _i1448 in range(_size1444): + _elem1449 = iprot.readDouble() + self.p.append(_elem1449) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.rad = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.qnear = [] + (_etype1453, _size1450) = iprot.readListBegin() + for _i1454 in range(_size1450): + _elem1455 = iprot.readDouble() + self.qnear.append(_elem1455) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1459, _size1456) = iprot.readListBegin() + for _i1460 in range(_size1456): + _elem1461 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1461) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.epose = [] + (_etype1465, _size1462) = iprot.readListBegin() + for _i1466 in range(_size1462): + _elem1467 = [] + (_etype1471, _size1468) = iprot.readListBegin() + for _i1472 in range(_size1468): + _elem1473 = iprot.readDouble() + _elem1467.append(_elem1473) + iprot.readListEnd() + self.epose.append(_elem1467) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.LIST: + self.eaxis_v = [] + (_etype1477, _size1474) = iprot.readListBegin() + for _i1478 in range(_size1474): + _elem1479 = iprot.readDouble() + self.eaxis_v.append(_elem1479) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movel_eaxis_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter1480 in self.p: + oprot.writeDouble(iter1480) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 2) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 3) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.rad is not None: + oprot.writeFieldBegin('rad', TType.DOUBLE, 4) + oprot.writeDouble(self.rad) + oprot.writeFieldEnd() + if self.qnear is not None: + oprot.writeFieldBegin('qnear', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.qnear)) + for iter1481 in self.qnear: + oprot.writeDouble(iter1481) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 6) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 7) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 8) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1482 in self.scheme_name: + oprot.writeString(iter1482.encode('utf-8') if sys.version_info[0] == 2 else iter1482) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 9) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1483 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1483)) + for iter1484 in iter1483: + oprot.writeDouble(iter1484) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_v is not None: + oprot.writeFieldBegin('eaxis_v', TType.LIST, 10) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_v)) + for iter1485 in self.eaxis_v: + oprot.writeDouble(iter1485) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 11) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 12) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 13) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movel_eaxis_args) +movel_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.DOUBLE, 'v', None, None, ), # 2 + (3, TType.DOUBLE, 'a', None, None, ), # 3 + (4, TType.DOUBLE, 'rad', None, None, ), # 4 + (5, TType.LIST, 'qnear', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.STRING, 'tool', 'UTF8', None, ), # 6 + (7, TType.STRING, 'wobj', 'UTF8', None, ), # 7 + (8, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 8 + (9, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 9 + (10, TType.LIST, 'eaxis_v', (TType.DOUBLE, None, False), None, ), # 10 + (11, TType.BOOL, 'block', None, None, ), # 11 + (12, TType.STRUCT, 'op', [Op, None], None, ), # 12 + (13, TType.BOOL, 'def_acc', None, None, ), # 13 +) + + +class movel_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movel_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movel_eaxis_result) +movel_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class movec_eaxis_args(object): + """ + Attributes: + - p1 + - p2 + - v + - a + - rad + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + - mode + - arc_rad + + """ + + + def __init__(self, p1=None, p2=None, v=None, a=None, rad=None, qnear=None, tool=None, wobj=None, scheme_name=None, epose=None, eaxis_v=None, block=None, op=None, def_acc=None, mode=None, arc_rad=None,): + self.p1 = p1 + self.p2 = p2 + self.v = v + self.a = a + self.rad = rad + self.qnear = qnear + self.tool = tool + self.wobj = wobj + self.scheme_name = scheme_name + self.epose = epose + self.eaxis_v = eaxis_v + self.block = block + self.op = op + self.def_acc = def_acc + self.mode = mode + self.arc_rad = arc_rad + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype1489, _size1486) = iprot.readListBegin() + for _i1490 in range(_size1486): + _elem1491 = iprot.readDouble() + self.p1.append(_elem1491) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype1495, _size1492) = iprot.readListBegin() + for _i1496 in range(_size1492): + _elem1497 = iprot.readDouble() + self.p2.append(_elem1497) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.rad = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.qnear = [] + (_etype1501, _size1498) = iprot.readListBegin() + for _i1502 in range(_size1498): + _elem1503 = iprot.readDouble() + self.qnear.append(_elem1503) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1507, _size1504) = iprot.readListBegin() + for _i1508 in range(_size1504): + _elem1509 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1509) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.LIST: + self.epose = [] + (_etype1513, _size1510) = iprot.readListBegin() + for _i1514 in range(_size1510): + _elem1515 = [] + (_etype1519, _size1516) = iprot.readListBegin() + for _i1520 in range(_size1516): + _elem1521 = iprot.readDouble() + _elem1515.append(_elem1521) + iprot.readListEnd() + self.epose.append(_elem1515) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.LIST: + self.eaxis_v = [] + (_etype1525, _size1522) = iprot.readListBegin() + for _i1526 in range(_size1522): + _elem1527 = iprot.readDouble() + self.eaxis_v.append(_elem1527) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 14: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 15: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 16: + if ftype == TType.DOUBLE: + self.arc_rad = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movec_eaxis_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter1528 in self.p1: + oprot.writeDouble(iter1528) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter1529 in self.p2: + oprot.writeDouble(iter1529) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 3) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 4) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.rad is not None: + oprot.writeFieldBegin('rad', TType.DOUBLE, 5) + oprot.writeDouble(self.rad) + oprot.writeFieldEnd() + if self.qnear is not None: + oprot.writeFieldBegin('qnear', TType.LIST, 6) + oprot.writeListBegin(TType.DOUBLE, len(self.qnear)) + for iter1530 in self.qnear: + oprot.writeDouble(iter1530) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 7) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 8) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 9) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1531 in self.scheme_name: + oprot.writeString(iter1531.encode('utf-8') if sys.version_info[0] == 2 else iter1531) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 10) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1532 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1532)) + for iter1533 in iter1532: + oprot.writeDouble(iter1533) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_v is not None: + oprot.writeFieldBegin('eaxis_v', TType.LIST, 11) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_v)) + for iter1534 in self.eaxis_v: + oprot.writeDouble(iter1534) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 12) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 13) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 14) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 15) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.arc_rad is not None: + oprot.writeFieldBegin('arc_rad', TType.DOUBLE, 16) + oprot.writeDouble(self.arc_rad) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movec_eaxis_args) +movec_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'v', None, None, ), # 3 + (4, TType.DOUBLE, 'a', None, None, ), # 4 + (5, TType.DOUBLE, 'rad', None, None, ), # 5 + (6, TType.LIST, 'qnear', (TType.DOUBLE, None, False), None, ), # 6 + (7, TType.STRING, 'tool', 'UTF8', None, ), # 7 + (8, TType.STRING, 'wobj', 'UTF8', None, ), # 8 + (9, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 9 + (10, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 10 + (11, TType.LIST, 'eaxis_v', (TType.DOUBLE, None, False), None, ), # 11 + (12, TType.BOOL, 'block', None, None, ), # 12 + (13, TType.STRUCT, 'op', [Op, None], None, ), # 13 + (14, TType.BOOL, 'def_acc', None, None, ), # 14 + (15, TType.I32, 'mode', None, None, ), # 15 + (16, TType.DOUBLE, 'arc_rad', None, None, ), # 16 +) + + +class movec_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('movec_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(movec_eaxis_result) +movec_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class move_circle_eaxis_args(object): + """ + Attributes: + - p1 + - p2 + - v + - a + - rad + - mode + - qnear + - tool + - wobj + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + + + def __init__(self, p1=None, p2=None, v=None, a=None, rad=None, mode=None, qnear=None, tool=None, wobj=None, scheme_name=None, epose=None, eaxis_v=None, block=None, op=None, def_acc=None,): + self.p1 = p1 + self.p2 = p2 + self.v = v + self.a = a + self.rad = rad + self.mode = mode + self.qnear = qnear + self.tool = tool + self.wobj = wobj + self.scheme_name = scheme_name + self.epose = epose + self.eaxis_v = eaxis_v + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p1 = [] + (_etype1538, _size1535) = iprot.readListBegin() + for _i1539 in range(_size1535): + _elem1540 = iprot.readDouble() + self.p1.append(_elem1540) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.p2 = [] + (_etype1544, _size1541) = iprot.readListBegin() + for _i1545 in range(_size1541): + _elem1546 = iprot.readDouble() + self.p2.append(_elem1546) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.v = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.a = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.rad = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.qnear = [] + (_etype1550, _size1547) = iprot.readListBegin() + for _i1551 in range(_size1547): + _elem1552 = iprot.readDouble() + self.qnear.append(_elem1552) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1556, _size1553) = iprot.readListBegin() + for _i1557 in range(_size1553): + _elem1558 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1558) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.LIST: + self.epose = [] + (_etype1562, _size1559) = iprot.readListBegin() + for _i1563 in range(_size1559): + _elem1564 = [] + (_etype1568, _size1565) = iprot.readListBegin() + for _i1569 in range(_size1565): + _elem1570 = iprot.readDouble() + _elem1564.append(_elem1570) + iprot.readListEnd() + self.epose.append(_elem1564) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.LIST: + self.eaxis_v = [] + (_etype1574, _size1571) = iprot.readListBegin() + for _i1575 in range(_size1571): + _elem1576 = iprot.readDouble() + self.eaxis_v.append(_elem1576) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 14: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 15: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_circle_eaxis_args') + if self.p1 is not None: + oprot.writeFieldBegin('p1', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p1)) + for iter1577 in self.p1: + oprot.writeDouble(iter1577) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.p2 is not None: + oprot.writeFieldBegin('p2', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.p2)) + for iter1578 in self.p2: + oprot.writeDouble(iter1578) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.v is not None: + oprot.writeFieldBegin('v', TType.DOUBLE, 3) + oprot.writeDouble(self.v) + oprot.writeFieldEnd() + if self.a is not None: + oprot.writeFieldBegin('a', TType.DOUBLE, 4) + oprot.writeDouble(self.a) + oprot.writeFieldEnd() + if self.rad is not None: + oprot.writeFieldBegin('rad', TType.DOUBLE, 5) + oprot.writeDouble(self.rad) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 6) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.qnear is not None: + oprot.writeFieldBegin('qnear', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.qnear)) + for iter1579 in self.qnear: + oprot.writeDouble(iter1579) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 8) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 9) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 10) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1580 in self.scheme_name: + oprot.writeString(iter1580.encode('utf-8') if sys.version_info[0] == 2 else iter1580) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 11) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1581 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1581)) + for iter1582 in iter1581: + oprot.writeDouble(iter1582) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_v is not None: + oprot.writeFieldBegin('eaxis_v', TType.LIST, 12) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_v)) + for iter1583 in self.eaxis_v: + oprot.writeDouble(iter1583) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 13) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 14) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 15) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_circle_eaxis_args) +move_circle_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p1', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'p2', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.DOUBLE, 'v', None, None, ), # 3 + (4, TType.DOUBLE, 'a', None, None, ), # 4 + (5, TType.DOUBLE, 'rad', None, None, ), # 5 + (6, TType.I32, 'mode', None, None, ), # 6 + (7, TType.LIST, 'qnear', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.STRING, 'tool', 'UTF8', None, ), # 8 + (9, TType.STRING, 'wobj', 'UTF8', None, ), # 9 + (10, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 10 + (11, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 11 + (12, TType.LIST, 'eaxis_v', (TType.DOUBLE, None, False), None, ), # 12 + (13, TType.BOOL, 'block', None, None, ), # 13 + (14, TType.STRUCT, 'op', [Op, None], None, ), # 14 + (15, TType.BOOL, 'def_acc', None, None, ), # 15 +) + + +class move_circle_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_circle_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_circle_eaxis_result) +move_circle_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class reach_check_args(object): + """ + Attributes: + - base + - wobj + - tool + - ref_pos + - check_points + + """ + + + def __init__(self, base=None, wobj=None, tool=None, ref_pos=None, check_points=None,): + self.base = base + self.wobj = wobj + self.tool = tool + self.ref_pos = ref_pos + self.check_points = check_points + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.base = [] + (_etype1587, _size1584) = iprot.readListBegin() + for _i1588 in range(_size1584): + _elem1589 = iprot.readDouble() + self.base.append(_elem1589) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.wobj = [] + (_etype1593, _size1590) = iprot.readListBegin() + for _i1594 in range(_size1590): + _elem1595 = iprot.readDouble() + self.wobj.append(_elem1595) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.tool = [] + (_etype1599, _size1596) = iprot.readListBegin() + for _i1600 in range(_size1596): + _elem1601 = iprot.readDouble() + self.tool.append(_elem1601) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.ref_pos = [] + (_etype1605, _size1602) = iprot.readListBegin() + for _i1606 in range(_size1602): + _elem1607 = iprot.readDouble() + self.ref_pos.append(_elem1607) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.check_points = [] + (_etype1611, _size1608) = iprot.readListBegin() + for _i1612 in range(_size1608): + _elem1613 = [] + (_etype1617, _size1614) = iprot.readListBegin() + for _i1618 in range(_size1614): + _elem1619 = iprot.readDouble() + _elem1613.append(_elem1619) + iprot.readListEnd() + self.check_points.append(_elem1613) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('reach_check_args') + if self.base is not None: + oprot.writeFieldBegin('base', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.base)) + for iter1620 in self.base: + oprot.writeDouble(iter1620) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.wobj)) + for iter1621 in self.wobj: + oprot.writeDouble(iter1621) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.tool)) + for iter1622 in self.tool: + oprot.writeDouble(iter1622) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.ref_pos is not None: + oprot.writeFieldBegin('ref_pos', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.ref_pos)) + for iter1623 in self.ref_pos: + oprot.writeDouble(iter1623) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.check_points is not None: + oprot.writeFieldBegin('check_points', TType.LIST, 5) + oprot.writeListBegin(TType.LIST, len(self.check_points)) + for iter1624 in self.check_points: + oprot.writeListBegin(TType.DOUBLE, len(iter1624)) + for iter1625 in iter1624: + oprot.writeDouble(iter1625) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(reach_check_args) +reach_check_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'base', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'wobj', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'tool', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'ref_pos', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'check_points', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 5 +) + + +class reach_check_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRUCT: + self.success = ReachabilityParam() + self.success.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('reach_check_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRUCT, 0) + self.success.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(reach_check_result) +reach_check_result.thrift_spec = ( + (0, TType.STRUCT, 'success', [ReachabilityParam, None], None, ), # 0 +) + + +class move_jog_eaxis_args(object): + """ + Attributes: + - name + - direction + - vel + - block + + """ + + + def __init__(self, name=None, direction=None, vel=None, block=None,): + self.name = name + self.direction = direction + self.vel = vel + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.direction = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_jog_eaxis_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.direction is not None: + oprot.writeFieldBegin('direction', TType.I32, 2) + oprot.writeI32(self.direction) + oprot.writeFieldEnd() + if self.vel is not None: + oprot.writeFieldBegin('vel', TType.DOUBLE, 3) + oprot.writeDouble(self.vel) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 4) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_jog_eaxis_args) +move_jog_eaxis_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.I32, 'direction', None, None, ), # 2 + (3, TType.DOUBLE, 'vel', None, None, ), # 3 + (4, TType.BOOL, 'block', None, None, ), # 4 +) + + +class move_jog_eaxis_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_jog_eaxis_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_jog_eaxis_result) +move_jog_eaxis_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_eaxis_info_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_eaxis_info_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_eaxis_info_args) +get_eaxis_info_args.thrift_spec = ( +) + + +class get_eaxis_info_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1629, _size1626) = iprot.readListBegin() + for _i1630 in range(_size1626): + _elem1631 = EAxissInfo() + _elem1631.read(iprot) + self.success.append(_elem1631) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_eaxis_info_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.STRUCT, len(self.success)) + for iter1632 in self.success: + iter1632.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_eaxis_info_result) +get_eaxis_info_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.STRUCT, [EAxissInfo, None], False), None, ), # 0 +) + + +class set_hand_teach_parameter_args(object): + """ + Attributes: + - space + - joint_scale + - cart_scale + - coord_type + - direction + - global_scale + + """ + + + def __init__(self, space=None, joint_scale=None, cart_scale=None, coord_type=None, direction=None, global_scale=None,): + self.space = space + self.joint_scale = joint_scale + self.cart_scale = cart_scale + self.coord_type = coord_type + self.direction = direction + self.global_scale = global_scale + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.space = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.joint_scale = [] + (_etype1636, _size1633) = iprot.readListBegin() + for _i1637 in range(_size1633): + _elem1638 = iprot.readI32() + self.joint_scale.append(_elem1638) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.cart_scale = [] + (_etype1642, _size1639) = iprot.readListBegin() + for _i1643 in range(_size1639): + _elem1644 = iprot.readI32() + self.cart_scale.append(_elem1644) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.coord_type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.direction = [] + (_etype1648, _size1645) = iprot.readListBegin() + for _i1649 in range(_size1645): + _elem1650 = iprot.readBool() + self.direction.append(_elem1650) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.I32: + self.global_scale = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_hand_teach_parameter_args') + if self.space is not None: + oprot.writeFieldBegin('space', TType.I32, 1) + oprot.writeI32(self.space) + oprot.writeFieldEnd() + if self.joint_scale is not None: + oprot.writeFieldBegin('joint_scale', TType.LIST, 2) + oprot.writeListBegin(TType.I32, len(self.joint_scale)) + for iter1651 in self.joint_scale: + oprot.writeI32(iter1651) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cart_scale is not None: + oprot.writeFieldBegin('cart_scale', TType.LIST, 3) + oprot.writeListBegin(TType.I32, len(self.cart_scale)) + for iter1652 in self.cart_scale: + oprot.writeI32(iter1652) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.coord_type is not None: + oprot.writeFieldBegin('coord_type', TType.I32, 4) + oprot.writeI32(self.coord_type) + oprot.writeFieldEnd() + if self.direction is not None: + oprot.writeFieldBegin('direction', TType.LIST, 5) + oprot.writeListBegin(TType.BOOL, len(self.direction)) + for iter1653 in self.direction: + oprot.writeBool(iter1653) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.global_scale is not None: + oprot.writeFieldBegin('global_scale', TType.I32, 6) + oprot.writeI32(self.global_scale) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_hand_teach_parameter_args) +set_hand_teach_parameter_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'space', None, None, ), # 1 + (2, TType.LIST, 'joint_scale', (TType.I32, None, False), None, ), # 2 + (3, TType.LIST, 'cart_scale', (TType.I32, None, False), None, ), # 3 + (4, TType.I32, 'coord_type', None, None, ), # 4 + (5, TType.LIST, 'direction', (TType.BOOL, None, False), None, ), # 5 + (6, TType.I32, 'global_scale', None, None, ), # 6 +) + + +class set_hand_teach_parameter_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_hand_teach_parameter_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_hand_teach_parameter_result) +set_hand_teach_parameter_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_pendant_type_args(object): + """ + Attributes: + - type + + """ + + + def __init__(self, type=None,): + self.type = type + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_pendant_type_args') + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 1) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_pendant_type_args) +set_pendant_type_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'type', None, None, ), # 1 +) + + +class set_pendant_type_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_pendant_type_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_pendant_type_result) +set_pendant_type_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_blend_ahead_args(object): + """ + Attributes: + - per + - num + + """ + + + def __init__(self, per=None, num=None,): + self.per = per + self.num = num + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.per = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.num = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_blend_ahead_args') + if self.per is not None: + oprot.writeFieldBegin('per', TType.I32, 1) + oprot.writeI32(self.per) + oprot.writeFieldEnd() + if self.num is not None: + oprot.writeFieldBegin('num', TType.I32, 2) + oprot.writeI32(self.num) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_blend_ahead_args) +set_blend_ahead_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'per', None, None, ), # 1 + (2, TType.I32, 'num', None, None, ), # 2 +) + + +class set_blend_ahead_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_blend_ahead_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_blend_ahead_result) +set_blend_ahead_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class switch_mode_args(object): + """ + Attributes: + - mode + + """ + + + def __init__(self, mode=None,): + self.mode = mode + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('switch_mode_args') + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 1) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(switch_mode_args) +switch_mode_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'mode', None, None, ), # 1 +) + + +class switch_mode_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('switch_mode_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(switch_mode_result) +switch_mode_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class read_encoder_count_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_encoder_count_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_encoder_count_args) +read_encoder_count_args.thrift_spec = ( +) + + +class read_encoder_count_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I64: + self.success = iprot.readI64() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('read_encoder_count_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I64, 0) + oprot.writeI64(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(read_encoder_count_result) +read_encoder_count_result.thrift_spec = ( + (0, TType.I64, 'success', None, None, ), # 0 +) + + +class set_kinematic_calibration_params_args(object): + """ + Attributes: + - params + + """ + + + def __init__(self, params=None,): + self.params = params + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.params = [] + (_etype1657, _size1654) = iprot.readListBegin() + for _i1658 in range(_size1654): + _elem1659 = [] + (_etype1663, _size1660) = iprot.readListBegin() + for _i1664 in range(_size1660): + _elem1665 = iprot.readDouble() + _elem1659.append(_elem1665) + iprot.readListEnd() + self.params.append(_elem1659) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_kinematic_calibration_params_args') + if self.params is not None: + oprot.writeFieldBegin('params', TType.LIST, 1) + oprot.writeListBegin(TType.LIST, len(self.params)) + for iter1666 in self.params: + oprot.writeListBegin(TType.DOUBLE, len(iter1666)) + for iter1667 in iter1666: + oprot.writeDouble(iter1667) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_kinematic_calibration_params_args) +set_kinematic_calibration_params_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'params', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 1 +) + + +class set_kinematic_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_kinematic_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_kinematic_calibration_params_result) +set_kinematic_calibration_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_pos_bias_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_pos_bias_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_pos_bias_args) +get_pos_bias_args.thrift_spec = ( +) + + +class get_pos_bias_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1671, _size1668) = iprot.readListBegin() + for _i1672 in range(_size1668): + _elem1673 = iprot.readDouble() + self.success.append(_elem1673) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_pos_bias_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1674 in self.success: + oprot.writeDouble(iter1674) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_pos_bias_result) +get_pos_bias_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_system_value_lists_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_lists_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_lists_args) +get_system_value_lists_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_system_value_lists_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1678, _size1675) = iprot.readListBegin() + for _i1679 in range(_size1675): + _elem1680 = [] + (_etype1684, _size1681) = iprot.readListBegin() + for _i1685 in range(_size1681): + _elem1686 = iprot.readDouble() + _elem1680.append(_elem1686) + iprot.readListEnd() + self.success.append(_elem1680) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_system_value_lists_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1687 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1687)) + for iter1688 in iter1687: + oprot.writeDouble(iter1688) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_system_value_lists_result) +get_system_value_lists_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class get_origin_DH_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_origin_DH_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_origin_DH_args) +get_origin_DH_args.thrift_spec = ( +) + + +class get_origin_DH_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1692, _size1689) = iprot.readListBegin() + for _i1693 in range(_size1689): + _elem1694 = [] + (_etype1698, _size1695) = iprot.readListBegin() + for _i1699 in range(_size1695): + _elem1700 = iprot.readDouble() + _elem1694.append(_elem1700) + iprot.readListEnd() + self.success.append(_elem1694) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_origin_DH_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1701 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1701)) + for iter1702 in iter1701: + oprot.writeDouble(iter1702) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_origin_DH_result) +get_origin_DH_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class get_calib_DH_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_calib_DH_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_calib_DH_args) +get_calib_DH_args.thrift_spec = ( +) + + +class get_calib_DH_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1706, _size1703) = iprot.readListBegin() + for _i1707 in range(_size1703): + _elem1708 = [] + (_etype1712, _size1709) = iprot.readListBegin() + for _i1713 in range(_size1709): + _elem1714 = iprot.readDouble() + _elem1708.append(_elem1714) + iprot.readListEnd() + self.success.append(_elem1708) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_calib_DH_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1715 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1715)) + for iter1716 in iter1715: + oprot.writeDouble(iter1716) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_calib_DH_result) +get_calib_DH_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class get_robot_type_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_robot_type_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_robot_type_args) +get_robot_type_args.thrift_spec = ( +) + + +class get_robot_type_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1720, _size1717) = iprot.readListBegin() + for _i1721 in range(_size1717): + _elem1722 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.success.append(_elem1722) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_robot_type_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.STRING, len(self.success)) + for iter1723 in self.success: + oprot.writeString(iter1723.encode('utf-8') if sys.version_info[0] == 2 else iter1723) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_robot_type_result) +get_robot_type_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.STRING, 'UTF8', False), None, ), # 0 +) + + +class get_ext_torque_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_ext_torque_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_ext_torque_args) +get_ext_torque_args.thrift_spec = ( +) + + +class get_ext_torque_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1727, _size1724) = iprot.readListBegin() + for _i1728 in range(_size1724): + _elem1729 = iprot.readDouble() + self.success.append(_elem1729) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_ext_torque_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1730 in self.success: + oprot.writeDouble(iter1730) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_ext_torque_result) +get_ext_torque_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class set_dynamic_calibration_params_args(object): + """ + Attributes: + - params + + """ + + + def __init__(self, params=None,): + self.params = params + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.params = [] + (_etype1734, _size1731) = iprot.readListBegin() + for _i1735 in range(_size1731): + _elem1736 = [] + (_etype1740, _size1737) = iprot.readListBegin() + for _i1741 in range(_size1737): + _elem1742 = iprot.readDouble() + _elem1736.append(_elem1742) + iprot.readListEnd() + self.params.append(_elem1736) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_dynamic_calibration_params_args') + if self.params is not None: + oprot.writeFieldBegin('params', TType.LIST, 1) + oprot.writeListBegin(TType.LIST, len(self.params)) + for iter1743 in self.params: + oprot.writeListBegin(TType.DOUBLE, len(iter1743)) + for iter1744 in iter1743: + oprot.writeDouble(iter1744) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_dynamic_calibration_params_args) +set_dynamic_calibration_params_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'params', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 1 +) + + +class set_dynamic_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_dynamic_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_dynamic_calibration_params_result) +set_dynamic_calibration_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_dynamic_calibration_params_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_dynamic_calibration_params_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_dynamic_calibration_params_args) +get_dynamic_calibration_params_args.thrift_spec = ( +) + + +class get_dynamic_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1748, _size1745) = iprot.readListBegin() + for _i1749 in range(_size1745): + _elem1750 = [] + (_etype1754, _size1751) = iprot.readListBegin() + for _i1755 in range(_size1751): + _elem1756 = iprot.readDouble() + _elem1750.append(_elem1756) + iprot.readListEnd() + self.success.append(_elem1750) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_dynamic_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1757 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1757)) + for iter1758 in iter1757: + oprot.writeDouble(iter1758) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_dynamic_calibration_params_result) +get_dynamic_calibration_params_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class upload_robot_param_to_toolboard_args(object): + """ + Attributes: + - passwd + + """ + + + def __init__(self, passwd=None,): + self.passwd = passwd + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('upload_robot_param_to_toolboard_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(upload_robot_param_to_toolboard_args) +upload_robot_param_to_toolboard_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 +) + + +class upload_robot_param_to_toolboard_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('upload_robot_param_to_toolboard_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(upload_robot_param_to_toolboard_result) +upload_robot_param_to_toolboard_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_kinematic_calibration_info_args(object): + """ + Attributes: + - passwd + - info + + """ + + + def __init__(self, passwd=None, info=None,): + self.passwd = passwd + self.info = info + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.info = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_kinematic_calibration_info_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + if self.info is not None: + oprot.writeFieldBegin('info', TType.STRING, 2) + oprot.writeString(self.info.encode('utf-8') if sys.version_info[0] == 2 else self.info) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_kinematic_calibration_info_args) +set_kinematic_calibration_info_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 + (2, TType.STRING, 'info', 'UTF8', None, ), # 2 +) + + +class set_kinematic_calibration_info_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_kinematic_calibration_info_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_kinematic_calibration_info_result) +set_kinematic_calibration_info_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_dynamic_calibration_info_args(object): + """ + Attributes: + - passwd + - info + + """ + + + def __init__(self, passwd=None, info=None,): + self.passwd = passwd + self.info = info + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.info = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_dynamic_calibration_info_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + if self.info is not None: + oprot.writeFieldBegin('info', TType.STRING, 2) + oprot.writeString(self.info.encode('utf-8') if sys.version_info[0] == 2 else self.info) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_dynamic_calibration_info_args) +set_dynamic_calibration_info_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 + (2, TType.STRING, 'info', 'UTF8', None, ), # 2 +) + + +class set_dynamic_calibration_info_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_dynamic_calibration_info_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_dynamic_calibration_info_result) +set_dynamic_calibration_info_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_vibration_calibration_info_args(object): + """ + Attributes: + - passwd + - info + + """ + + + def __init__(self, passwd=None, info=None,): + self.passwd = passwd + self.info = info + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.info = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_vibration_calibration_info_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + if self.info is not None: + oprot.writeFieldBegin('info', TType.STRING, 2) + oprot.writeString(self.info.encode('utf-8') if sys.version_info[0] == 2 else self.info) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_vibration_calibration_info_args) +set_vibration_calibration_info_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 + (2, TType.STRING, 'info', 'UTF8', None, ), # 2 +) + + +class set_vibration_calibration_info_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_vibration_calibration_info_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_vibration_calibration_info_result) +set_vibration_calibration_info_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_axis_motor_rated_current_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_axis_motor_rated_current_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_axis_motor_rated_current_args) +get_axis_motor_rated_current_args.thrift_spec = ( +) + + +class get_axis_motor_rated_current_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1762, _size1759) = iprot.readListBegin() + for _i1763 in range(_size1759): + _elem1764 = iprot.readDouble() + self.success.append(_elem1764) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_axis_motor_rated_current_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1765 in self.success: + oprot.writeDouble(iter1765) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_axis_motor_rated_current_result) +get_axis_motor_rated_current_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_axis_motor_kt_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_axis_motor_kt_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_axis_motor_kt_args) +get_axis_motor_kt_args.thrift_spec = ( +) + + +class get_axis_motor_kt_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1769, _size1766) = iprot.readListBegin() + for _i1770 in range(_size1766): + _elem1771 = iprot.readDouble() + self.success.append(_elem1771) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_axis_motor_kt_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1772 in self.success: + oprot.writeDouble(iter1772) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_axis_motor_kt_result) +get_axis_motor_kt_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class abort_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('abort_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(abort_args) +abort_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class abort_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('abort_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(abort_result) +abort_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_vibration_calibration_params_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_vibration_calibration_params_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_vibration_calibration_params_args) +get_vibration_calibration_params_args.thrift_spec = ( +) + + +class get_vibration_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1776, _size1773) = iprot.readListBegin() + for _i1777 in range(_size1773): + _elem1778 = [] + (_etype1782, _size1779) = iprot.readListBegin() + for _i1783 in range(_size1779): + _elem1784 = iprot.readDouble() + _elem1778.append(_elem1784) + iprot.readListEnd() + self.success.append(_elem1778) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_vibration_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1785 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1785)) + for iter1786 in iter1785: + oprot.writeDouble(iter1786) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_vibration_calibration_params_result) +get_vibration_calibration_params_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class save_kinematic_calibration_params_args(object): + """ + Attributes: + - passwd + + """ + + + def __init__(self, passwd=None,): + self.passwd = passwd + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('save_kinematic_calibration_params_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(save_kinematic_calibration_params_args) +save_kinematic_calibration_params_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 +) + + +class save_kinematic_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('save_kinematic_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(save_kinematic_calibration_params_result) +save_kinematic_calibration_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class save_dynamic_calibration_params_args(object): + """ + Attributes: + - passwd + + """ + + + def __init__(self, passwd=None,): + self.passwd = passwd + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('save_dynamic_calibration_params_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(save_dynamic_calibration_params_args) +save_dynamic_calibration_params_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 +) + + +class save_dynamic_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('save_dynamic_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(save_dynamic_calibration_params_result) +save_dynamic_calibration_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_flange_pose_cmd_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_pose_cmd_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_pose_cmd_args) +get_flange_pose_cmd_args.thrift_spec = ( +) + + +class get_flange_pose_cmd_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1790, _size1787) = iprot.readListBegin() + for _i1791 in range(_size1787): + _elem1792 = iprot.readDouble() + self.success.append(_elem1792) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_pose_cmd_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1793 in self.success: + oprot.writeDouble(iter1793) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_pose_cmd_result) +get_flange_pose_cmd_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_flange_speed_cmd_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_speed_cmd_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_speed_cmd_args) +get_flange_speed_cmd_args.thrift_spec = ( +) + + +class get_flange_speed_cmd_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1797, _size1794) = iprot.readListBegin() + for _i1798 in range(_size1794): + _elem1799 = iprot.readDouble() + self.success.append(_elem1799) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_speed_cmd_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1800 in self.success: + oprot.writeDouble(iter1800) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_speed_cmd_result) +get_flange_speed_cmd_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_flange_acceleration_cmd_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_acceleration_cmd_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_acceleration_cmd_args) +get_flange_acceleration_cmd_args.thrift_spec = ( +) + + +class get_flange_acceleration_cmd_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1804, _size1801) = iprot.readListBegin() + for _i1805 in range(_size1801): + _elem1806 = iprot.readDouble() + self.success.append(_elem1806) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_flange_acceleration_cmd_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1807 in self.success: + oprot.writeDouble(iter1807) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_flange_acceleration_cmd_result) +get_flange_acceleration_cmd_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_pose_command_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_command_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_command_args) +get_tcp_pose_command_args.thrift_spec = ( +) + + +class get_tcp_pose_command_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1811, _size1808) = iprot.readListBegin() + for _i1812 in range(_size1808): + _elem1813 = iprot.readDouble() + self.success.append(_elem1813) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_command_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1814 in self.success: + oprot.writeDouble(iter1814) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_command_result) +get_tcp_pose_command_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_speed_command_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_command_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_command_args) +get_tcp_speed_command_args.thrift_spec = ( +) + + +class get_tcp_speed_command_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1818, _size1815) = iprot.readListBegin() + for _i1819 in range(_size1815): + _elem1820 = iprot.readDouble() + self.success.append(_elem1820) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_command_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1821 in self.success: + oprot.writeDouble(iter1821) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_command_result) +get_tcp_speed_command_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_acceleration_command_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_command_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_command_args) +get_tcp_acceleration_command_args.thrift_spec = ( +) + + +class get_tcp_acceleration_command_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1825, _size1822) = iprot.readListBegin() + for _i1826 in range(_size1822): + _elem1827 = iprot.readDouble() + self.success.append(_elem1827) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_command_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1828 in self.success: + oprot.writeDouble(iter1828) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_command_result) +get_tcp_acceleration_command_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_pose_command_coord_args(object): + """ + Attributes: + - tool + - wobj + + """ + + + def __init__(self, tool=None, wobj=None,): + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_command_coord_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_command_coord_args) +get_tcp_pose_command_coord_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 +) + + +class get_tcp_pose_command_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1832, _size1829) = iprot.readListBegin() + for _i1833 in range(_size1829): + _elem1834 = iprot.readDouble() + self.success.append(_elem1834) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_pose_command_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1835 in self.success: + oprot.writeDouble(iter1835) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_pose_command_coord_result) +get_tcp_pose_command_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_speed_command_coord_args(object): + """ + Attributes: + - tool + - wobj + + """ + + + def __init__(self, tool=None, wobj=None,): + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_command_coord_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_command_coord_args) +get_tcp_speed_command_coord_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 +) + + +class get_tcp_speed_command_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1839, _size1836) = iprot.readListBegin() + for _i1840 in range(_size1836): + _elem1841 = iprot.readDouble() + self.success.append(_elem1841) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_command_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1842 in self.success: + oprot.writeDouble(iter1842) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_command_coord_result) +get_tcp_speed_command_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_acceleration_command_coord_args(object): + """ + Attributes: + - tool + - wobj + + """ + + + def __init__(self, tool=None, wobj=None,): + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_command_coord_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_command_coord_args) +get_tcp_acceleration_command_coord_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 +) + + +class get_tcp_acceleration_command_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1846, _size1843) = iprot.readListBegin() + for _i1847 in range(_size1843): + _elem1848 = iprot.readDouble() + self.success.append(_elem1848) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_command_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1849 in self.success: + oprot.writeDouble(iter1849) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_command_coord_result) +get_tcp_acceleration_command_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_speed_coord_args(object): + """ + Attributes: + - tool + - wobj + + """ + + + def __init__(self, tool=None, wobj=None,): + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_coord_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_coord_args) +get_tcp_speed_coord_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 +) + + +class get_tcp_speed_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1853, _size1850) = iprot.readListBegin() + for _i1854 in range(_size1850): + _elem1855 = iprot.readDouble() + self.success.append(_elem1855) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_speed_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1856 in self.success: + oprot.writeDouble(iter1856) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_speed_coord_result) +get_tcp_speed_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tcp_acceleration_coord_args(object): + """ + Attributes: + - tool + - wobj + + """ + + + def __init__(self, tool=None, wobj=None,): + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_coord_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_coord_args) +get_tcp_acceleration_coord_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 +) + + +class get_tcp_acceleration_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1860, _size1857) = iprot.readListBegin() + for _i1861 in range(_size1857): + _elem1862 = iprot.readDouble() + self.success.append(_elem1862) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tcp_acceleration_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1863 in self.success: + oprot.writeDouble(iter1863) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tcp_acceleration_coord_result) +get_tcp_acceleration_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class clear_error_message_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('clear_error_message_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(clear_error_message_args) +clear_error_message_args.thrift_spec = ( +) + + +class clear_error_message_result(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('clear_error_message_result') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(clear_error_message_result) +clear_error_message_result.thrift_spec = ( +) + + +class cal_traj_time_args(object): + """ + Attributes: + - param + + """ + + + def __init__(self, param=None,): + self.param = param + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRUCT: + self.param = TrajTimeParam() + self.param.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('cal_traj_time_args') + if self.param is not None: + oprot.writeFieldBegin('param', TType.STRUCT, 1) + self.param.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(cal_traj_time_args) +cal_traj_time_args.thrift_spec = ( + None, # 0 + (1, TType.STRUCT, 'param', [TrajTimeParam, None], None, ), # 1 +) + + +class cal_traj_time_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I64: + self.success = iprot.readI64() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('cal_traj_time_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I64, 0) + oprot.writeI64(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(cal_traj_time_result) +cal_traj_time_result.thrift_spec = ( + (0, TType.I64, 'success', None, None, ), # 0 +) + + +class get_hardware_clock_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_hardware_clock_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_hardware_clock_args) +get_hardware_clock_args.thrift_spec = ( +) + + +class get_hardware_clock_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I64: + self.success = iprot.readI64() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_hardware_clock_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I64, 0) + oprot.writeI64(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_hardware_clock_result) +get_hardware_clock_result.thrift_spec = ( + (0, TType.I64, 'success', None, None, ), # 0 +) + + +class get_tool_data_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_data_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_data_args) +get_tool_data_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_tool_data_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1867, _size1864) = iprot.readListBegin() + for _i1868 in range(_size1864): + _elem1869 = iprot.readDouble() + self.success.append(_elem1869) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_data_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1870 in self.success: + oprot.writeDouble(iter1870) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_data_result) +get_tool_data_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_tool_load_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_load_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_load_args) +get_tool_load_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_tool_load_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1874, _size1871) = iprot.readListBegin() + for _i1875 in range(_size1871): + _elem1876 = iprot.readDouble() + self.success.append(_elem1876) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_tool_load_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1877 in self.success: + oprot.writeDouble(iter1877) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_tool_load_result) +get_tool_load_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_wobj_args(object): + """ + Attributes: + - name + + """ + + + def __init__(self, name=None,): + self.name = name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_wobj_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_wobj_args) +get_wobj_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 +) + + +class get_wobj_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1881, _size1878) = iprot.readListBegin() + for _i1882 in range(_size1878): + _elem1883 = iprot.readDouble() + self.success.append(_elem1883) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_wobj_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1884 in self.success: + oprot.writeDouble(iter1884) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_wobj_result) +get_wobj_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_base_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_base_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_base_args) +get_base_args.thrift_spec = ( +) + + +class get_base_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1888, _size1885) = iprot.readListBegin() + for _i1889 in range(_size1885): + _elem1890 = iprot.readDouble() + self.success.append(_elem1890) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_base_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1891 in self.success: + oprot.writeDouble(iter1891) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_base_result) +get_base_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class get_home_pose_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_home_pose_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_home_pose_args) +get_home_pose_args.thrift_spec = ( +) + + +class get_home_pose_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1895, _size1892) = iprot.readListBegin() + for _i1896 in range(_size1892): + _elem1897 = iprot.readDouble() + self.success.append(_elem1897) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_home_pose_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter1898 in self.success: + oprot.writeDouble(iter1898) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_home_pose_result) +get_home_pose_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class set_tool_data_workcell_args(object): + """ + Attributes: + - name + - tool + - payload + - inertia_tensor + + """ + + + def __init__(self, name=None, tool=None, payload=None, inertia_tensor=None,): + self.name = name + self.tool = tool + self.payload = payload + self.inertia_tensor = inertia_tensor + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tool = [] + (_etype1902, _size1899) = iprot.readListBegin() + for _i1903 in range(_size1899): + _elem1904 = iprot.readDouble() + self.tool.append(_elem1904) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.payload = [] + (_etype1908, _size1905) = iprot.readListBegin() + for _i1909 in range(_size1905): + _elem1910 = iprot.readDouble() + self.payload.append(_elem1910) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.inertia_tensor = [] + (_etype1914, _size1911) = iprot.readListBegin() + for _i1915 in range(_size1911): + _elem1916 = iprot.readDouble() + self.inertia_tensor.append(_elem1916) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_tool_data_workcell_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.tool)) + for iter1917 in self.tool: + oprot.writeDouble(iter1917) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.payload is not None: + oprot.writeFieldBegin('payload', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.payload)) + for iter1918 in self.payload: + oprot.writeDouble(iter1918) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.inertia_tensor is not None: + oprot.writeFieldBegin('inertia_tensor', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.inertia_tensor)) + for iter1919 in self.inertia_tensor: + oprot.writeDouble(iter1919) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_tool_data_workcell_args) +set_tool_data_workcell_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'tool', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'payload', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'inertia_tensor', (TType.DOUBLE, None, False), None, ), # 4 +) + + +class set_tool_data_workcell_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_tool_data_workcell_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_tool_data_workcell_result) +set_tool_data_workcell_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_wobj_workcell_args(object): + """ + Attributes: + - name + - wobj + + """ + + + def __init__(self, name=None, wobj=None,): + self.name = name + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.wobj = [] + (_etype1923, _size1920) = iprot.readListBegin() + for _i1924 in range(_size1920): + _elem1925 = iprot.readDouble() + self.wobj.append(_elem1925) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_wobj_workcell_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.wobj)) + for iter1926 in self.wobj: + oprot.writeDouble(iter1926) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_wobj_workcell_args) +set_wobj_workcell_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'wobj', (TType.DOUBLE, None, False), None, ), # 2 +) + + +class set_wobj_workcell_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_wobj_workcell_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_wobj_workcell_result) +set_wobj_workcell_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_simulation_state_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_simulation_state_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_simulation_state_args) +get_simulation_state_args.thrift_spec = ( +) + + +class get_simulation_state_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.BOOL: + self.success = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_simulation_state_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.BOOL, 0) + oprot.writeBool(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_simulation_state_result) +get_simulation_state_result.thrift_spec = ( + (0, TType.BOOL, 'success', None, None, ), # 0 +) + + +class save_stiffness_calibration_params_args(object): + """ + Attributes: + - passwd + + """ + + + def __init__(self, passwd=None,): + self.passwd = passwd + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.passwd = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('save_stiffness_calibration_params_args') + if self.passwd is not None: + oprot.writeFieldBegin('passwd', TType.STRING, 1) + oprot.writeString(self.passwd.encode('utf-8') if sys.version_info[0] == 2 else self.passwd) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(save_stiffness_calibration_params_args) +save_stiffness_calibration_params_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'passwd', 'UTF8', None, ), # 1 +) + + +class save_stiffness_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('save_stiffness_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(save_stiffness_calibration_params_result) +save_stiffness_calibration_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_stiffness_calibration_params_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_stiffness_calibration_params_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_stiffness_calibration_params_args) +get_stiffness_calibration_params_args.thrift_spec = ( +) + + +class get_stiffness_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1930, _size1927) = iprot.readListBegin() + for _i1931 in range(_size1927): + _elem1932 = [] + (_etype1936, _size1933) = iprot.readListBegin() + for _i1937 in range(_size1933): + _elem1938 = iprot.readDouble() + _elem1932.append(_elem1938) + iprot.readListEnd() + self.success.append(_elem1932) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_stiffness_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1939 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1939)) + for iter1940 in iter1939: + oprot.writeDouble(iter1940) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_stiffness_calibration_params_result) +get_stiffness_calibration_params_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class set_stiffness_calibration_params_args(object): + """ + Attributes: + - params + + """ + + + def __init__(self, params=None,): + self.params = params + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.params = [] + (_etype1944, _size1941) = iprot.readListBegin() + for _i1945 in range(_size1941): + _elem1946 = [] + (_etype1950, _size1947) = iprot.readListBegin() + for _i1951 in range(_size1947): + _elem1952 = iprot.readDouble() + _elem1946.append(_elem1952) + iprot.readListEnd() + self.params.append(_elem1946) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_stiffness_calibration_params_args') + if self.params is not None: + oprot.writeFieldBegin('params', TType.LIST, 1) + oprot.writeListBegin(TType.LIST, len(self.params)) + for iter1953 in self.params: + oprot.writeListBegin(TType.DOUBLE, len(iter1953)) + for iter1954 in iter1953: + oprot.writeDouble(iter1954) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_stiffness_calibration_params_args) +set_stiffness_calibration_params_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'params', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 1 +) + + +class set_stiffness_calibration_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_stiffness_calibration_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_stiffness_calibration_params_result) +set_stiffness_calibration_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_joint_motion_params_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_joint_motion_params_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_joint_motion_params_args) +get_joint_motion_params_args.thrift_spec = ( +) + + +class get_joint_motion_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype1958, _size1955) = iprot.readListBegin() + for _i1959 in range(_size1955): + _elem1960 = [] + (_etype1964, _size1961) = iprot.readListBegin() + for _i1965 in range(_size1961): + _elem1966 = iprot.readDouble() + _elem1960.append(_elem1966) + iprot.readListEnd() + self.success.append(_elem1960) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_joint_motion_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter1967 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter1967)) + for iter1968 in iter1967: + oprot.writeDouble(iter1968) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_joint_motion_params_result) +get_joint_motion_params_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class move_fixed_args(object): + """ + Attributes: + - tool + - wobj + - axis + - timer + - scheme_name + - epose + - eaxis_v + - block + - op + - def_acc + + """ + + + def __init__(self, tool=None, wobj=None, axis=None, timer=None, scheme_name=None, epose=None, eaxis_v=None, block=None, op=None, def_acc=None,): + self.tool = tool + self.wobj = wobj + self.axis = axis + self.timer = timer + self.scheme_name = scheme_name + self.epose = epose + self.eaxis_v = eaxis_v + self.block = block + self.op = op + self.def_acc = def_acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.axis = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.I32: + self.timer = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.scheme_name = [] + (_etype1972, _size1969) = iprot.readListBegin() + for _i1973 in range(_size1969): + _elem1974 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.scheme_name.append(_elem1974) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.epose = [] + (_etype1978, _size1975) = iprot.readListBegin() + for _i1979 in range(_size1975): + _elem1980 = [] + (_etype1984, _size1981) = iprot.readListBegin() + for _i1985 in range(_size1981): + _elem1986 = iprot.readDouble() + _elem1980.append(_elem1986) + iprot.readListEnd() + self.epose.append(_elem1980) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.eaxis_v = [] + (_etype1990, _size1987) = iprot.readListBegin() + for _i1991 in range(_size1987): + _elem1992 = iprot.readDouble() + self.eaxis_v.append(_elem1992) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.def_acc = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_fixed_args') + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 1) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 2) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.axis is not None: + oprot.writeFieldBegin('axis', TType.I32, 3) + oprot.writeI32(self.axis) + oprot.writeFieldEnd() + if self.timer is not None: + oprot.writeFieldBegin('timer', TType.I32, 4) + oprot.writeI32(self.timer) + oprot.writeFieldEnd() + if self.scheme_name is not None: + oprot.writeFieldBegin('scheme_name', TType.LIST, 5) + oprot.writeListBegin(TType.STRING, len(self.scheme_name)) + for iter1993 in self.scheme_name: + oprot.writeString(iter1993.encode('utf-8') if sys.version_info[0] == 2 else iter1993) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.epose is not None: + oprot.writeFieldBegin('epose', TType.LIST, 6) + oprot.writeListBegin(TType.LIST, len(self.epose)) + for iter1994 in self.epose: + oprot.writeListBegin(TType.DOUBLE, len(iter1994)) + for iter1995 in iter1994: + oprot.writeDouble(iter1995) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_v is not None: + oprot.writeFieldBegin('eaxis_v', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_v)) + for iter1996 in self.eaxis_v: + oprot.writeDouble(iter1996) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 8) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 9) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.def_acc is not None: + oprot.writeFieldBegin('def_acc', TType.BOOL, 10) + oprot.writeBool(self.def_acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_fixed_args) +move_fixed_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'tool', 'UTF8', None, ), # 1 + (2, TType.STRING, 'wobj', 'UTF8', None, ), # 2 + (3, TType.I32, 'axis', None, None, ), # 3 + (4, TType.I32, 'timer', None, None, ), # 4 + (5, TType.LIST, 'scheme_name', (TType.STRING, 'UTF8', False), None, ), # 5 + (6, TType.LIST, 'epose', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 6 + (7, TType.LIST, 'eaxis_v', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.BOOL, 'block', None, None, ), # 8 + (9, TType.STRUCT, 'op', [Op, None], None, ), # 9 + (10, TType.BOOL, 'def_acc', None, None, ), # 10 +) + + +class move_fixed_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('move_fixed_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(move_fixed_result) +move_fixed_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class track_enqueue_op_vel_args(object): + """ + Attributes: + - track + - block + + """ + + + def __init__(self, track=None, block=None,): + self.track = track + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.track = [] + (_etype2000, _size1997) = iprot.readListBegin() + for _i2001 in range(_size1997): + _elem2002 = PointOp() + _elem2002.read(iprot) + self.track.append(_elem2002) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('track_enqueue_op_vel_args') + if self.track is not None: + oprot.writeFieldBegin('track', TType.LIST, 1) + oprot.writeListBegin(TType.STRUCT, len(self.track)) + for iter2003 in self.track: + iter2003.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 2) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(track_enqueue_op_vel_args) +track_enqueue_op_vel_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'track', (TType.STRUCT, [PointOp, None], False), None, ), # 1 + (2, TType.BOOL, 'block', None, None, ), # 2 +) + + +class track_enqueue_op_vel_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('track_enqueue_op_vel_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(track_enqueue_op_vel_result) +track_enqueue_op_vel_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class track_cart_vel_motion_args(object): + """ + Attributes: + - acc + - tool + - wobj + - block + + """ + + + def __init__(self, acc=None, tool=None, wobj=None, block=None,): + self.acc = acc + self.tool = tool + self.wobj = wobj + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.DOUBLE: + self.acc = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('track_cart_vel_motion_args') + if self.acc is not None: + oprot.writeFieldBegin('acc', TType.DOUBLE, 1) + oprot.writeDouble(self.acc) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 2) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 3) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 4) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(track_cart_vel_motion_args) +track_cart_vel_motion_args.thrift_spec = ( + None, # 0 + (1, TType.DOUBLE, 'acc', None, None, ), # 1 + (2, TType.STRING, 'tool', 'UTF8', None, ), # 2 + (3, TType.STRING, 'wobj', 'UTF8', None, ), # 3 + (4, TType.BOOL, 'block', None, None, ), # 4 +) + + +class track_cart_vel_motion_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('track_cart_vel_motion_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(track_cart_vel_motion_result) +track_cart_vel_motion_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class path_offset_cal_args(object): + """ + Attributes: + - path_type + - path_point + - tool + - wobj + - path_offset + - path_retract + - tool_rotation + - path_rotation + - weld_rotation + - tool_z_offset + - path_type_list + - plane_normals + - average_height + + """ + + + def __init__(self, path_type=None, path_point=None, tool=None, wobj=None, path_offset=None, path_retract=None, tool_rotation=None, path_rotation=None, weld_rotation=None, tool_z_offset=None, path_type_list=None, plane_normals=None, average_height=None,): + self.path_type = path_type + self.path_point = path_point + self.tool = tool + self.wobj = wobj + self.path_offset = path_offset + self.path_retract = path_retract + self.tool_rotation = tool_rotation + self.path_rotation = path_rotation + self.weld_rotation = weld_rotation + self.tool_z_offset = tool_z_offset + self.path_type_list = path_type_list + self.plane_normals = plane_normals + self.average_height = average_height + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.path_type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.path_point = [] + (_etype2007, _size2004) = iprot.readListBegin() + for _i2008 in range(_size2004): + _elem2009 = [] + (_etype2013, _size2010) = iprot.readListBegin() + for _i2014 in range(_size2010): + _elem2015 = iprot.readDouble() + _elem2009.append(_elem2015) + iprot.readListEnd() + self.path_point.append(_elem2009) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.path_offset = [] + (_etype2019, _size2016) = iprot.readListBegin() + for _i2020 in range(_size2016): + _elem2021 = iprot.readDouble() + self.path_offset.append(_elem2021) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.path_retract = [] + (_etype2025, _size2022) = iprot.readListBegin() + for _i2026 in range(_size2022): + _elem2027 = iprot.readDouble() + self.path_retract.append(_elem2027) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.tool_rotation = [] + (_etype2031, _size2028) = iprot.readListBegin() + for _i2032 in range(_size2028): + _elem2033 = iprot.readDouble() + self.tool_rotation.append(_elem2033) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.path_rotation = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.weld_rotation = [] + (_etype2037, _size2034) = iprot.readListBegin() + for _i2038 in range(_size2034): + _elem2039 = iprot.readDouble() + self.weld_rotation.append(_elem2039) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.I32: + self.tool_z_offset = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.LIST: + self.path_type_list = [] + (_etype2043, _size2040) = iprot.readListBegin() + for _i2044 in range(_size2040): + _elem2045 = iprot.readI32() + self.path_type_list.append(_elem2045) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.LIST: + self.plane_normals = [] + (_etype2049, _size2046) = iprot.readListBegin() + for _i2050 in range(_size2046): + _elem2051 = iprot.readDouble() + self.plane_normals.append(_elem2051) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.BOOL: + self.average_height = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('path_offset_cal_args') + if self.path_type is not None: + oprot.writeFieldBegin('path_type', TType.I32, 1) + oprot.writeI32(self.path_type) + oprot.writeFieldEnd() + if self.path_point is not None: + oprot.writeFieldBegin('path_point', TType.LIST, 2) + oprot.writeListBegin(TType.LIST, len(self.path_point)) + for iter2052 in self.path_point: + oprot.writeListBegin(TType.DOUBLE, len(iter2052)) + for iter2053 in iter2052: + oprot.writeDouble(iter2053) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 3) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 4) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + if self.path_offset is not None: + oprot.writeFieldBegin('path_offset', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.path_offset)) + for iter2054 in self.path_offset: + oprot.writeDouble(iter2054) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.path_retract is not None: + oprot.writeFieldBegin('path_retract', TType.LIST, 6) + oprot.writeListBegin(TType.DOUBLE, len(self.path_retract)) + for iter2055 in self.path_retract: + oprot.writeDouble(iter2055) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool_rotation is not None: + oprot.writeFieldBegin('tool_rotation', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.tool_rotation)) + for iter2056 in self.tool_rotation: + oprot.writeDouble(iter2056) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.path_rotation is not None: + oprot.writeFieldBegin('path_rotation', TType.DOUBLE, 8) + oprot.writeDouble(self.path_rotation) + oprot.writeFieldEnd() + if self.weld_rotation is not None: + oprot.writeFieldBegin('weld_rotation', TType.LIST, 9) + oprot.writeListBegin(TType.DOUBLE, len(self.weld_rotation)) + for iter2057 in self.weld_rotation: + oprot.writeDouble(iter2057) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.tool_z_offset is not None: + oprot.writeFieldBegin('tool_z_offset', TType.I32, 10) + oprot.writeI32(self.tool_z_offset) + oprot.writeFieldEnd() + if self.path_type_list is not None: + oprot.writeFieldBegin('path_type_list', TType.LIST, 11) + oprot.writeListBegin(TType.I32, len(self.path_type_list)) + for iter2058 in self.path_type_list: + oprot.writeI32(iter2058) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.plane_normals is not None: + oprot.writeFieldBegin('plane_normals', TType.LIST, 12) + oprot.writeListBegin(TType.DOUBLE, len(self.plane_normals)) + for iter2059 in self.plane_normals: + oprot.writeDouble(iter2059) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.average_height is not None: + oprot.writeFieldBegin('average_height', TType.BOOL, 13) + oprot.writeBool(self.average_height) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(path_offset_cal_args) +path_offset_cal_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'path_type', None, None, ), # 1 + (2, TType.LIST, 'path_point', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 2 + (3, TType.STRING, 'tool', 'UTF8', None, ), # 3 + (4, TType.STRING, 'wobj', 'UTF8', None, ), # 4 + (5, TType.LIST, 'path_offset', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.LIST, 'path_retract', (TType.DOUBLE, None, False), None, ), # 6 + (7, TType.LIST, 'tool_rotation', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.DOUBLE, 'path_rotation', None, None, ), # 8 + (9, TType.LIST, 'weld_rotation', (TType.DOUBLE, None, False), None, ), # 9 + (10, TType.I32, 'tool_z_offset', None, None, ), # 10 + (11, TType.LIST, 'path_type_list', (TType.I32, None, False), None, ), # 11 + (12, TType.LIST, 'plane_normals', (TType.DOUBLE, None, False), None, ), # 12 + (13, TType.BOOL, 'average_height', None, None, ), # 13 +) + + +class path_offset_cal_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.STRUCT: + self.success = PathOffsetResult() + self.success.read(iprot) + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('path_offset_cal_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.STRUCT, 0) + self.success.write(oprot) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(path_offset_cal_result) +path_offset_cal_result.thrift_spec = ( + (0, TType.STRUCT, 'success', [PathOffsetResult, None], None, ), # 0 +) + + +class set_installation_args(object): + """ + Attributes: + - installation + + """ + + + def __init__(self, installation=None,): + self.installation = installation + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.installation = [] + (_etype2063, _size2060) = iprot.readListBegin() + for _i2064 in range(_size2060): + _elem2065 = iprot.readDouble() + self.installation.append(_elem2065) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_installation_args') + if self.installation is not None: + oprot.writeFieldBegin('installation', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.installation)) + for iter2066 in self.installation: + oprot.writeDouble(iter2066) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_installation_args) +set_installation_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'installation', (TType.DOUBLE, None, False), None, ), # 1 +) + + +class set_installation_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_installation_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_installation_result) +set_installation_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_installation_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_installation_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_installation_args) +get_installation_args.thrift_spec = ( +) + + +class get_installation_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype2070, _size2067) = iprot.readListBegin() + for _i2071 in range(_size2067): + _elem2072 = iprot.readDouble() + self.success.append(_elem2072) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_installation_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.DOUBLE, len(self.success)) + for iter2073 in self.success: + oprot.writeDouble(iter2073) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_installation_result) +get_installation_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.DOUBLE, None, False), None, ), # 0 +) + + +class enable_laser_track_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_laser_track_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_laser_track_args) +enable_laser_track_args.thrift_spec = ( +) + + +class enable_laser_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_laser_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_laser_track_result) +enable_laser_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_laser_track_params_args(object): + """ + Attributes: + - plane + - freq + - lead_len + - active + - kp + - kd + - offset + - offset_vel + - filter_coe + - path_length_correction + - track_type + + """ + + + def __init__(self, plane=None, freq=None, lead_len=None, active=None, kp=None, kd=None, offset=None, offset_vel=None, filter_coe=None, path_length_correction=None, track_type=None,): + self.plane = plane + self.freq = freq + self.lead_len = lead_len + self.active = active + self.kp = kp + self.kd = kd + self.offset = offset + self.offset_vel = offset_vel + self.filter_coe = filter_coe + self.path_length_correction = path_length_correction + self.track_type = track_type + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.plane = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.freq = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.lead_len = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.active = [] + (_etype2077, _size2074) = iprot.readListBegin() + for _i2078 in range(_size2074): + _elem2079 = iprot.readDouble() + self.active.append(_elem2079) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.kp = [] + (_etype2083, _size2080) = iprot.readListBegin() + for _i2084 in range(_size2080): + _elem2085 = iprot.readDouble() + self.kp.append(_elem2085) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.kd = [] + (_etype2089, _size2086) = iprot.readListBegin() + for _i2090 in range(_size2086): + _elem2091 = iprot.readDouble() + self.kd.append(_elem2091) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.offset = [] + (_etype2095, _size2092) = iprot.readListBegin() + for _i2096 in range(_size2092): + _elem2097 = iprot.readDouble() + self.offset.append(_elem2097) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.LIST: + self.offset_vel = [] + (_etype2101, _size2098) = iprot.readListBegin() + for _i2102 in range(_size2098): + _elem2103 = iprot.readDouble() + self.offset_vel.append(_elem2103) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.filter_coe = [] + (_etype2107, _size2104) = iprot.readListBegin() + for _i2108 in range(_size2104): + _elem2109 = iprot.readDouble() + self.filter_coe.append(_elem2109) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.BOOL: + self.path_length_correction = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.BOOL: + self.track_type = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_laser_track_params_args') + if self.plane is not None: + oprot.writeFieldBegin('plane', TType.I32, 1) + oprot.writeI32(self.plane) + oprot.writeFieldEnd() + if self.freq is not None: + oprot.writeFieldBegin('freq', TType.I32, 2) + oprot.writeI32(self.freq) + oprot.writeFieldEnd() + if self.lead_len is not None: + oprot.writeFieldBegin('lead_len', TType.I32, 3) + oprot.writeI32(self.lead_len) + oprot.writeFieldEnd() + if self.active is not None: + oprot.writeFieldBegin('active', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.active)) + for iter2110 in self.active: + oprot.writeDouble(iter2110) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.kp is not None: + oprot.writeFieldBegin('kp', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.kp)) + for iter2111 in self.kp: + oprot.writeDouble(iter2111) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.kd is not None: + oprot.writeFieldBegin('kd', TType.LIST, 6) + oprot.writeListBegin(TType.DOUBLE, len(self.kd)) + for iter2112 in self.kd: + oprot.writeDouble(iter2112) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.offset is not None: + oprot.writeFieldBegin('offset', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.offset)) + for iter2113 in self.offset: + oprot.writeDouble(iter2113) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.offset_vel is not None: + oprot.writeFieldBegin('offset_vel', TType.LIST, 8) + oprot.writeListBegin(TType.DOUBLE, len(self.offset_vel)) + for iter2114 in self.offset_vel: + oprot.writeDouble(iter2114) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.filter_coe is not None: + oprot.writeFieldBegin('filter_coe', TType.LIST, 9) + oprot.writeListBegin(TType.DOUBLE, len(self.filter_coe)) + for iter2115 in self.filter_coe: + oprot.writeDouble(iter2115) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.path_length_correction is not None: + oprot.writeFieldBegin('path_length_correction', TType.BOOL, 10) + oprot.writeBool(self.path_length_correction) + oprot.writeFieldEnd() + if self.track_type is not None: + oprot.writeFieldBegin('track_type', TType.BOOL, 11) + oprot.writeBool(self.track_type) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_laser_track_params_args) +set_laser_track_params_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'plane', None, None, ), # 1 + (2, TType.I32, 'freq', None, None, ), # 2 + (3, TType.I32, 'lead_len', None, None, ), # 3 + (4, TType.LIST, 'active', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'kp', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.LIST, 'kd', (TType.DOUBLE, None, False), None, ), # 6 + (7, TType.LIST, 'offset', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.LIST, 'offset_vel', (TType.DOUBLE, None, False), None, ), # 8 + (9, TType.LIST, 'filter_coe', (TType.DOUBLE, None, False), None, ), # 9 + (10, TType.BOOL, 'path_length_correction', None, None, ), # 10 + (11, TType.BOOL, 'track_type', None, None, ), # 11 +) + + +class set_laser_track_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_laser_track_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_laser_track_params_result) +set_laser_track_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_laser_track_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_laser_track_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_laser_track_args) +disable_laser_track_args.thrift_spec = ( +) + + +class disable_laser_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_laser_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_laser_track_result) +disable_laser_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class reset_laser_track_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('reset_laser_track_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(reset_laser_track_args) +reset_laser_track_args.thrift_spec = ( +) + + +class reset_laser_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('reset_laser_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(reset_laser_track_result) +reset_laser_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class write_laser_search_pos_args(object): + """ + Attributes: + - p + + """ + + + def __init__(self, p=None,): + self.p = p + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.p = [] + (_etype2119, _size2116) = iprot.readListBegin() + for _i2120 in range(_size2116): + _elem2121 = iprot.readDouble() + self.p.append(_elem2121) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_laser_search_pos_args') + if self.p is not None: + oprot.writeFieldBegin('p', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.p)) + for iter2122 in self.p: + oprot.writeDouble(iter2122) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_laser_search_pos_args) +write_laser_search_pos_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'p', (TType.DOUBLE, None, False), None, ), # 1 +) + + +class write_laser_search_pos_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_laser_search_pos_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_laser_search_pos_result) +write_laser_search_pos_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_arc_system_params_args(object): + """ + Attributes: + - coeff_K + - lag_time + - start_time + - sensi_coeff + - diff_constant + - diff_value + - voltage_track + - cal_lag_time + - arc_direction + - ref_cycle_times + + """ + + + def __init__(self, coeff_K=None, lag_time=None, start_time=None, sensi_coeff=None, diff_constant=None, diff_value=None, voltage_track=None, cal_lag_time=None, arc_direction=None, ref_cycle_times=None,): + self.coeff_K = coeff_K + self.lag_time = lag_time + self.start_time = start_time + self.sensi_coeff = sensi_coeff + self.diff_constant = diff_constant + self.diff_value = diff_value + self.voltage_track = voltage_track + self.cal_lag_time = cal_lag_time + self.arc_direction = arc_direction + self.ref_cycle_times = ref_cycle_times + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.DOUBLE: + self.coeff_K = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.lag_time = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.start_time = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.sensi_coeff = [] + (_etype2126, _size2123) = iprot.readListBegin() + for _i2127 in range(_size2123): + _elem2128 = iprot.readDouble() + self.sensi_coeff.append(_elem2128) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.diff_constant = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.diff_value = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BOOL: + self.voltage_track = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.cal_lag_time = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.BOOL: + self.arc_direction = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.I32: + self.ref_cycle_times = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_arc_system_params_args') + if self.coeff_K is not None: + oprot.writeFieldBegin('coeff_K', TType.DOUBLE, 1) + oprot.writeDouble(self.coeff_K) + oprot.writeFieldEnd() + if self.lag_time is not None: + oprot.writeFieldBegin('lag_time', TType.I32, 2) + oprot.writeI32(self.lag_time) + oprot.writeFieldEnd() + if self.start_time is not None: + oprot.writeFieldBegin('start_time', TType.I32, 3) + oprot.writeI32(self.start_time) + oprot.writeFieldEnd() + if self.sensi_coeff is not None: + oprot.writeFieldBegin('sensi_coeff', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.sensi_coeff)) + for iter2129 in self.sensi_coeff: + oprot.writeDouble(iter2129) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.diff_constant is not None: + oprot.writeFieldBegin('diff_constant', TType.DOUBLE, 5) + oprot.writeDouble(self.diff_constant) + oprot.writeFieldEnd() + if self.diff_value is not None: + oprot.writeFieldBegin('diff_value', TType.DOUBLE, 6) + oprot.writeDouble(self.diff_value) + oprot.writeFieldEnd() + if self.voltage_track is not None: + oprot.writeFieldBegin('voltage_track', TType.BOOL, 7) + oprot.writeBool(self.voltage_track) + oprot.writeFieldEnd() + if self.cal_lag_time is not None: + oprot.writeFieldBegin('cal_lag_time', TType.BOOL, 8) + oprot.writeBool(self.cal_lag_time) + oprot.writeFieldEnd() + if self.arc_direction is not None: + oprot.writeFieldBegin('arc_direction', TType.BOOL, 9) + oprot.writeBool(self.arc_direction) + oprot.writeFieldEnd() + if self.ref_cycle_times is not None: + oprot.writeFieldBegin('ref_cycle_times', TType.I32, 10) + oprot.writeI32(self.ref_cycle_times) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_arc_system_params_args) +set_arc_system_params_args.thrift_spec = ( + None, # 0 + (1, TType.DOUBLE, 'coeff_K', None, None, ), # 1 + (2, TType.I32, 'lag_time', None, None, ), # 2 + (3, TType.I32, 'start_time', None, None, ), # 3 + (4, TType.LIST, 'sensi_coeff', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.DOUBLE, 'diff_constant', None, None, ), # 5 + (6, TType.DOUBLE, 'diff_value', None, None, ), # 6 + (7, TType.BOOL, 'voltage_track', None, None, ), # 7 + (8, TType.BOOL, 'cal_lag_time', None, None, ), # 8 + (9, TType.BOOL, 'arc_direction', None, None, ), # 9 + (10, TType.I32, 'ref_cycle_times', None, None, ), # 10 +) + + +class set_arc_system_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_arc_system_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_arc_system_params_result) +set_arc_system_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_arc_track_params_args(object): + """ + Attributes: + - freq + - active + - kp + - kd + - max_displacement + - max_displacement_vel + - base_current + - track_type + - max_displacement_cycle + - constant_bias + + """ + + + def __init__(self, freq=None, active=None, kp=None, kd=None, max_displacement=None, max_displacement_vel=None, base_current=None, track_type=None, max_displacement_cycle=None, constant_bias=None,): + self.freq = freq + self.active = active + self.kp = kp + self.kd = kd + self.max_displacement = max_displacement + self.max_displacement_vel = max_displacement_vel + self.base_current = base_current + self.track_type = track_type + self.max_displacement_cycle = max_displacement_cycle + self.constant_bias = constant_bias + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.freq = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.active = [] + (_etype2133, _size2130) = iprot.readListBegin() + for _i2134 in range(_size2130): + _elem2135 = iprot.readBool() + self.active.append(_elem2135) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.kp = [] + (_etype2139, _size2136) = iprot.readListBegin() + for _i2140 in range(_size2136): + _elem2141 = iprot.readDouble() + self.kp.append(_elem2141) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.kd = [] + (_etype2145, _size2142) = iprot.readListBegin() + for _i2146 in range(_size2142): + _elem2147 = iprot.readDouble() + self.kd.append(_elem2147) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.max_displacement = [] + (_etype2151, _size2148) = iprot.readListBegin() + for _i2152 in range(_size2148): + _elem2153 = iprot.readDouble() + self.max_displacement.append(_elem2153) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.max_displacement_vel = [] + (_etype2157, _size2154) = iprot.readListBegin() + for _i2158 in range(_size2154): + _elem2159 = iprot.readDouble() + self.max_displacement_vel.append(_elem2159) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.base_current = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.track_type = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.max_displacement_cycle = [] + (_etype2163, _size2160) = iprot.readListBegin() + for _i2164 in range(_size2160): + _elem2165 = iprot.readDouble() + self.max_displacement_cycle.append(_elem2165) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.DOUBLE: + self.constant_bias = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_arc_track_params_args') + if self.freq is not None: + oprot.writeFieldBegin('freq', TType.I32, 1) + oprot.writeI32(self.freq) + oprot.writeFieldEnd() + if self.active is not None: + oprot.writeFieldBegin('active', TType.LIST, 2) + oprot.writeListBegin(TType.BOOL, len(self.active)) + for iter2166 in self.active: + oprot.writeBool(iter2166) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.kp is not None: + oprot.writeFieldBegin('kp', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.kp)) + for iter2167 in self.kp: + oprot.writeDouble(iter2167) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.kd is not None: + oprot.writeFieldBegin('kd', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.kd)) + for iter2168 in self.kd: + oprot.writeDouble(iter2168) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.max_displacement is not None: + oprot.writeFieldBegin('max_displacement', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.max_displacement)) + for iter2169 in self.max_displacement: + oprot.writeDouble(iter2169) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.max_displacement_vel is not None: + oprot.writeFieldBegin('max_displacement_vel', TType.LIST, 6) + oprot.writeListBegin(TType.DOUBLE, len(self.max_displacement_vel)) + for iter2170 in self.max_displacement_vel: + oprot.writeDouble(iter2170) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.base_current is not None: + oprot.writeFieldBegin('base_current', TType.DOUBLE, 7) + oprot.writeDouble(self.base_current) + oprot.writeFieldEnd() + if self.track_type is not None: + oprot.writeFieldBegin('track_type', TType.BOOL, 8) + oprot.writeBool(self.track_type) + oprot.writeFieldEnd() + if self.max_displacement_cycle is not None: + oprot.writeFieldBegin('max_displacement_cycle', TType.LIST, 9) + oprot.writeListBegin(TType.DOUBLE, len(self.max_displacement_cycle)) + for iter2171 in self.max_displacement_cycle: + oprot.writeDouble(iter2171) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.constant_bias is not None: + oprot.writeFieldBegin('constant_bias', TType.DOUBLE, 10) + oprot.writeDouble(self.constant_bias) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_arc_track_params_args) +set_arc_track_params_args.thrift_spec = ( + None, # 0 + (1, TType.I32, 'freq', None, None, ), # 1 + (2, TType.LIST, 'active', (TType.BOOL, None, False), None, ), # 2 + (3, TType.LIST, 'kp', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'kd', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'max_displacement', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.LIST, 'max_displacement_vel', (TType.DOUBLE, None, False), None, ), # 6 + (7, TType.DOUBLE, 'base_current', None, None, ), # 7 + (8, TType.BOOL, 'track_type', None, None, ), # 8 + (9, TType.LIST, 'max_displacement_cycle', (TType.DOUBLE, None, False), None, ), # 9 + (10, TType.DOUBLE, 'constant_bias', None, None, ), # 10 +) + + +class set_arc_track_params_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_arc_track_params_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_arc_track_params_result) +set_arc_track_params_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class enable_arc_track_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_arc_track_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_arc_track_args) +enable_arc_track_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class enable_arc_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('enable_arc_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(enable_arc_track_result) +enable_arc_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class disable_arc_track_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_arc_track_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_arc_track_args) +disable_arc_track_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class disable_arc_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('disable_arc_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(disable_arc_track_result) +disable_arc_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_arc_track_lag_time_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_arc_track_lag_time_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_arc_track_lag_time_args) +get_arc_track_lag_time_args.thrift_spec = ( +) + + +class get_arc_track_lag_time_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype2175, _size2172) = iprot.readListBegin() + for _i2176 in range(_size2172): + _elem2177 = iprot.readI32() + self.success.append(_elem2177) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_arc_track_lag_time_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.I32, len(self.success)) + for iter2178 in self.success: + oprot.writeI32(iter2178) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_arc_track_lag_time_result) +get_arc_track_lag_time_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.I32, None, False), None, ), # 0 +) + + +class reset_arc_track_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('reset_arc_track_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(reset_arc_track_args) +reset_arc_track_args.thrift_spec = ( +) + + +class reset_arc_track_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('reset_arc_track_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(reset_arc_track_result) +reset_arc_track_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class write_arc_current_data_args(object): + """ + Attributes: + - current + + """ + + + def __init__(self, current=None,): + self.current = current + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.DOUBLE: + self.current = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_arc_current_data_args') + if self.current is not None: + oprot.writeFieldBegin('current', TType.DOUBLE, 1) + oprot.writeDouble(self.current) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_arc_current_data_args) +write_arc_current_data_args.thrift_spec = ( + None, # 0 + (1, TType.DOUBLE, 'current', None, None, ), # 1 +) + + +class write_arc_current_data_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('write_arc_current_data_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(write_arc_current_data_result) +write_arc_current_data_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class clear_arc_current_data_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('clear_arc_current_data_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(clear_arc_current_data_args) +clear_arc_current_data_args.thrift_spec = ( +) + + +class clear_arc_current_data_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('clear_arc_current_data_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(clear_arc_current_data_result) +clear_arc_current_data_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class start_load_identification_args(object): + """ + Attributes: + - block + + """ + + + def __init__(self, block=None,): + self.block = block + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.block = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('start_load_identification_args') + if self.block is not None: + oprot.writeFieldBegin('block', TType.BOOL, 1) + oprot.writeBool(self.block) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(start_load_identification_args) +start_load_identification_args.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'block', None, None, ), # 1 +) + + +class start_load_identification_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('start_load_identification_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(start_load_identification_result) +start_load_identification_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_eaxis_coord_args(object): + """ + Attributes: + - eaxis_scheme_name + + """ + + + def __init__(self, eaxis_scheme_name=None,): + self.eaxis_scheme_name = eaxis_scheme_name + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.eaxis_scheme_name = [] + (_etype2182, _size2179) = iprot.readListBegin() + for _i2183 in range(_size2179): + _elem2184 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.eaxis_scheme_name.append(_elem2184) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_eaxis_coord_args') + if self.eaxis_scheme_name is not None: + oprot.writeFieldBegin('eaxis_scheme_name', TType.LIST, 1) + oprot.writeListBegin(TType.STRING, len(self.eaxis_scheme_name)) + for iter2185 in self.eaxis_scheme_name: + oprot.writeString(iter2185.encode('utf-8') if sys.version_info[0] == 2 else iter2185) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_eaxis_coord_args) +get_eaxis_coord_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'eaxis_scheme_name', (TType.STRING, 'UTF8', False), None, ), # 1 +) + + +class get_eaxis_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype2189, _size2186) = iprot.readListBegin() + for _i2190 in range(_size2186): + _elem2191 = [] + (_etype2195, _size2192) = iprot.readListBegin() + for _i2196 in range(_size2192): + _elem2197 = iprot.readDouble() + _elem2191.append(_elem2197) + iprot.readListEnd() + self.success.append(_elem2191) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_eaxis_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.LIST, len(self.success)) + for iter2198 in self.success: + oprot.writeListBegin(TType.DOUBLE, len(iter2198)) + for iter2199 in iter2198: + oprot.writeDouble(iter2199) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_eaxis_coord_result) +get_eaxis_coord_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 0 +) + + +class set_eaxis_coord_args(object): + """ + Attributes: + - eaxis_scheme_name + - coord_list + + """ + + + def __init__(self, eaxis_scheme_name=None, coord_list=None,): + self.eaxis_scheme_name = eaxis_scheme_name + self.coord_list = coord_list + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.eaxis_scheme_name = [] + (_etype2203, _size2200) = iprot.readListBegin() + for _i2204 in range(_size2200): + _elem2205 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + self.eaxis_scheme_name.append(_elem2205) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.coord_list = [] + (_etype2209, _size2206) = iprot.readListBegin() + for _i2210 in range(_size2206): + _elem2211 = [] + (_etype2215, _size2212) = iprot.readListBegin() + for _i2216 in range(_size2212): + _elem2217 = iprot.readDouble() + _elem2211.append(_elem2217) + iprot.readListEnd() + self.coord_list.append(_elem2211) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_eaxis_coord_args') + if self.eaxis_scheme_name is not None: + oprot.writeFieldBegin('eaxis_scheme_name', TType.LIST, 1) + oprot.writeListBegin(TType.STRING, len(self.eaxis_scheme_name)) + for iter2218 in self.eaxis_scheme_name: + oprot.writeString(iter2218.encode('utf-8') if sys.version_info[0] == 2 else iter2218) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.coord_list is not None: + oprot.writeFieldBegin('coord_list', TType.LIST, 2) + oprot.writeListBegin(TType.LIST, len(self.coord_list)) + for iter2219 in self.coord_list: + oprot.writeListBegin(TType.DOUBLE, len(iter2219)) + for iter2220 in iter2219: + oprot.writeDouble(iter2220) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_eaxis_coord_args) +set_eaxis_coord_args.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'eaxis_scheme_name', (TType.STRING, 'UTF8', False), None, ), # 1 + (2, TType.LIST, 'coord_list', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 2 +) + + +class set_eaxis_coord_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_eaxis_coord_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_eaxis_coord_result) +set_eaxis_coord_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_remote_tool_data_args(object): + """ + Attributes: + - name + - tool_offset + - payload + - inertia_tensor + + """ + + + def __init__(self, name=None, tool_offset=None, payload=None, inertia_tensor=None,): + self.name = name + self.tool_offset = tool_offset + self.payload = payload + self.inertia_tensor = inertia_tensor + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tool_offset = [] + (_etype2224, _size2221) = iprot.readListBegin() + for _i2225 in range(_size2221): + _elem2226 = iprot.readDouble() + self.tool_offset.append(_elem2226) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.payload = [] + (_etype2230, _size2227) = iprot.readListBegin() + for _i2231 in range(_size2227): + _elem2232 = iprot.readDouble() + self.payload.append(_elem2232) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.inertia_tensor = [] + (_etype2236, _size2233) = iprot.readListBegin() + for _i2237 in range(_size2233): + _elem2238 = iprot.readDouble() + self.inertia_tensor.append(_elem2238) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_remote_tool_data_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.tool_offset is not None: + oprot.writeFieldBegin('tool_offset', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.tool_offset)) + for iter2239 in self.tool_offset: + oprot.writeDouble(iter2239) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.payload is not None: + oprot.writeFieldBegin('payload', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.payload)) + for iter2240 in self.payload: + oprot.writeDouble(iter2240) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.inertia_tensor is not None: + oprot.writeFieldBegin('inertia_tensor', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.inertia_tensor)) + for iter2241 in self.inertia_tensor: + oprot.writeDouble(iter2241) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_remote_tool_data_args) +set_remote_tool_data_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'tool_offset', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'payload', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'inertia_tensor', (TType.DOUBLE, None, False), None, ), # 4 +) + + +class set_remote_tool_data_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_remote_tool_data_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_remote_tool_data_result) +set_remote_tool_data_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class set_remote_tool_data_workcell_args(object): + """ + Attributes: + - name + - tool_offset + - payload + - inertia_tensor + + """ + + + def __init__(self, name=None, tool_offset=None, payload=None, inertia_tensor=None,): + self.name = name + self.tool_offset = tool_offset + self.payload = payload + self.inertia_tensor = inertia_tensor + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.tool_offset = [] + (_etype2245, _size2242) = iprot.readListBegin() + for _i2246 in range(_size2242): + _elem2247 = iprot.readDouble() + self.tool_offset.append(_elem2247) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.payload = [] + (_etype2251, _size2248) = iprot.readListBegin() + for _i2252 in range(_size2248): + _elem2253 = iprot.readDouble() + self.payload.append(_elem2253) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.inertia_tensor = [] + (_etype2257, _size2254) = iprot.readListBegin() + for _i2258 in range(_size2254): + _elem2259 = iprot.readDouble() + self.inertia_tensor.append(_elem2259) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_remote_tool_data_workcell_args') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.tool_offset is not None: + oprot.writeFieldBegin('tool_offset', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.tool_offset)) + for iter2260 in self.tool_offset: + oprot.writeDouble(iter2260) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.payload is not None: + oprot.writeFieldBegin('payload', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.payload)) + for iter2261 in self.payload: + oprot.writeDouble(iter2261) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.inertia_tensor is not None: + oprot.writeFieldBegin('inertia_tensor', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.inertia_tensor)) + for iter2262 in self.inertia_tensor: + oprot.writeDouble(iter2262) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_remote_tool_data_workcell_args) +set_remote_tool_data_workcell_args.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'tool_offset', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'payload', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'inertia_tensor', (TType.DOUBLE, None, False), None, ), # 4 +) + + +class set_remote_tool_data_workcell_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.I32: + self.success = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('set_remote_tool_data_workcell_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.I32, 0) + oprot.writeI32(self.success) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(set_remote_tool_data_workcell_result) +set_remote_tool_data_workcell_result.thrift_spec = ( + (0, TType.I32, 'success', None, None, ), # 0 +) + + +class get_eaxis_scheme_info_args(object): + + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_eaxis_scheme_info_args') + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_eaxis_scheme_info_args) +get_eaxis_scheme_info_args.thrift_spec = ( +) + + +class get_eaxis_scheme_info_result(object): + """ + Attributes: + - success + + """ + + + def __init__(self, success=None,): + self.success = success + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 0: + if ftype == TType.LIST: + self.success = [] + (_etype2266, _size2263) = iprot.readListBegin() + for _i2267 in range(_size2263): + _elem2268 = EAxisSchemesParam() + _elem2268.read(iprot) + self.success.append(_elem2268) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('get_eaxis_scheme_info_result') + if self.success is not None: + oprot.writeFieldBegin('success', TType.LIST, 0) + oprot.writeListBegin(TType.STRUCT, len(self.success)) + for iter2269 in self.success: + iter2269.write(oprot) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(get_eaxis_scheme_info_result) +get_eaxis_scheme_info_result.thrift_spec = ( + (0, TType.LIST, 'success', (TType.STRUCT, [EAxisSchemesParam, None], False), None, ), # 0 +) +fix_spec(all_structs) +del all_structs + diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/__init__.py b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/__init__.py new file mode 100644 index 00000000..7dd51e7a --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/__init__.py @@ -0,0 +1 @@ +__all__ = ['ttypes', 'constants', 'RPCRobot'] diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/constants.py b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/constants.py new file mode 100644 index 00000000..bbe41d88 --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/constants.py @@ -0,0 +1,14 @@ +# +# Autogenerated by Thrift Compiler (0.13.0) +# +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING +# +# options string: py +# + +from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException +from thrift.protocol.TProtocol import TProtocolException +from thrift.TRecursive import fix_spec + +import sys +from .ttypes import * diff --git a/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/ttypes.py b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/ttypes.py new file mode 100644 index 00000000..401a1164 --- /dev/null +++ b/unilabos/devices/eit_agv/driver/DucoCobot/gen_py/robot/ttypes.py @@ -0,0 +1,3036 @@ +# +# Autogenerated by Thrift Compiler (0.13.0) +# +# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING +# +# options string: py +# + +from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException +from thrift.protocol.TProtocol import TProtocolException +from thrift.TRecursive import fix_spec + +import sys + +from thrift.transport import TTransport +all_structs = [] + + +class StateRobot(object): + SR_Start = 0 + SR_Initialize = 1 + SR_Logout = 2 + SR_Login = 3 + SR_PowerOff = 4 + SR_Disable = 5 + SR_Enable = 6 + + _VALUES_TO_NAMES = { + 0: "SR_Start", + 1: "SR_Initialize", + 2: "SR_Logout", + 3: "SR_Login", + 4: "SR_PowerOff", + 5: "SR_Disable", + 6: "SR_Enable", + } + + _NAMES_TO_VALUES = { + "SR_Start": 0, + "SR_Initialize": 1, + "SR_Logout": 2, + "SR_Login": 3, + "SR_PowerOff": 4, + "SR_Disable": 5, + "SR_Enable": 6, + } + + +class StateProgram(object): + SP_Stopped = 0 + SP_Stopping = 1 + SP_Running = 2 + SP_Paused = 3 + SP_Pausing = 4 + + _VALUES_TO_NAMES = { + 0: "SP_Stopped", + 1: "SP_Stopping", + 2: "SP_Running", + 3: "SP_Paused", + 4: "SP_Pausing", + } + + _NAMES_TO_VALUES = { + "SP_Stopped": 0, + "SP_Stopping": 1, + "SP_Running": 2, + "SP_Paused": 3, + "SP_Pausing": 4, + } + + +class OperationMode(object): + kManual = 0 + kAuto = 1 + kRemote = 2 + + _VALUES_TO_NAMES = { + 0: "kManual", + 1: "kAuto", + 2: "kRemote", + } + + _NAMES_TO_VALUES = { + "kManual": 0, + "kAuto": 1, + "kRemote": 2, + } + + +class TaskState(object): + ST_Idle = 0 + ST_Running = 1 + ST_Paused = 2 + ST_Stopped = 3 + ST_Finished = 4 + ST_Interrupt = 5 + ST_Error = 6 + ST_Illegal = 7 + ST_ParameterMismatch = 8 + + _VALUES_TO_NAMES = { + 0: "ST_Idle", + 1: "ST_Running", + 2: "ST_Paused", + 3: "ST_Stopped", + 4: "ST_Finished", + 5: "ST_Interrupt", + 6: "ST_Error", + 7: "ST_Illegal", + 8: "ST_ParameterMismatch", + } + + _NAMES_TO_VALUES = { + "ST_Idle": 0, + "ST_Running": 1, + "ST_Paused": 2, + "ST_Stopped": 3, + "ST_Finished": 4, + "ST_Interrupt": 5, + "ST_Error": 6, + "ST_Illegal": 7, + "ST_ParameterMismatch": 8, + } + + +class SafetyState(object): + SS_INIT = 0 + SS_WAIT = 2 + SS_CONFIG = 3 + SS_POWER_OFF = 4 + SS_RUN = 5 + SS_RECOVERY = 6 + SS_STOP2 = 7 + SS_STOP1 = 8 + SS_STOP0 = 9 + SS_MODEL = 10 + SS_REDUCE = 12 + SS_BOOT = 13 + SS_FAIL = 14 + SS_UPDATE = 99 + + _VALUES_TO_NAMES = { + 0: "SS_INIT", + 2: "SS_WAIT", + 3: "SS_CONFIG", + 4: "SS_POWER_OFF", + 5: "SS_RUN", + 6: "SS_RECOVERY", + 7: "SS_STOP2", + 8: "SS_STOP1", + 9: "SS_STOP0", + 10: "SS_MODEL", + 12: "SS_REDUCE", + 13: "SS_BOOT", + 14: "SS_FAIL", + 99: "SS_UPDATE", + } + + _NAMES_TO_VALUES = { + "SS_INIT": 0, + "SS_WAIT": 2, + "SS_CONFIG": 3, + "SS_POWER_OFF": 4, + "SS_RUN": 5, + "SS_RECOVERY": 6, + "SS_STOP2": 7, + "SS_STOP1": 8, + "SS_STOP0": 9, + "SS_MODEL": 10, + "SS_REDUCE": 12, + "SS_BOOT": 13, + "SS_FAIL": 14, + "SS_UPDATE": 99, + } + + +class Op(object): + """ + Attributes: + - time_or_dist_1 + - trig_io_1 + - trig_value_1 + - trig_time_1 + - trig_dist_1 + - trig_event_1 + - time_or_dist_2 + - trig_io_2 + - trig_value_2 + - trig_time_2 + - trig_dist_2 + - trig_event_2 + - time_or_dist_3 + - trig_io_3 + - trig_value_3 + - trig_time_3 + - trig_dist_3 + - trig_event_3 + + """ + + + def __init__(self, time_or_dist_1=None, trig_io_1=None, trig_value_1=None, trig_time_1=None, trig_dist_1=None, trig_event_1=None, time_or_dist_2=None, trig_io_2=None, trig_value_2=None, trig_time_2=None, trig_dist_2=None, trig_event_2=None, time_or_dist_3=None, trig_io_3=None, trig_value_3=None, trig_time_3=None, trig_dist_3=None, trig_event_3=None,): + self.time_or_dist_1 = time_or_dist_1 + self.trig_io_1 = trig_io_1 + self.trig_value_1 = trig_value_1 + self.trig_time_1 = trig_time_1 + self.trig_dist_1 = trig_dist_1 + self.trig_event_1 = trig_event_1 + self.time_or_dist_2 = time_or_dist_2 + self.trig_io_2 = trig_io_2 + self.trig_value_2 = trig_value_2 + self.trig_time_2 = trig_time_2 + self.trig_dist_2 = trig_dist_2 + self.trig_event_2 = trig_event_2 + self.time_or_dist_3 = time_or_dist_3 + self.trig_io_3 = trig_io_3 + self.trig_value_3 = trig_value_3 + self.trig_time_3 = trig_time_3 + self.trig_dist_3 = trig_dist_3 + self.trig_event_3 = trig_event_3 + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BYTE: + self.time_or_dist_1 = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.BYTE: + self.trig_io_1 = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.BOOL: + self.trig_value_1 = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.trig_time_1 = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.trig_dist_1 = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.STRING: + self.trig_event_1 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BYTE: + self.time_or_dist_2 = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BYTE: + self.trig_io_2 = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.BOOL: + self.trig_value_2 = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.DOUBLE: + self.trig_time_2 = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.DOUBLE: + self.trig_dist_2 = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.STRING: + self.trig_event_2 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.BYTE: + self.time_or_dist_3 = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 14: + if ftype == TType.BYTE: + self.trig_io_3 = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 15: + if ftype == TType.BOOL: + self.trig_value_3 = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 16: + if ftype == TType.DOUBLE: + self.trig_time_3 = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 17: + if ftype == TType.DOUBLE: + self.trig_dist_3 = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 18: + if ftype == TType.STRING: + self.trig_event_3 = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('Op') + if self.time_or_dist_1 is not None: + oprot.writeFieldBegin('time_or_dist_1', TType.BYTE, 1) + oprot.writeByte(self.time_or_dist_1) + oprot.writeFieldEnd() + if self.trig_io_1 is not None: + oprot.writeFieldBegin('trig_io_1', TType.BYTE, 2) + oprot.writeByte(self.trig_io_1) + oprot.writeFieldEnd() + if self.trig_value_1 is not None: + oprot.writeFieldBegin('trig_value_1', TType.BOOL, 3) + oprot.writeBool(self.trig_value_1) + oprot.writeFieldEnd() + if self.trig_time_1 is not None: + oprot.writeFieldBegin('trig_time_1', TType.DOUBLE, 4) + oprot.writeDouble(self.trig_time_1) + oprot.writeFieldEnd() + if self.trig_dist_1 is not None: + oprot.writeFieldBegin('trig_dist_1', TType.DOUBLE, 5) + oprot.writeDouble(self.trig_dist_1) + oprot.writeFieldEnd() + if self.trig_event_1 is not None: + oprot.writeFieldBegin('trig_event_1', TType.STRING, 6) + oprot.writeString(self.trig_event_1.encode('utf-8') if sys.version_info[0] == 2 else self.trig_event_1) + oprot.writeFieldEnd() + if self.time_or_dist_2 is not None: + oprot.writeFieldBegin('time_or_dist_2', TType.BYTE, 7) + oprot.writeByte(self.time_or_dist_2) + oprot.writeFieldEnd() + if self.trig_io_2 is not None: + oprot.writeFieldBegin('trig_io_2', TType.BYTE, 8) + oprot.writeByte(self.trig_io_2) + oprot.writeFieldEnd() + if self.trig_value_2 is not None: + oprot.writeFieldBegin('trig_value_2', TType.BOOL, 9) + oprot.writeBool(self.trig_value_2) + oprot.writeFieldEnd() + if self.trig_time_2 is not None: + oprot.writeFieldBegin('trig_time_2', TType.DOUBLE, 10) + oprot.writeDouble(self.trig_time_2) + oprot.writeFieldEnd() + if self.trig_dist_2 is not None: + oprot.writeFieldBegin('trig_dist_2', TType.DOUBLE, 11) + oprot.writeDouble(self.trig_dist_2) + oprot.writeFieldEnd() + if self.trig_event_2 is not None: + oprot.writeFieldBegin('trig_event_2', TType.STRING, 12) + oprot.writeString(self.trig_event_2.encode('utf-8') if sys.version_info[0] == 2 else self.trig_event_2) + oprot.writeFieldEnd() + if self.time_or_dist_3 is not None: + oprot.writeFieldBegin('time_or_dist_3', TType.BYTE, 13) + oprot.writeByte(self.time_or_dist_3) + oprot.writeFieldEnd() + if self.trig_io_3 is not None: + oprot.writeFieldBegin('trig_io_3', TType.BYTE, 14) + oprot.writeByte(self.trig_io_3) + oprot.writeFieldEnd() + if self.trig_value_3 is not None: + oprot.writeFieldBegin('trig_value_3', TType.BOOL, 15) + oprot.writeBool(self.trig_value_3) + oprot.writeFieldEnd() + if self.trig_time_3 is not None: + oprot.writeFieldBegin('trig_time_3', TType.DOUBLE, 16) + oprot.writeDouble(self.trig_time_3) + oprot.writeFieldEnd() + if self.trig_dist_3 is not None: + oprot.writeFieldBegin('trig_dist_3', TType.DOUBLE, 17) + oprot.writeDouble(self.trig_dist_3) + oprot.writeFieldEnd() + if self.trig_event_3 is not None: + oprot.writeFieldBegin('trig_event_3', TType.STRING, 18) + oprot.writeString(self.trig_event_3.encode('utf-8') if sys.version_info[0] == 2 else self.trig_event_3) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.time_or_dist_1 is None: + raise TProtocolException(message='Required field time_or_dist_1 is unset!') + if self.trig_io_1 is None: + raise TProtocolException(message='Required field trig_io_1 is unset!') + if self.trig_value_1 is None: + raise TProtocolException(message='Required field trig_value_1 is unset!') + if self.trig_time_1 is None: + raise TProtocolException(message='Required field trig_time_1 is unset!') + if self.trig_dist_1 is None: + raise TProtocolException(message='Required field trig_dist_1 is unset!') + if self.trig_event_1 is None: + raise TProtocolException(message='Required field trig_event_1 is unset!') + if self.time_or_dist_2 is None: + raise TProtocolException(message='Required field time_or_dist_2 is unset!') + if self.trig_io_2 is None: + raise TProtocolException(message='Required field trig_io_2 is unset!') + if self.trig_value_2 is None: + raise TProtocolException(message='Required field trig_value_2 is unset!') + if self.trig_time_2 is None: + raise TProtocolException(message='Required field trig_time_2 is unset!') + if self.trig_dist_2 is None: + raise TProtocolException(message='Required field trig_dist_2 is unset!') + if self.trig_event_2 is None: + raise TProtocolException(message='Required field trig_event_2 is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class RobotStatus(object): + """ + Attributes: + - jointExpectPosition + - jointExpectVelocity + - jointExpectAccelera + - jointActualPosition + - jointActualVelocity + - jointActualAccelera + - jointActualCurrent + - jointTemperature + - driverTemperature + - cartExpectPosition + - cartExpectVelocity + - cartExpectAccelera + - cartActualPosition + - cartActualVelocity + - cartActualAccelera + - slaveReady + - collision + - collisionAxis + - emcStopSignal + - robotState + - robotError + - jointAuxiliaryPosition + - jointDynamicTorque + - jointGravityTorque + - jointActualTorque + - jointExtraTorque + + """ + + + def __init__(self, jointExpectPosition=None, jointExpectVelocity=None, jointExpectAccelera=None, jointActualPosition=None, jointActualVelocity=None, jointActualAccelera=None, jointActualCurrent=None, jointTemperature=None, driverTemperature=None, cartExpectPosition=None, cartExpectVelocity=None, cartExpectAccelera=None, cartActualPosition=None, cartActualVelocity=None, cartActualAccelera=None, slaveReady=None, collision=None, collisionAxis=None, emcStopSignal=None, robotState=None, robotError=None, jointAuxiliaryPosition=None, jointDynamicTorque=None, jointGravityTorque=None, jointActualTorque=None, jointExtraTorque=None,): + self.jointExpectPosition = jointExpectPosition + self.jointExpectVelocity = jointExpectVelocity + self.jointExpectAccelera = jointExpectAccelera + self.jointActualPosition = jointActualPosition + self.jointActualVelocity = jointActualVelocity + self.jointActualAccelera = jointActualAccelera + self.jointActualCurrent = jointActualCurrent + self.jointTemperature = jointTemperature + self.driverTemperature = driverTemperature + self.cartExpectPosition = cartExpectPosition + self.cartExpectVelocity = cartExpectVelocity + self.cartExpectAccelera = cartExpectAccelera + self.cartActualPosition = cartActualPosition + self.cartActualVelocity = cartActualVelocity + self.cartActualAccelera = cartActualAccelera + self.slaveReady = slaveReady + self.collision = collision + self.collisionAxis = collisionAxis + self.emcStopSignal = emcStopSignal + self.robotState = robotState + self.robotError = robotError + self.jointAuxiliaryPosition = jointAuxiliaryPosition + self.jointDynamicTorque = jointDynamicTorque + self.jointGravityTorque = jointGravityTorque + self.jointActualTorque = jointActualTorque + self.jointExtraTorque = jointExtraTorque + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.jointExpectPosition = [] + (_etype3, _size0) = iprot.readListBegin() + for _i4 in range(_size0): + _elem5 = iprot.readDouble() + self.jointExpectPosition.append(_elem5) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.jointExpectVelocity = [] + (_etype9, _size6) = iprot.readListBegin() + for _i10 in range(_size6): + _elem11 = iprot.readDouble() + self.jointExpectVelocity.append(_elem11) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.jointExpectAccelera = [] + (_etype15, _size12) = iprot.readListBegin() + for _i16 in range(_size12): + _elem17 = iprot.readDouble() + self.jointExpectAccelera.append(_elem17) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.jointActualPosition = [] + (_etype21, _size18) = iprot.readListBegin() + for _i22 in range(_size18): + _elem23 = iprot.readDouble() + self.jointActualPosition.append(_elem23) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.jointActualVelocity = [] + (_etype27, _size24) = iprot.readListBegin() + for _i28 in range(_size24): + _elem29 = iprot.readDouble() + self.jointActualVelocity.append(_elem29) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.jointActualAccelera = [] + (_etype33, _size30) = iprot.readListBegin() + for _i34 in range(_size30): + _elem35 = iprot.readDouble() + self.jointActualAccelera.append(_elem35) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.jointActualCurrent = [] + (_etype39, _size36) = iprot.readListBegin() + for _i40 in range(_size36): + _elem41 = iprot.readDouble() + self.jointActualCurrent.append(_elem41) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.LIST: + self.jointTemperature = [] + (_etype45, _size42) = iprot.readListBegin() + for _i46 in range(_size42): + _elem47 = iprot.readDouble() + self.jointTemperature.append(_elem47) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.driverTemperature = [] + (_etype51, _size48) = iprot.readListBegin() + for _i52 in range(_size48): + _elem53 = iprot.readDouble() + self.driverTemperature.append(_elem53) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.LIST: + self.cartExpectPosition = [] + (_etype57, _size54) = iprot.readListBegin() + for _i58 in range(_size54): + _elem59 = iprot.readDouble() + self.cartExpectPosition.append(_elem59) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.LIST: + self.cartExpectVelocity = [] + (_etype63, _size60) = iprot.readListBegin() + for _i64 in range(_size60): + _elem65 = iprot.readDouble() + self.cartExpectVelocity.append(_elem65) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.LIST: + self.cartExpectAccelera = [] + (_etype69, _size66) = iprot.readListBegin() + for _i70 in range(_size66): + _elem71 = iprot.readDouble() + self.cartExpectAccelera.append(_elem71) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.LIST: + self.cartActualPosition = [] + (_etype75, _size72) = iprot.readListBegin() + for _i76 in range(_size72): + _elem77 = iprot.readDouble() + self.cartActualPosition.append(_elem77) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 14: + if ftype == TType.LIST: + self.cartActualVelocity = [] + (_etype81, _size78) = iprot.readListBegin() + for _i82 in range(_size78): + _elem83 = iprot.readDouble() + self.cartActualVelocity.append(_elem83) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 15: + if ftype == TType.LIST: + self.cartActualAccelera = [] + (_etype87, _size84) = iprot.readListBegin() + for _i88 in range(_size84): + _elem89 = iprot.readDouble() + self.cartActualAccelera.append(_elem89) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 16: + if ftype == TType.LIST: + self.slaveReady = [] + (_etype93, _size90) = iprot.readListBegin() + for _i94 in range(_size90): + _elem95 = iprot.readBool() + self.slaveReady.append(_elem95) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 17: + if ftype == TType.BOOL: + self.collision = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 18: + if ftype == TType.BYTE: + self.collisionAxis = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 19: + if ftype == TType.BOOL: + self.emcStopSignal = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 20: + if ftype == TType.BYTE: + self.robotState = iprot.readByte() + else: + iprot.skip(ftype) + elif fid == 21: + if ftype == TType.I32: + self.robotError = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 22: + if ftype == TType.LIST: + self.jointAuxiliaryPosition = [] + (_etype99, _size96) = iprot.readListBegin() + for _i100 in range(_size96): + _elem101 = iprot.readDouble() + self.jointAuxiliaryPosition.append(_elem101) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 23: + if ftype == TType.LIST: + self.jointDynamicTorque = [] + (_etype105, _size102) = iprot.readListBegin() + for _i106 in range(_size102): + _elem107 = iprot.readDouble() + self.jointDynamicTorque.append(_elem107) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 24: + if ftype == TType.LIST: + self.jointGravityTorque = [] + (_etype111, _size108) = iprot.readListBegin() + for _i112 in range(_size108): + _elem113 = iprot.readDouble() + self.jointGravityTorque.append(_elem113) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 25: + if ftype == TType.LIST: + self.jointActualTorque = [] + (_etype117, _size114) = iprot.readListBegin() + for _i118 in range(_size114): + _elem119 = iprot.readDouble() + self.jointActualTorque.append(_elem119) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 26: + if ftype == TType.LIST: + self.jointExtraTorque = [] + (_etype123, _size120) = iprot.readListBegin() + for _i124 in range(_size120): + _elem125 = iprot.readDouble() + self.jointExtraTorque.append(_elem125) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('RobotStatus') + if self.jointExpectPosition is not None: + oprot.writeFieldBegin('jointExpectPosition', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.jointExpectPosition)) + for iter126 in self.jointExpectPosition: + oprot.writeDouble(iter126) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointExpectVelocity is not None: + oprot.writeFieldBegin('jointExpectVelocity', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.jointExpectVelocity)) + for iter127 in self.jointExpectVelocity: + oprot.writeDouble(iter127) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointExpectAccelera is not None: + oprot.writeFieldBegin('jointExpectAccelera', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.jointExpectAccelera)) + for iter128 in self.jointExpectAccelera: + oprot.writeDouble(iter128) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointActualPosition is not None: + oprot.writeFieldBegin('jointActualPosition', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.jointActualPosition)) + for iter129 in self.jointActualPosition: + oprot.writeDouble(iter129) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointActualVelocity is not None: + oprot.writeFieldBegin('jointActualVelocity', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.jointActualVelocity)) + for iter130 in self.jointActualVelocity: + oprot.writeDouble(iter130) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointActualAccelera is not None: + oprot.writeFieldBegin('jointActualAccelera', TType.LIST, 6) + oprot.writeListBegin(TType.DOUBLE, len(self.jointActualAccelera)) + for iter131 in self.jointActualAccelera: + oprot.writeDouble(iter131) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointActualCurrent is not None: + oprot.writeFieldBegin('jointActualCurrent', TType.LIST, 7) + oprot.writeListBegin(TType.DOUBLE, len(self.jointActualCurrent)) + for iter132 in self.jointActualCurrent: + oprot.writeDouble(iter132) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointTemperature is not None: + oprot.writeFieldBegin('jointTemperature', TType.LIST, 8) + oprot.writeListBegin(TType.DOUBLE, len(self.jointTemperature)) + for iter133 in self.jointTemperature: + oprot.writeDouble(iter133) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.driverTemperature is not None: + oprot.writeFieldBegin('driverTemperature', TType.LIST, 9) + oprot.writeListBegin(TType.DOUBLE, len(self.driverTemperature)) + for iter134 in self.driverTemperature: + oprot.writeDouble(iter134) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cartExpectPosition is not None: + oprot.writeFieldBegin('cartExpectPosition', TType.LIST, 10) + oprot.writeListBegin(TType.DOUBLE, len(self.cartExpectPosition)) + for iter135 in self.cartExpectPosition: + oprot.writeDouble(iter135) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cartExpectVelocity is not None: + oprot.writeFieldBegin('cartExpectVelocity', TType.LIST, 11) + oprot.writeListBegin(TType.DOUBLE, len(self.cartExpectVelocity)) + for iter136 in self.cartExpectVelocity: + oprot.writeDouble(iter136) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cartExpectAccelera is not None: + oprot.writeFieldBegin('cartExpectAccelera', TType.LIST, 12) + oprot.writeListBegin(TType.DOUBLE, len(self.cartExpectAccelera)) + for iter137 in self.cartExpectAccelera: + oprot.writeDouble(iter137) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cartActualPosition is not None: + oprot.writeFieldBegin('cartActualPosition', TType.LIST, 13) + oprot.writeListBegin(TType.DOUBLE, len(self.cartActualPosition)) + for iter138 in self.cartActualPosition: + oprot.writeDouble(iter138) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cartActualVelocity is not None: + oprot.writeFieldBegin('cartActualVelocity', TType.LIST, 14) + oprot.writeListBegin(TType.DOUBLE, len(self.cartActualVelocity)) + for iter139 in self.cartActualVelocity: + oprot.writeDouble(iter139) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cartActualAccelera is not None: + oprot.writeFieldBegin('cartActualAccelera', TType.LIST, 15) + oprot.writeListBegin(TType.DOUBLE, len(self.cartActualAccelera)) + for iter140 in self.cartActualAccelera: + oprot.writeDouble(iter140) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.slaveReady is not None: + oprot.writeFieldBegin('slaveReady', TType.LIST, 16) + oprot.writeListBegin(TType.BOOL, len(self.slaveReady)) + for iter141 in self.slaveReady: + oprot.writeBool(iter141) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.collision is not None: + oprot.writeFieldBegin('collision', TType.BOOL, 17) + oprot.writeBool(self.collision) + oprot.writeFieldEnd() + if self.collisionAxis is not None: + oprot.writeFieldBegin('collisionAxis', TType.BYTE, 18) + oprot.writeByte(self.collisionAxis) + oprot.writeFieldEnd() + if self.emcStopSignal is not None: + oprot.writeFieldBegin('emcStopSignal', TType.BOOL, 19) + oprot.writeBool(self.emcStopSignal) + oprot.writeFieldEnd() + if self.robotState is not None: + oprot.writeFieldBegin('robotState', TType.BYTE, 20) + oprot.writeByte(self.robotState) + oprot.writeFieldEnd() + if self.robotError is not None: + oprot.writeFieldBegin('robotError', TType.I32, 21) + oprot.writeI32(self.robotError) + oprot.writeFieldEnd() + if self.jointAuxiliaryPosition is not None: + oprot.writeFieldBegin('jointAuxiliaryPosition', TType.LIST, 22) + oprot.writeListBegin(TType.DOUBLE, len(self.jointAuxiliaryPosition)) + for iter142 in self.jointAuxiliaryPosition: + oprot.writeDouble(iter142) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointDynamicTorque is not None: + oprot.writeFieldBegin('jointDynamicTorque', TType.LIST, 23) + oprot.writeListBegin(TType.DOUBLE, len(self.jointDynamicTorque)) + for iter143 in self.jointDynamicTorque: + oprot.writeDouble(iter143) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointGravityTorque is not None: + oprot.writeFieldBegin('jointGravityTorque', TType.LIST, 24) + oprot.writeListBegin(TType.DOUBLE, len(self.jointGravityTorque)) + for iter144 in self.jointGravityTorque: + oprot.writeDouble(iter144) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointActualTorque is not None: + oprot.writeFieldBegin('jointActualTorque', TType.LIST, 25) + oprot.writeListBegin(TType.DOUBLE, len(self.jointActualTorque)) + for iter145 in self.jointActualTorque: + oprot.writeDouble(iter145) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.jointExtraTorque is not None: + oprot.writeFieldBegin('jointExtraTorque', TType.LIST, 26) + oprot.writeListBegin(TType.DOUBLE, len(self.jointExtraTorque)) + for iter146 in self.jointExtraTorque: + oprot.writeDouble(iter146) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.jointExpectPosition is None: + raise TProtocolException(message='Required field jointExpectPosition is unset!') + if self.jointExpectVelocity is None: + raise TProtocolException(message='Required field jointExpectVelocity is unset!') + if self.jointExpectAccelera is None: + raise TProtocolException(message='Required field jointExpectAccelera is unset!') + if self.jointActualPosition is None: + raise TProtocolException(message='Required field jointActualPosition is unset!') + if self.jointActualVelocity is None: + raise TProtocolException(message='Required field jointActualVelocity is unset!') + if self.jointActualAccelera is None: + raise TProtocolException(message='Required field jointActualAccelera is unset!') + if self.jointActualCurrent is None: + raise TProtocolException(message='Required field jointActualCurrent is unset!') + if self.jointTemperature is None: + raise TProtocolException(message='Required field jointTemperature is unset!') + if self.driverTemperature is None: + raise TProtocolException(message='Required field driverTemperature is unset!') + if self.cartExpectPosition is None: + raise TProtocolException(message='Required field cartExpectPosition is unset!') + if self.cartExpectVelocity is None: + raise TProtocolException(message='Required field cartExpectVelocity is unset!') + if self.cartExpectAccelera is None: + raise TProtocolException(message='Required field cartExpectAccelera is unset!') + if self.cartActualPosition is None: + raise TProtocolException(message='Required field cartActualPosition is unset!') + if self.cartActualVelocity is None: + raise TProtocolException(message='Required field cartActualVelocity is unset!') + if self.cartActualAccelera is None: + raise TProtocolException(message='Required field cartActualAccelera is unset!') + if self.slaveReady is None: + raise TProtocolException(message='Required field slaveReady is unset!') + if self.collision is None: + raise TProtocolException(message='Required field collision is unset!') + if self.collisionAxis is None: + raise TProtocolException(message='Required field collisionAxis is unset!') + if self.emcStopSignal is None: + raise TProtocolException(message='Required field emcStopSignal is unset!') + if self.robotState is None: + raise TProtocolException(message='Required field robotState is unset!') + if self.robotError is None: + raise TProtocolException(message='Required field robotError is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class IOStatus(object): + """ + Attributes: + - analogCurrentOutputs + - analogVoltageOutputs + - analogCurrentInputs + - analogVoltageInputs + - digitalInputs + - digitalOutputs + - toolIOIn + - toolIOOut + - toolButton + - funRegisterInputs + - funRegisterOutputs + - boolRegisterInputs + - boolRegisterOutputs + - wordRegisterInputs + - wordRegisterOutputs + - floatRegisterInputs + - floatRegisterOutputs + + """ + + + def __init__(self, analogCurrentOutputs=None, analogVoltageOutputs=None, analogCurrentInputs=None, analogVoltageInputs=None, digitalInputs=None, digitalOutputs=None, toolIOIn=None, toolIOOut=None, toolButton=None, funRegisterInputs=None, funRegisterOutputs=None, boolRegisterInputs=None, boolRegisterOutputs=None, wordRegisterInputs=None, wordRegisterOutputs=None, floatRegisterInputs=None, floatRegisterOutputs=None,): + self.analogCurrentOutputs = analogCurrentOutputs + self.analogVoltageOutputs = analogVoltageOutputs + self.analogCurrentInputs = analogCurrentInputs + self.analogVoltageInputs = analogVoltageInputs + self.digitalInputs = digitalInputs + self.digitalOutputs = digitalOutputs + self.toolIOIn = toolIOIn + self.toolIOOut = toolIOOut + self.toolButton = toolButton + self.funRegisterInputs = funRegisterInputs + self.funRegisterOutputs = funRegisterOutputs + self.boolRegisterInputs = boolRegisterInputs + self.boolRegisterOutputs = boolRegisterOutputs + self.wordRegisterInputs = wordRegisterInputs + self.wordRegisterOutputs = wordRegisterOutputs + self.floatRegisterInputs = floatRegisterInputs + self.floatRegisterOutputs = floatRegisterOutputs + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.analogCurrentOutputs = [] + (_etype150, _size147) = iprot.readListBegin() + for _i151 in range(_size147): + _elem152 = iprot.readDouble() + self.analogCurrentOutputs.append(_elem152) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.analogVoltageOutputs = [] + (_etype156, _size153) = iprot.readListBegin() + for _i157 in range(_size153): + _elem158 = iprot.readDouble() + self.analogVoltageOutputs.append(_elem158) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.analogCurrentInputs = [] + (_etype162, _size159) = iprot.readListBegin() + for _i163 in range(_size159): + _elem164 = iprot.readDouble() + self.analogCurrentInputs.append(_elem164) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.analogVoltageInputs = [] + (_etype168, _size165) = iprot.readListBegin() + for _i169 in range(_size165): + _elem170 = iprot.readDouble() + self.analogVoltageInputs.append(_elem170) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.digitalInputs = [] + (_etype174, _size171) = iprot.readListBegin() + for _i175 in range(_size171): + _elem176 = iprot.readBool() + self.digitalInputs.append(_elem176) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.digitalOutputs = [] + (_etype180, _size177) = iprot.readListBegin() + for _i181 in range(_size177): + _elem182 = iprot.readBool() + self.digitalOutputs.append(_elem182) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.LIST: + self.toolIOIn = [] + (_etype186, _size183) = iprot.readListBegin() + for _i187 in range(_size183): + _elem188 = iprot.readBool() + self.toolIOIn.append(_elem188) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.LIST: + self.toolIOOut = [] + (_etype192, _size189) = iprot.readListBegin() + for _i193 in range(_size189): + _elem194 = iprot.readBool() + self.toolIOOut.append(_elem194) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.LIST: + self.toolButton = [] + (_etype198, _size195) = iprot.readListBegin() + for _i199 in range(_size195): + _elem200 = iprot.readBool() + self.toolButton.append(_elem200) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.LIST: + self.funRegisterInputs = [] + (_etype204, _size201) = iprot.readListBegin() + for _i205 in range(_size201): + _elem206 = iprot.readBool() + self.funRegisterInputs.append(_elem206) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.LIST: + self.funRegisterOutputs = [] + (_etype210, _size207) = iprot.readListBegin() + for _i211 in range(_size207): + _elem212 = iprot.readBool() + self.funRegisterOutputs.append(_elem212) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.LIST: + self.boolRegisterInputs = [] + (_etype216, _size213) = iprot.readListBegin() + for _i217 in range(_size213): + _elem218 = iprot.readBool() + self.boolRegisterInputs.append(_elem218) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.LIST: + self.boolRegisterOutputs = [] + (_etype222, _size219) = iprot.readListBegin() + for _i223 in range(_size219): + _elem224 = iprot.readBool() + self.boolRegisterOutputs.append(_elem224) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 14: + if ftype == TType.LIST: + self.wordRegisterInputs = [] + (_etype228, _size225) = iprot.readListBegin() + for _i229 in range(_size225): + _elem230 = iprot.readI16() + self.wordRegisterInputs.append(_elem230) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 15: + if ftype == TType.LIST: + self.wordRegisterOutputs = [] + (_etype234, _size231) = iprot.readListBegin() + for _i235 in range(_size231): + _elem236 = iprot.readI16() + self.wordRegisterOutputs.append(_elem236) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 16: + if ftype == TType.LIST: + self.floatRegisterInputs = [] + (_etype240, _size237) = iprot.readListBegin() + for _i241 in range(_size237): + _elem242 = iprot.readDouble() + self.floatRegisterInputs.append(_elem242) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 17: + if ftype == TType.LIST: + self.floatRegisterOutputs = [] + (_etype246, _size243) = iprot.readListBegin() + for _i247 in range(_size243): + _elem248 = iprot.readDouble() + self.floatRegisterOutputs.append(_elem248) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('IOStatus') + if self.analogCurrentOutputs is not None: + oprot.writeFieldBegin('analogCurrentOutputs', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.analogCurrentOutputs)) + for iter249 in self.analogCurrentOutputs: + oprot.writeDouble(iter249) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.analogVoltageOutputs is not None: + oprot.writeFieldBegin('analogVoltageOutputs', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.analogVoltageOutputs)) + for iter250 in self.analogVoltageOutputs: + oprot.writeDouble(iter250) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.analogCurrentInputs is not None: + oprot.writeFieldBegin('analogCurrentInputs', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.analogCurrentInputs)) + for iter251 in self.analogCurrentInputs: + oprot.writeDouble(iter251) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.analogVoltageInputs is not None: + oprot.writeFieldBegin('analogVoltageInputs', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.analogVoltageInputs)) + for iter252 in self.analogVoltageInputs: + oprot.writeDouble(iter252) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.digitalInputs is not None: + oprot.writeFieldBegin('digitalInputs', TType.LIST, 5) + oprot.writeListBegin(TType.BOOL, len(self.digitalInputs)) + for iter253 in self.digitalInputs: + oprot.writeBool(iter253) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.digitalOutputs is not None: + oprot.writeFieldBegin('digitalOutputs', TType.LIST, 6) + oprot.writeListBegin(TType.BOOL, len(self.digitalOutputs)) + for iter254 in self.digitalOutputs: + oprot.writeBool(iter254) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.toolIOIn is not None: + oprot.writeFieldBegin('toolIOIn', TType.LIST, 7) + oprot.writeListBegin(TType.BOOL, len(self.toolIOIn)) + for iter255 in self.toolIOIn: + oprot.writeBool(iter255) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.toolIOOut is not None: + oprot.writeFieldBegin('toolIOOut', TType.LIST, 8) + oprot.writeListBegin(TType.BOOL, len(self.toolIOOut)) + for iter256 in self.toolIOOut: + oprot.writeBool(iter256) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.toolButton is not None: + oprot.writeFieldBegin('toolButton', TType.LIST, 9) + oprot.writeListBegin(TType.BOOL, len(self.toolButton)) + for iter257 in self.toolButton: + oprot.writeBool(iter257) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.funRegisterInputs is not None: + oprot.writeFieldBegin('funRegisterInputs', TType.LIST, 10) + oprot.writeListBegin(TType.BOOL, len(self.funRegisterInputs)) + for iter258 in self.funRegisterInputs: + oprot.writeBool(iter258) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.funRegisterOutputs is not None: + oprot.writeFieldBegin('funRegisterOutputs', TType.LIST, 11) + oprot.writeListBegin(TType.BOOL, len(self.funRegisterOutputs)) + for iter259 in self.funRegisterOutputs: + oprot.writeBool(iter259) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.boolRegisterInputs is not None: + oprot.writeFieldBegin('boolRegisterInputs', TType.LIST, 12) + oprot.writeListBegin(TType.BOOL, len(self.boolRegisterInputs)) + for iter260 in self.boolRegisterInputs: + oprot.writeBool(iter260) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.boolRegisterOutputs is not None: + oprot.writeFieldBegin('boolRegisterOutputs', TType.LIST, 13) + oprot.writeListBegin(TType.BOOL, len(self.boolRegisterOutputs)) + for iter261 in self.boolRegisterOutputs: + oprot.writeBool(iter261) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.wordRegisterInputs is not None: + oprot.writeFieldBegin('wordRegisterInputs', TType.LIST, 14) + oprot.writeListBegin(TType.I16, len(self.wordRegisterInputs)) + for iter262 in self.wordRegisterInputs: + oprot.writeI16(iter262) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.wordRegisterOutputs is not None: + oprot.writeFieldBegin('wordRegisterOutputs', TType.LIST, 15) + oprot.writeListBegin(TType.I16, len(self.wordRegisterOutputs)) + for iter263 in self.wordRegisterOutputs: + oprot.writeI16(iter263) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.floatRegisterInputs is not None: + oprot.writeFieldBegin('floatRegisterInputs', TType.LIST, 16) + oprot.writeListBegin(TType.DOUBLE, len(self.floatRegisterInputs)) + for iter264 in self.floatRegisterInputs: + oprot.writeDouble(iter264) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.floatRegisterOutputs is not None: + oprot.writeFieldBegin('floatRegisterOutputs', TType.LIST, 17) + oprot.writeListBegin(TType.DOUBLE, len(self.floatRegisterOutputs)) + for iter265 in self.floatRegisterOutputs: + oprot.writeDouble(iter265) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.analogCurrentOutputs is None: + raise TProtocolException(message='Required field analogCurrentOutputs is unset!') + if self.analogVoltageOutputs is None: + raise TProtocolException(message='Required field analogVoltageOutputs is unset!') + if self.analogCurrentInputs is None: + raise TProtocolException(message='Required field analogCurrentInputs is unset!') + if self.analogVoltageInputs is None: + raise TProtocolException(message='Required field analogVoltageInputs is unset!') + if self.digitalInputs is None: + raise TProtocolException(message='Required field digitalInputs is unset!') + if self.digitalOutputs is None: + raise TProtocolException(message='Required field digitalOutputs is unset!') + if self.toolIOIn is None: + raise TProtocolException(message='Required field toolIOIn is unset!') + if self.toolIOOut is None: + raise TProtocolException(message='Required field toolIOOut is unset!') + if self.toolButton is None: + raise TProtocolException(message='Required field toolButton is unset!') + if self.funRegisterInputs is None: + raise TProtocolException(message='Required field funRegisterInputs is unset!') + if self.funRegisterOutputs is None: + raise TProtocolException(message='Required field funRegisterOutputs is unset!') + if self.boolRegisterInputs is None: + raise TProtocolException(message='Required field boolRegisterInputs is unset!') + if self.boolRegisterOutputs is None: + raise TProtocolException(message='Required field boolRegisterOutputs is unset!') + if self.wordRegisterInputs is None: + raise TProtocolException(message='Required field wordRegisterInputs is unset!') + if self.wordRegisterOutputs is None: + raise TProtocolException(message='Required field wordRegisterOutputs is unset!') + if self.floatRegisterInputs is None: + raise TProtocolException(message='Required field floatRegisterInputs is unset!') + if self.floatRegisterOutputs is None: + raise TProtocolException(message='Required field floatRegisterOutputs is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class EAxisParam(object): + """ + Attributes: + - type + - mode + - ref_velo + - ref_acc + - ref_jerk + - max_velo + - max_acc + - encoder_type + - encoder_resolution + - position_bias + - screw_lead + - invert + - position_limit + + """ + + + def __init__(self, type=None, mode=None, ref_velo=None, ref_acc=None, ref_jerk=None, max_velo=None, max_acc=None, encoder_type=None, encoder_resolution=None, position_bias=None, screw_lead=None, invert=None, position_limit=None,): + self.type = type + self.mode = mode + self.ref_velo = ref_velo + self.ref_acc = ref_acc + self.ref_jerk = ref_jerk + self.max_velo = max_velo + self.max_acc = max_acc + self.encoder_type = encoder_type + self.encoder_resolution = encoder_resolution + self.position_bias = position_bias + self.screw_lead = screw_lead + self.invert = invert + self.position_limit = position_limit + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.mode = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.ref_velo = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.ref_acc = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.ref_jerk = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.max_velo = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.max_acc = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.I32: + self.encoder_type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.I32: + self.encoder_resolution = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.DOUBLE: + self.position_bias = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.DOUBLE: + self.screw_lead = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 12: + if ftype == TType.BOOL: + self.invert = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 13: + if ftype == TType.LIST: + self.position_limit = [] + (_etype269, _size266) = iprot.readListBegin() + for _i270 in range(_size266): + _elem271 = iprot.readDouble() + self.position_limit.append(_elem271) + iprot.readListEnd() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('EAxisParam') + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 1) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + if self.mode is not None: + oprot.writeFieldBegin('mode', TType.I32, 2) + oprot.writeI32(self.mode) + oprot.writeFieldEnd() + if self.ref_velo is not None: + oprot.writeFieldBegin('ref_velo', TType.DOUBLE, 3) + oprot.writeDouble(self.ref_velo) + oprot.writeFieldEnd() + if self.ref_acc is not None: + oprot.writeFieldBegin('ref_acc', TType.DOUBLE, 4) + oprot.writeDouble(self.ref_acc) + oprot.writeFieldEnd() + if self.ref_jerk is not None: + oprot.writeFieldBegin('ref_jerk', TType.DOUBLE, 5) + oprot.writeDouble(self.ref_jerk) + oprot.writeFieldEnd() + if self.max_velo is not None: + oprot.writeFieldBegin('max_velo', TType.DOUBLE, 6) + oprot.writeDouble(self.max_velo) + oprot.writeFieldEnd() + if self.max_acc is not None: + oprot.writeFieldBegin('max_acc', TType.DOUBLE, 7) + oprot.writeDouble(self.max_acc) + oprot.writeFieldEnd() + if self.encoder_type is not None: + oprot.writeFieldBegin('encoder_type', TType.I32, 8) + oprot.writeI32(self.encoder_type) + oprot.writeFieldEnd() + if self.encoder_resolution is not None: + oprot.writeFieldBegin('encoder_resolution', TType.I32, 9) + oprot.writeI32(self.encoder_resolution) + oprot.writeFieldEnd() + if self.position_bias is not None: + oprot.writeFieldBegin('position_bias', TType.DOUBLE, 10) + oprot.writeDouble(self.position_bias) + oprot.writeFieldEnd() + if self.screw_lead is not None: + oprot.writeFieldBegin('screw_lead', TType.DOUBLE, 11) + oprot.writeDouble(self.screw_lead) + oprot.writeFieldEnd() + if self.invert is not None: + oprot.writeFieldBegin('invert', TType.BOOL, 12) + oprot.writeBool(self.invert) + oprot.writeFieldEnd() + if self.position_limit is not None: + oprot.writeFieldBegin('position_limit', TType.LIST, 13) + oprot.writeListBegin(TType.DOUBLE, len(self.position_limit)) + for iter272 in self.position_limit: + oprot.writeDouble(iter272) + oprot.writeListEnd() + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.type is None: + raise TProtocolException(message='Required field type is unset!') + if self.mode is None: + raise TProtocolException(message='Required field mode is unset!') + if self.ref_velo is None: + raise TProtocolException(message='Required field ref_velo is unset!') + if self.ref_acc is None: + raise TProtocolException(message='Required field ref_acc is unset!') + if self.ref_jerk is None: + raise TProtocolException(message='Required field ref_jerk is unset!') + if self.max_velo is None: + raise TProtocolException(message='Required field max_velo is unset!') + if self.max_acc is None: + raise TProtocolException(message='Required field max_acc is unset!') + if self.encoder_type is None: + raise TProtocolException(message='Required field encoder_type is unset!') + if self.encoder_resolution is None: + raise TProtocolException(message='Required field encoder_resolution is unset!') + if self.position_bias is None: + raise TProtocolException(message='Required field position_bias is unset!') + if self.screw_lead is None: + raise TProtocolException(message='Required field screw_lead is unset!') + if self.invert is None: + raise TProtocolException(message='Required field invert is unset!') + if self.position_limit is None: + raise TProtocolException(message='Required field position_limit is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class EAxisSchemeParam(object): + """ + Attributes: + - type + - axis_link + - base + - dh + - install + + """ + + + def __init__(self, type=None, axis_link=None, base=None, dh=None, install=None,): + self.type = type + self.axis_link = axis_link + self.base = base + self.dh = dh + self.install = install + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.axis_link = [] + (_etype276, _size273) = iprot.readListBegin() + for _i277 in range(_size273): + _elem278 = iprot.readI32() + self.axis_link.append(_elem278) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.base = [] + (_etype282, _size279) = iprot.readListBegin() + for _i283 in range(_size279): + _elem284 = iprot.readDouble() + self.base.append(_elem284) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.dh = [] + (_etype288, _size285) = iprot.readListBegin() + for _i289 in range(_size285): + _elem290 = [] + (_etype294, _size291) = iprot.readListBegin() + for _i295 in range(_size291): + _elem296 = iprot.readDouble() + _elem290.append(_elem296) + iprot.readListEnd() + self.dh.append(_elem290) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.install = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('EAxisSchemeParam') + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 1) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + if self.axis_link is not None: + oprot.writeFieldBegin('axis_link', TType.LIST, 2) + oprot.writeListBegin(TType.I32, len(self.axis_link)) + for iter297 in self.axis_link: + oprot.writeI32(iter297) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.base is not None: + oprot.writeFieldBegin('base', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.base)) + for iter298 in self.base: + oprot.writeDouble(iter298) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.dh is not None: + oprot.writeFieldBegin('dh', TType.LIST, 4) + oprot.writeListBegin(TType.LIST, len(self.dh)) + for iter299 in self.dh: + oprot.writeListBegin(TType.DOUBLE, len(iter299)) + for iter300 in iter299: + oprot.writeDouble(iter300) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.install is not None: + oprot.writeFieldBegin('install', TType.I32, 5) + oprot.writeI32(self.install) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.type is None: + raise TProtocolException(message='Required field type is unset!') + if self.axis_link is None: + raise TProtocolException(message='Required field axis_link is unset!') + if self.base is None: + raise TProtocolException(message='Required field base is unset!') + if self.dh is None: + raise TProtocolException(message='Required field dh is unset!') + if self.install is None: + raise TProtocolException(message='Required field install is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class MoveJogTaskParam(object): + """ + Attributes: + - jog_direction + - jog_type + - axis_num + - vel + - jog_coordinate + - use_step + - step_jointValue + - step_cartvalue + + """ + + + def __init__(self, jog_direction=None, jog_type=None, axis_num=None, vel=None, jog_coordinate=None, use_step=None, step_jointValue=None, step_cartvalue=None,): + self.jog_direction = jog_direction + self.jog_type = jog_type + self.axis_num = axis_num + self.vel = vel + self.jog_coordinate = jog_coordinate + self.use_step = use_step + self.step_jointValue = step_jointValue + self.step_cartvalue = step_cartvalue + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.I32: + self.jog_direction = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.jog_type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.axis_num = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.jog_coordinate = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.BOOL: + self.use_step = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.step_jointValue = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.DOUBLE: + self.step_cartvalue = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('MoveJogTaskParam') + if self.jog_direction is not None: + oprot.writeFieldBegin('jog_direction', TType.I32, 1) + oprot.writeI32(self.jog_direction) + oprot.writeFieldEnd() + if self.jog_type is not None: + oprot.writeFieldBegin('jog_type', TType.I32, 2) + oprot.writeI32(self.jog_type) + oprot.writeFieldEnd() + if self.axis_num is not None: + oprot.writeFieldBegin('axis_num', TType.I32, 3) + oprot.writeI32(self.axis_num) + oprot.writeFieldEnd() + if self.vel is not None: + oprot.writeFieldBegin('vel', TType.DOUBLE, 4) + oprot.writeDouble(self.vel) + oprot.writeFieldEnd() + if self.jog_coordinate is not None: + oprot.writeFieldBegin('jog_coordinate', TType.I32, 5) + oprot.writeI32(self.jog_coordinate) + oprot.writeFieldEnd() + if self.use_step is not None: + oprot.writeFieldBegin('use_step', TType.BOOL, 6) + oprot.writeBool(self.use_step) + oprot.writeFieldEnd() + if self.step_jointValue is not None: + oprot.writeFieldBegin('step_jointValue', TType.DOUBLE, 7) + oprot.writeDouble(self.step_jointValue) + oprot.writeFieldEnd() + if self.step_cartvalue is not None: + oprot.writeFieldBegin('step_cartvalue', TType.DOUBLE, 8) + oprot.writeDouble(self.step_cartvalue) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.jog_direction is None: + raise TProtocolException(message='Required field jog_direction is unset!') + if self.jog_type is None: + raise TProtocolException(message='Required field jog_type is unset!') + if self.axis_num is None: + raise TProtocolException(message='Required field axis_num is unset!') + if self.vel is None: + raise TProtocolException(message='Required field vel is unset!') + if self.jog_coordinate is None: + raise TProtocolException(message='Required field jog_coordinate is unset!') + if self.use_step is None: + raise TProtocolException(message='Required field use_step is unset!') + if self.step_jointValue is None: + raise TProtocolException(message='Required field step_jointValue is unset!') + if self.step_cartvalue is None: + raise TProtocolException(message='Required field step_cartvalue is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class ReachabilityParam(object): + """ + Attributes: + - result + - index + - singularity_type + + """ + + + def __init__(self, result=None, index=None, singularity_type=None,): + self.result = result + self.index = index + self.singularity_type = singularity_type + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.BOOL: + self.result = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.index = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.I32: + self.singularity_type = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('ReachabilityParam') + if self.result is not None: + oprot.writeFieldBegin('result', TType.BOOL, 1) + oprot.writeBool(self.result) + oprot.writeFieldEnd() + if self.index is not None: + oprot.writeFieldBegin('index', TType.I32, 2) + oprot.writeI32(self.index) + oprot.writeFieldEnd() + if self.singularity_type is not None: + oprot.writeFieldBegin('singularity_type', TType.I32, 3) + oprot.writeI32(self.singularity_type) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.result is None: + raise TProtocolException(message='Required field result is unset!') + if self.index is None: + raise TProtocolException(message='Required field index is unset!') + if self.singularity_type is None: + raise TProtocolException(message='Required field singularity_type is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class EAxissInfo(object): + """ + Attributes: + - index + - status + - pos + - vel + - acc + + """ + + + def __init__(self, index=None, status=None, pos=None, vel=None, acc=None,): + self.index = index + self.status = status + self.pos = pos + self.vel = vel + self.acc = acc + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.index = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.status = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.pos = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.acc = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('EAxissInfo') + if self.index is not None: + oprot.writeFieldBegin('index', TType.STRING, 1) + oprot.writeString(self.index.encode('utf-8') if sys.version_info[0] == 2 else self.index) + oprot.writeFieldEnd() + if self.status is not None: + oprot.writeFieldBegin('status', TType.I32, 2) + oprot.writeI32(self.status) + oprot.writeFieldEnd() + if self.pos is not None: + oprot.writeFieldBegin('pos', TType.DOUBLE, 3) + oprot.writeDouble(self.pos) + oprot.writeFieldEnd() + if self.vel is not None: + oprot.writeFieldBegin('vel', TType.DOUBLE, 4) + oprot.writeDouble(self.vel) + oprot.writeFieldEnd() + if self.acc is not None: + oprot.writeFieldBegin('acc', TType.DOUBLE, 5) + oprot.writeDouble(self.acc) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.index is None: + raise TProtocolException(message='Required field index is unset!') + if self.status is None: + raise TProtocolException(message='Required field status is unset!') + if self.pos is None: + raise TProtocolException(message='Required field pos is unset!') + if self.vel is None: + raise TProtocolException(message='Required field vel is unset!') + if self.acc is None: + raise TProtocolException(message='Required field acc is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class EAxisSchemesParam(object): + """ + Attributes: + - name + - eaxis_status + - eaxis_pos + - end_pos + - active + - valid + + """ + + + def __init__(self, name=None, eaxis_status=None, eaxis_pos=None, end_pos=None, active=None, valid=None,): + self.name = name + self.eaxis_status = eaxis_status + self.eaxis_pos = eaxis_pos + self.end_pos = end_pos + self.active = active + self.valid = valid + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.STRING: + self.name = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.eaxis_status = [] + (_etype304, _size301) = iprot.readListBegin() + for _i305 in range(_size301): + _elem306 = iprot.readI32() + self.eaxis_status.append(_elem306) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.eaxis_pos = [] + (_etype310, _size307) = iprot.readListBegin() + for _i311 in range(_size307): + _elem312 = iprot.readDouble() + self.eaxis_pos.append(_elem312) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.end_pos = [] + (_etype316, _size313) = iprot.readListBegin() + for _i317 in range(_size313): + _elem318 = iprot.readDouble() + self.end_pos.append(_elem318) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.BOOL: + self.active = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.BOOL: + self.valid = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('EAxisSchemesParam') + if self.name is not None: + oprot.writeFieldBegin('name', TType.STRING, 1) + oprot.writeString(self.name.encode('utf-8') if sys.version_info[0] == 2 else self.name) + oprot.writeFieldEnd() + if self.eaxis_status is not None: + oprot.writeFieldBegin('eaxis_status', TType.LIST, 2) + oprot.writeListBegin(TType.I32, len(self.eaxis_status)) + for iter319 in self.eaxis_status: + oprot.writeI32(iter319) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.eaxis_pos is not None: + oprot.writeFieldBegin('eaxis_pos', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.eaxis_pos)) + for iter320 in self.eaxis_pos: + oprot.writeDouble(iter320) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.end_pos is not None: + oprot.writeFieldBegin('end_pos', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.end_pos)) + for iter321 in self.end_pos: + oprot.writeDouble(iter321) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.active is not None: + oprot.writeFieldBegin('active', TType.BOOL, 5) + oprot.writeBool(self.active) + oprot.writeFieldEnd() + if self.valid is not None: + oprot.writeFieldBegin('valid', TType.BOOL, 6) + oprot.writeBool(self.valid) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.name is None: + raise TProtocolException(message='Required field name is unset!') + if self.eaxis_status is None: + raise TProtocolException(message='Required field eaxis_status is unset!') + if self.eaxis_pos is None: + raise TProtocolException(message='Required field eaxis_pos is unset!') + if self.end_pos is None: + raise TProtocolException(message='Required field end_pos is unset!') + if self.active is None: + raise TProtocolException(message='Required field active is unset!') + if self.valid is None: + raise TProtocolException(message='Required field valid is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class RealTimeControlData(object): + """ + Attributes: + - joint_pos_cmd + - joint_vel_cmd + - joint_torq_cmd + - cart_pos_tool_wobj_cmd + - cart_vel_tool_wobj_cmd + - cart_ft_cmd + - status + + """ + + + def __init__(self, joint_pos_cmd=None, joint_vel_cmd=None, joint_torq_cmd=None, cart_pos_tool_wobj_cmd=None, cart_vel_tool_wobj_cmd=None, cart_ft_cmd=None, status=None,): + self.joint_pos_cmd = joint_pos_cmd + self.joint_vel_cmd = joint_vel_cmd + self.joint_torq_cmd = joint_torq_cmd + self.cart_pos_tool_wobj_cmd = cart_pos_tool_wobj_cmd + self.cart_vel_tool_wobj_cmd = cart_vel_tool_wobj_cmd + self.cart_ft_cmd = cart_ft_cmd + self.status = status + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.joint_pos_cmd = [] + (_etype325, _size322) = iprot.readListBegin() + for _i326 in range(_size322): + _elem327 = iprot.readDouble() + self.joint_pos_cmd.append(_elem327) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.joint_vel_cmd = [] + (_etype331, _size328) = iprot.readListBegin() + for _i332 in range(_size328): + _elem333 = iprot.readDouble() + self.joint_vel_cmd.append(_elem333) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.joint_torq_cmd = [] + (_etype337, _size334) = iprot.readListBegin() + for _i338 in range(_size334): + _elem339 = iprot.readDouble() + self.joint_torq_cmd.append(_elem339) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.cart_pos_tool_wobj_cmd = [] + (_etype343, _size340) = iprot.readListBegin() + for _i344 in range(_size340): + _elem345 = iprot.readDouble() + self.cart_pos_tool_wobj_cmd.append(_elem345) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.LIST: + self.cart_vel_tool_wobj_cmd = [] + (_etype349, _size346) = iprot.readListBegin() + for _i350 in range(_size346): + _elem351 = iprot.readDouble() + self.cart_vel_tool_wobj_cmd.append(_elem351) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.LIST: + self.cart_ft_cmd = [] + (_etype355, _size352) = iprot.readListBegin() + for _i356 in range(_size352): + _elem357 = iprot.readDouble() + self.cart_ft_cmd.append(_elem357) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.BOOL: + self.status = iprot.readBool() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('RealTimeControlData') + if self.joint_pos_cmd is not None: + oprot.writeFieldBegin('joint_pos_cmd', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.joint_pos_cmd)) + for iter358 in self.joint_pos_cmd: + oprot.writeDouble(iter358) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.joint_vel_cmd is not None: + oprot.writeFieldBegin('joint_vel_cmd', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.joint_vel_cmd)) + for iter359 in self.joint_vel_cmd: + oprot.writeDouble(iter359) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.joint_torq_cmd is not None: + oprot.writeFieldBegin('joint_torq_cmd', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.joint_torq_cmd)) + for iter360 in self.joint_torq_cmd: + oprot.writeDouble(iter360) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cart_pos_tool_wobj_cmd is not None: + oprot.writeFieldBegin('cart_pos_tool_wobj_cmd', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.cart_pos_tool_wobj_cmd)) + for iter361 in self.cart_pos_tool_wobj_cmd: + oprot.writeDouble(iter361) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cart_vel_tool_wobj_cmd is not None: + oprot.writeFieldBegin('cart_vel_tool_wobj_cmd', TType.LIST, 5) + oprot.writeListBegin(TType.DOUBLE, len(self.cart_vel_tool_wobj_cmd)) + for iter362 in self.cart_vel_tool_wobj_cmd: + oprot.writeDouble(iter362) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.cart_ft_cmd is not None: + oprot.writeFieldBegin('cart_ft_cmd', TType.LIST, 6) + oprot.writeListBegin(TType.DOUBLE, len(self.cart_ft_cmd)) + for iter363 in self.cart_ft_cmd: + oprot.writeDouble(iter363) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.status is not None: + oprot.writeFieldBegin('status', TType.BOOL, 7) + oprot.writeBool(self.status) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.joint_pos_cmd is None: + raise TProtocolException(message='Required field joint_pos_cmd is unset!') + if self.joint_vel_cmd is None: + raise TProtocolException(message='Required field joint_vel_cmd is unset!') + if self.joint_torq_cmd is None: + raise TProtocolException(message='Required field joint_torq_cmd is unset!') + if self.cart_pos_tool_wobj_cmd is None: + raise TProtocolException(message='Required field cart_pos_tool_wobj_cmd is unset!') + if self.cart_vel_tool_wobj_cmd is None: + raise TProtocolException(message='Required field cart_vel_tool_wobj_cmd is unset!') + if self.cart_ft_cmd is None: + raise TProtocolException(message='Required field cart_ft_cmd is unset!') + if self.status is None: + raise TProtocolException(message='Required field status is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class PointOp(object): + """ + Attributes: + - pos + - op + - vel + - blend_time + - dwell_time + + """ + + + def __init__(self, pos=None, op=None, vel=None, blend_time=None, dwell_time=None,): + self.pos = pos + self.op = op + self.vel = vel + self.blend_time = blend_time + self.dwell_time = dwell_time + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.pos = [] + (_etype367, _size364) = iprot.readListBegin() + for _i368 in range(_size364): + _elem369 = iprot.readDouble() + self.pos.append(_elem369) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.STRUCT: + self.op = Op() + self.op.read(iprot) + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.DOUBLE: + self.vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.DOUBLE: + self.blend_time = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.DOUBLE: + self.dwell_time = iprot.readDouble() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('PointOp') + if self.pos is not None: + oprot.writeFieldBegin('pos', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.pos)) + for iter370 in self.pos: + oprot.writeDouble(iter370) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.op is not None: + oprot.writeFieldBegin('op', TType.STRUCT, 2) + self.op.write(oprot) + oprot.writeFieldEnd() + if self.vel is not None: + oprot.writeFieldBegin('vel', TType.DOUBLE, 3) + oprot.writeDouble(self.vel) + oprot.writeFieldEnd() + if self.blend_time is not None: + oprot.writeFieldBegin('blend_time', TType.DOUBLE, 4) + oprot.writeDouble(self.blend_time) + oprot.writeFieldEnd() + if self.dwell_time is not None: + oprot.writeFieldBegin('dwell_time', TType.DOUBLE, 5) + oprot.writeDouble(self.dwell_time) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.pos is None: + raise TProtocolException(message='Required field pos is unset!') + if self.op is None: + raise TProtocolException(message='Required field op is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class TrajTimeParam(object): + """ + Attributes: + - start + - target + - middle + - qnear + - type + - vel + - acc + - circle + - scale + - tool + - wobj + + """ + + + def __init__(self, start=None, target=None, middle=None, qnear=None, type=None, vel=None, acc=None, circle=None, scale=None, tool=None, wobj=None,): + self.start = start + self.target = target + self.middle = middle + self.qnear = qnear + self.type = type + self.vel = vel + self.acc = acc + self.circle = circle + self.scale = scale + self.tool = tool + self.wobj = wobj + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.start = [] + (_etype374, _size371) = iprot.readListBegin() + for _i375 in range(_size371): + _elem376 = iprot.readDouble() + self.start.append(_elem376) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.LIST: + self.target = [] + (_etype380, _size377) = iprot.readListBegin() + for _i381 in range(_size377): + _elem382 = iprot.readDouble() + self.target.append(_elem382) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 3: + if ftype == TType.LIST: + self.middle = [] + (_etype386, _size383) = iprot.readListBegin() + for _i387 in range(_size383): + _elem388 = iprot.readDouble() + self.middle.append(_elem388) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 4: + if ftype == TType.LIST: + self.qnear = [] + (_etype392, _size389) = iprot.readListBegin() + for _i393 in range(_size389): + _elem394 = iprot.readDouble() + self.qnear.append(_elem394) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 5: + if ftype == TType.I32: + self.type = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 6: + if ftype == TType.DOUBLE: + self.vel = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 7: + if ftype == TType.DOUBLE: + self.acc = iprot.readDouble() + else: + iprot.skip(ftype) + elif fid == 8: + if ftype == TType.BOOL: + self.circle = iprot.readBool() + else: + iprot.skip(ftype) + elif fid == 9: + if ftype == TType.I32: + self.scale = iprot.readI32() + else: + iprot.skip(ftype) + elif fid == 10: + if ftype == TType.STRING: + self.tool = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + elif fid == 11: + if ftype == TType.STRING: + self.wobj = iprot.readString().decode('utf-8') if sys.version_info[0] == 2 else iprot.readString() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('TrajTimeParam') + if self.start is not None: + oprot.writeFieldBegin('start', TType.LIST, 1) + oprot.writeListBegin(TType.DOUBLE, len(self.start)) + for iter395 in self.start: + oprot.writeDouble(iter395) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.target is not None: + oprot.writeFieldBegin('target', TType.LIST, 2) + oprot.writeListBegin(TType.DOUBLE, len(self.target)) + for iter396 in self.target: + oprot.writeDouble(iter396) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.middle is not None: + oprot.writeFieldBegin('middle', TType.LIST, 3) + oprot.writeListBegin(TType.DOUBLE, len(self.middle)) + for iter397 in self.middle: + oprot.writeDouble(iter397) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.qnear is not None: + oprot.writeFieldBegin('qnear', TType.LIST, 4) + oprot.writeListBegin(TType.DOUBLE, len(self.qnear)) + for iter398 in self.qnear: + oprot.writeDouble(iter398) + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.type is not None: + oprot.writeFieldBegin('type', TType.I32, 5) + oprot.writeI32(self.type) + oprot.writeFieldEnd() + if self.vel is not None: + oprot.writeFieldBegin('vel', TType.DOUBLE, 6) + oprot.writeDouble(self.vel) + oprot.writeFieldEnd() + if self.acc is not None: + oprot.writeFieldBegin('acc', TType.DOUBLE, 7) + oprot.writeDouble(self.acc) + oprot.writeFieldEnd() + if self.circle is not None: + oprot.writeFieldBegin('circle', TType.BOOL, 8) + oprot.writeBool(self.circle) + oprot.writeFieldEnd() + if self.scale is not None: + oprot.writeFieldBegin('scale', TType.I32, 9) + oprot.writeI32(self.scale) + oprot.writeFieldEnd() + if self.tool is not None: + oprot.writeFieldBegin('tool', TType.STRING, 10) + oprot.writeString(self.tool.encode('utf-8') if sys.version_info[0] == 2 else self.tool) + oprot.writeFieldEnd() + if self.wobj is not None: + oprot.writeFieldBegin('wobj', TType.STRING, 11) + oprot.writeString(self.wobj.encode('utf-8') if sys.version_info[0] == 2 else self.wobj) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.start is None: + raise TProtocolException(message='Required field start is unset!') + if self.target is None: + raise TProtocolException(message='Required field target is unset!') + if self.middle is None: + raise TProtocolException(message='Required field middle is unset!') + if self.qnear is None: + raise TProtocolException(message='Required field qnear is unset!') + if self.type is None: + raise TProtocolException(message='Required field type is unset!') + if self.vel is None: + raise TProtocolException(message='Required field vel is unset!') + if self.acc is None: + raise TProtocolException(message='Required field acc is unset!') + if self.circle is None: + raise TProtocolException(message='Required field circle is unset!') + if self.scale is None: + raise TProtocolException(message='Required field scale is unset!') + if self.tool is None: + raise TProtocolException(message='Required field tool is unset!') + if self.wobj is None: + raise TProtocolException(message='Required field wobj is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) + + +class PathOffsetResult(object): + """ + Attributes: + - path_result + - status_code + + """ + + + def __init__(self, path_result=None, status_code=None,): + self.path_result = path_result + self.status_code = status_code + + def read(self, iprot): + if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: + iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) + return + iprot.readStructBegin() + while True: + (fname, ftype, fid) = iprot.readFieldBegin() + if ftype == TType.STOP: + break + if fid == 1: + if ftype == TType.LIST: + self.path_result = [] + (_etype402, _size399) = iprot.readListBegin() + for _i403 in range(_size399): + _elem404 = [] + (_etype408, _size405) = iprot.readListBegin() + for _i409 in range(_size405): + _elem410 = iprot.readDouble() + _elem404.append(_elem410) + iprot.readListEnd() + self.path_result.append(_elem404) + iprot.readListEnd() + else: + iprot.skip(ftype) + elif fid == 2: + if ftype == TType.I32: + self.status_code = iprot.readI32() + else: + iprot.skip(ftype) + else: + iprot.skip(ftype) + iprot.readFieldEnd() + iprot.readStructEnd() + + def write(self, oprot): + if oprot._fast_encode is not None and self.thrift_spec is not None: + oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) + return + oprot.writeStructBegin('PathOffsetResult') + if self.path_result is not None: + oprot.writeFieldBegin('path_result', TType.LIST, 1) + oprot.writeListBegin(TType.LIST, len(self.path_result)) + for iter411 in self.path_result: + oprot.writeListBegin(TType.DOUBLE, len(iter411)) + for iter412 in iter411: + oprot.writeDouble(iter412) + oprot.writeListEnd() + oprot.writeListEnd() + oprot.writeFieldEnd() + if self.status_code is not None: + oprot.writeFieldBegin('status_code', TType.I32, 2) + oprot.writeI32(self.status_code) + oprot.writeFieldEnd() + oprot.writeFieldStop() + oprot.writeStructEnd() + + def validate(self): + if self.path_result is None: + raise TProtocolException(message='Required field path_result is unset!') + if self.status_code is None: + raise TProtocolException(message='Required field status_code is unset!') + return + + def __repr__(self): + L = ['%s=%r' % (key, value) + for key, value in self.__dict__.items()] + return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) + + def __eq__(self, other): + return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return not (self == other) +all_structs.append(Op) +Op.thrift_spec = ( + None, # 0 + (1, TType.BYTE, 'time_or_dist_1', None, None, ), # 1 + (2, TType.BYTE, 'trig_io_1', None, None, ), # 2 + (3, TType.BOOL, 'trig_value_1', None, None, ), # 3 + (4, TType.DOUBLE, 'trig_time_1', None, None, ), # 4 + (5, TType.DOUBLE, 'trig_dist_1', None, None, ), # 5 + (6, TType.STRING, 'trig_event_1', 'UTF8', None, ), # 6 + (7, TType.BYTE, 'time_or_dist_2', None, None, ), # 7 + (8, TType.BYTE, 'trig_io_2', None, None, ), # 8 + (9, TType.BOOL, 'trig_value_2', None, None, ), # 9 + (10, TType.DOUBLE, 'trig_time_2', None, None, ), # 10 + (11, TType.DOUBLE, 'trig_dist_2', None, None, ), # 11 + (12, TType.STRING, 'trig_event_2', 'UTF8', None, ), # 12 + (13, TType.BYTE, 'time_or_dist_3', None, None, ), # 13 + (14, TType.BYTE, 'trig_io_3', None, None, ), # 14 + (15, TType.BOOL, 'trig_value_3', None, None, ), # 15 + (16, TType.DOUBLE, 'trig_time_3', None, None, ), # 16 + (17, TType.DOUBLE, 'trig_dist_3', None, None, ), # 17 + (18, TType.STRING, 'trig_event_3', 'UTF8', None, ), # 18 +) +all_structs.append(RobotStatus) +RobotStatus.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'jointExpectPosition', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'jointExpectVelocity', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'jointExpectAccelera', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'jointActualPosition', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'jointActualVelocity', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.LIST, 'jointActualAccelera', (TType.DOUBLE, None, False), None, ), # 6 + (7, TType.LIST, 'jointActualCurrent', (TType.DOUBLE, None, False), None, ), # 7 + (8, TType.LIST, 'jointTemperature', (TType.DOUBLE, None, False), None, ), # 8 + (9, TType.LIST, 'driverTemperature', (TType.DOUBLE, None, False), None, ), # 9 + (10, TType.LIST, 'cartExpectPosition', (TType.DOUBLE, None, False), None, ), # 10 + (11, TType.LIST, 'cartExpectVelocity', (TType.DOUBLE, None, False), None, ), # 11 + (12, TType.LIST, 'cartExpectAccelera', (TType.DOUBLE, None, False), None, ), # 12 + (13, TType.LIST, 'cartActualPosition', (TType.DOUBLE, None, False), None, ), # 13 + (14, TType.LIST, 'cartActualVelocity', (TType.DOUBLE, None, False), None, ), # 14 + (15, TType.LIST, 'cartActualAccelera', (TType.DOUBLE, None, False), None, ), # 15 + (16, TType.LIST, 'slaveReady', (TType.BOOL, None, False), None, ), # 16 + (17, TType.BOOL, 'collision', None, None, ), # 17 + (18, TType.BYTE, 'collisionAxis', None, None, ), # 18 + (19, TType.BOOL, 'emcStopSignal', None, None, ), # 19 + (20, TType.BYTE, 'robotState', None, None, ), # 20 + (21, TType.I32, 'robotError', None, None, ), # 21 + (22, TType.LIST, 'jointAuxiliaryPosition', (TType.DOUBLE, None, False), None, ), # 22 + (23, TType.LIST, 'jointDynamicTorque', (TType.DOUBLE, None, False), None, ), # 23 + (24, TType.LIST, 'jointGravityTorque', (TType.DOUBLE, None, False), None, ), # 24 + (25, TType.LIST, 'jointActualTorque', (TType.DOUBLE, None, False), None, ), # 25 + (26, TType.LIST, 'jointExtraTorque', (TType.DOUBLE, None, False), None, ), # 26 +) +all_structs.append(IOStatus) +IOStatus.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'analogCurrentOutputs', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'analogVoltageOutputs', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'analogCurrentInputs', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'analogVoltageInputs', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'digitalInputs', (TType.BOOL, None, False), None, ), # 5 + (6, TType.LIST, 'digitalOutputs', (TType.BOOL, None, False), None, ), # 6 + (7, TType.LIST, 'toolIOIn', (TType.BOOL, None, False), None, ), # 7 + (8, TType.LIST, 'toolIOOut', (TType.BOOL, None, False), None, ), # 8 + (9, TType.LIST, 'toolButton', (TType.BOOL, None, False), None, ), # 9 + (10, TType.LIST, 'funRegisterInputs', (TType.BOOL, None, False), None, ), # 10 + (11, TType.LIST, 'funRegisterOutputs', (TType.BOOL, None, False), None, ), # 11 + (12, TType.LIST, 'boolRegisterInputs', (TType.BOOL, None, False), None, ), # 12 + (13, TType.LIST, 'boolRegisterOutputs', (TType.BOOL, None, False), None, ), # 13 + (14, TType.LIST, 'wordRegisterInputs', (TType.I16, None, False), None, ), # 14 + (15, TType.LIST, 'wordRegisterOutputs', (TType.I16, None, False), None, ), # 15 + (16, TType.LIST, 'floatRegisterInputs', (TType.DOUBLE, None, False), None, ), # 16 + (17, TType.LIST, 'floatRegisterOutputs', (TType.DOUBLE, None, False), None, ), # 17 +) +all_structs.append(EAxisParam) +EAxisParam.thrift_spec = ( + None, # 0 + (1, TType.I32, 'type', None, None, ), # 1 + (2, TType.I32, 'mode', None, None, ), # 2 + (3, TType.DOUBLE, 'ref_velo', None, None, ), # 3 + (4, TType.DOUBLE, 'ref_acc', None, None, ), # 4 + (5, TType.DOUBLE, 'ref_jerk', None, None, ), # 5 + (6, TType.DOUBLE, 'max_velo', None, None, ), # 6 + (7, TType.DOUBLE, 'max_acc', None, None, ), # 7 + (8, TType.I32, 'encoder_type', None, None, ), # 8 + (9, TType.I32, 'encoder_resolution', None, None, ), # 9 + (10, TType.DOUBLE, 'position_bias', None, None, ), # 10 + (11, TType.DOUBLE, 'screw_lead', None, None, ), # 11 + (12, TType.BOOL, 'invert', None, None, ), # 12 + (13, TType.LIST, 'position_limit', (TType.DOUBLE, None, False), None, ), # 13 +) +all_structs.append(EAxisSchemeParam) +EAxisSchemeParam.thrift_spec = ( + None, # 0 + (1, TType.I32, 'type', None, None, ), # 1 + (2, TType.LIST, 'axis_link', (TType.I32, None, False), None, ), # 2 + (3, TType.LIST, 'base', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'dh', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 4 + (5, TType.I32, 'install', None, None, ), # 5 +) +all_structs.append(MoveJogTaskParam) +MoveJogTaskParam.thrift_spec = ( + None, # 0 + (1, TType.I32, 'jog_direction', None, None, ), # 1 + (2, TType.I32, 'jog_type', None, None, ), # 2 + (3, TType.I32, 'axis_num', None, None, ), # 3 + (4, TType.DOUBLE, 'vel', None, None, ), # 4 + (5, TType.I32, 'jog_coordinate', None, None, ), # 5 + (6, TType.BOOL, 'use_step', None, None, ), # 6 + (7, TType.DOUBLE, 'step_jointValue', None, None, ), # 7 + (8, TType.DOUBLE, 'step_cartvalue', None, None, ), # 8 +) +all_structs.append(ReachabilityParam) +ReachabilityParam.thrift_spec = ( + None, # 0 + (1, TType.BOOL, 'result', None, None, ), # 1 + (2, TType.I32, 'index', None, None, ), # 2 + (3, TType.I32, 'singularity_type', None, None, ), # 3 +) +all_structs.append(EAxissInfo) +EAxissInfo.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'index', 'UTF8', None, ), # 1 + (2, TType.I32, 'status', None, None, ), # 2 + (3, TType.DOUBLE, 'pos', None, None, ), # 3 + (4, TType.DOUBLE, 'vel', None, None, ), # 4 + (5, TType.DOUBLE, 'acc', None, None, ), # 5 +) +all_structs.append(EAxisSchemesParam) +EAxisSchemesParam.thrift_spec = ( + None, # 0 + (1, TType.STRING, 'name', 'UTF8', None, ), # 1 + (2, TType.LIST, 'eaxis_status', (TType.I32, None, False), None, ), # 2 + (3, TType.LIST, 'eaxis_pos', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'end_pos', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.BOOL, 'active', None, None, ), # 5 + (6, TType.BOOL, 'valid', None, None, ), # 6 +) +all_structs.append(RealTimeControlData) +RealTimeControlData.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'joint_pos_cmd', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'joint_vel_cmd', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'joint_torq_cmd', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'cart_pos_tool_wobj_cmd', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.LIST, 'cart_vel_tool_wobj_cmd', (TType.DOUBLE, None, False), None, ), # 5 + (6, TType.LIST, 'cart_ft_cmd', (TType.DOUBLE, None, False), None, ), # 6 + (7, TType.BOOL, 'status', None, None, ), # 7 +) +all_structs.append(PointOp) +PointOp.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'pos', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.STRUCT, 'op', [Op, None], None, ), # 2 + (3, TType.DOUBLE, 'vel', None, None, ), # 3 + (4, TType.DOUBLE, 'blend_time', None, None, ), # 4 + (5, TType.DOUBLE, 'dwell_time', None, None, ), # 5 +) +all_structs.append(TrajTimeParam) +TrajTimeParam.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'start', (TType.DOUBLE, None, False), None, ), # 1 + (2, TType.LIST, 'target', (TType.DOUBLE, None, False), None, ), # 2 + (3, TType.LIST, 'middle', (TType.DOUBLE, None, False), None, ), # 3 + (4, TType.LIST, 'qnear', (TType.DOUBLE, None, False), None, ), # 4 + (5, TType.I32, 'type', None, None, ), # 5 + (6, TType.DOUBLE, 'vel', None, None, ), # 6 + (7, TType.DOUBLE, 'acc', None, None, ), # 7 + (8, TType.BOOL, 'circle', None, None, ), # 8 + (9, TType.I32, 'scale', None, None, ), # 9 + (10, TType.STRING, 'tool', 'UTF8', None, ), # 10 + (11, TType.STRING, 'wobj', 'UTF8', None, ), # 11 +) +all_structs.append(PathOffsetResult) +PathOffsetResult.thrift_spec = ( + None, # 0 + (1, TType.LIST, 'path_result', (TType.LIST, (TType.DOUBLE, None, False), False), None, ), # 1 + (2, TType.I32, 'status_code', None, None, ), # 2 +) +fix_spec(all_structs) +del all_structs diff --git a/unilabos/devices/eit_agv/driver/agv_driver.py b/unilabos/devices/eit_agv/driver/agv_driver.py new file mode 100644 index 00000000..c1390e15 --- /dev/null +++ b/unilabos/devices/eit_agv/driver/agv_driver.py @@ -0,0 +1,969 @@ +# agv_driver.py +# -*- coding: utf-8 -*- +""" +AGV导航状态查询驱动模块 +""" + +from __future__ import annotations + +import json +import logging +import socket +import time +from dataclasses import dataclass +from typing import Any, Dict, Optional, Tuple + +from ..config.agv_config import ( + FRAME_HEAD_SIZE, + REQ_CMD_ROBOT_STATUS_TASK, + RSP_CMD_ROBOT_STATUS_TASK, + REQ_CMD_ROBOT_TASK_GOTARGET, + RSP_CMD_ROBOT_TASK_GOTARGET, + REQ_CMD_ROBOT_STATUS_LOC, + RSP_CMD_ROBOT_STATUS_LOC, + REQ_CMD_ROBOT_STATUS_BATTERY, + RSP_CMD_ROBOT_STATUS_BATTERY, + TASK_STATUS_MAP, + TASK_TYPE_MAP, + AGV_HOST, + AGV_PORT, + AGV_PORT_NAVIGATION, + AGV_TIMEOUT, + AGV_MAX_SPEED, + AGV_MAX_WSPEED, + AGV_MAX_ACC, + AGV_MAX_WACC, +) + +logger = logging.getLogger(__name__) + + +def _hexdump(b: bytes, max_len: int = 64) -> str: + """ + 功能: + 将字节数据转换为十六进制字符串用于调试 + 参数: + b: 待转换的字节数据 + max_len: 最大显示长度, 默认64字节 + 返回: + str, 十六进制字符串表示 + """ + bb = b[:max_len] + s = " ".join(f"{x:02X}" for x in bb) + return s + (" ..." if len(b) > max_len else "") + +def _recv_exact(sock: socket.socket, n: int) -> bytes: + """ + 功能: + 从socket精确读取指定字节数 + 参数: + sock: socket对象 + n: 需要读取的字节数 + 返回: + bytes, 读取到的数据 + """ + buf = bytearray() + while len(buf) < n: + chunk = sock.recv(n - len(buf)) + if not chunk: + raise ConnectionError("连接已关闭") + buf.extend(chunk) + return bytes(buf) + +def _build_frame(cmd_id: int, payload: Optional[bytes] = None, req_id: int = 1) -> bytes: + """ + 功能: + 构造AGV协议请求帧, 完全参考厂家协议格式 + 参数: + cmd_id: 命令ID (消息类型) + payload: 可选的负载数据 + req_id: 请求序列号, 默认为1 + 返回: + bytes, 完整的请求帧数据 + 协议格式: + 字节0-1: 帧头标识 0x5A 0x01 + 字节2-3: 请求ID (大端序) + 字节4-7: 负载长度 (大端序) + 字节8-9: 消息类型/命令ID (大端序) + 字节10-15: 保留字段 (6字节, 填充0x00) + """ + payload = payload or b"" + if len(payload) > 0xFFFFFFFF: + raise ValueError("负载数据过大") + + header = bytearray(FRAME_HEAD_SIZE) + header[0] = 0x5A # 帧头标识1 + header[1] = 0x01 # 帧头标识2 + header[2:4] = req_id.to_bytes(2, byteorder="big", signed=False) # 请求ID + header[4:8] = len(payload).to_bytes(4, byteorder="big", signed=False) # 负载长度 + header[8:10] = cmd_id.to_bytes(2, byteorder="big", signed=False) # 消息类型 + header[10:16] = b"\x00" * 6 # 保留字段 + + return bytes(header) + payload + +def _parse_frame_header(hdr: bytes) -> Tuple[int, int, int]: + """ + 功能: + 解析AGV协议响应帧头部, 参考厂家协议格式 + 参数: + hdr: 帧头部字节数据 + 返回: + Tuple[int, int, int], 包含(负载长度, 响应命令ID, 响应序列号) + 协议格式: + 字节0-1: 帧头标识 0x5A 0x01 + 字节2-3: 响应序列号 (大端序) + 字节4-7: 负载长度 (大端序) + 字节8-9: 响应消息类型/命令ID (大端序) + 字节10-15: 保留字段 (6字节) + """ + if len(hdr) != FRAME_HEAD_SIZE: + raise ValueError(f"帧头长度错误: {len(hdr)} != {FRAME_HEAD_SIZE}") + if hdr[0] != 0x5A or hdr[1] != 0x01: + raise ValueError(f"帧头标识错误: {hdr[0]:02X} {hdr[1]:02X}") + + rsp_seq_num = int.from_bytes(hdr[2:4], byteorder="big", signed=False) # 响应序列号 + payload_len = int.from_bytes(hdr[4:8], byteorder="big", signed=False) # 负载长度 + rsp_cmd_id = int.from_bytes(hdr[8:10], byteorder="big", signed=False) # 响应命令ID + + return payload_len, rsp_cmd_id, rsp_seq_num + +def _decode_json_payload(payload: bytes) -> Dict[str, Any]: + """ + 功能: + 解析JSON格式的负载数据, 参考厂家代码实现 + 参数: + payload: JSON字节数据 + 返回: + Dict[str, Any], 解析后的字典对象 + """ + if not payload: + return {} + # 去除尾部的空字节填充 + payload = payload.rstrip(b"\x00") + if not payload: + return {} + # 使用ASCII解码(厂家代码使用ascii编码) + text = payload.decode("ascii", errors="replace") + obj = json.loads(text) + if not isinstance(obj, dict): + raise ValueError("JSON负载不是对象类型") + return obj + +def _try_parse_json_string(value: Any) -> Any: + """ + 功能: + 尝试将字符串形式的JSON解析为字典对象 + 参数: + value: 待解析的值 + 返回: + Any, 解析后的对象或原值 + """ + if not isinstance(value, str): + return value + s = value.strip() + if not (s.startswith("{") and s.endswith("}")): + return value + try: + return json.loads(s) + except json.JSONDecodeError: + return value + +@dataclass +class AGVDriverConfig: + """AGV驱动配置类""" + host: str + port: int + port_navigation: int # 路径导航专用端口 + timeout_s: float = 3.0 + debug_hex: bool = False + +class AGVDriver: + """AGV驱动类""" + + def __init__(self, cfg: AGVDriverConfig): + """ + 功能: + 初始化AGV驱动 + 参数: + cfg: AGV驱动配置对象 + """ + self.cfg = cfg + self._sock: Optional[socket.socket] = None + self._sock_nav: Optional[socket.socket] = None # 导航专用socket + self._req_id: int = 1 # 请求序列号, 每次请求递增 + + def connect(self) -> None: + """ + 功能: + 连接到AGV控制器 + """ + if self._sock is not None: + return + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.cfg.timeout_s) + s.connect((self.cfg.host, self.cfg.port)) + self._sock = s + logger.debug("已连接到查询端口 %s:%s", self.cfg.host, self.cfg.port) + + def connect_navigation(self) -> None: + """ + 功能: + 连接到AGV导航控制器 + """ + if self._sock_nav is not None: + return + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.cfg.timeout_s) + s.connect((self.cfg.host, self.cfg.port_navigation)) + self._sock_nav = s + logger.debug("已连接到导航端口 %s:%s", self.cfg.host, self.cfg.port_navigation) + + def close(self) -> None: + """ + 功能: + 关闭与AGV控制器的连接 + """ + if self._sock: + try: + self._sock.close() + finally: + self._sock = None + if self._sock_nav: + try: + self._sock_nav.close() + finally: + self._sock_nav = None + + def reconnect(self) -> None: + """ + 功能: + 重新连接到AGV查询端口, 先关闭已有连接再创建新连接 + """ + # 关闭旧的查询端口socket + if self._sock is not None: + try: + self._sock.close() + except Exception: + pass + self._sock = None + # 创建新连接 + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.cfg.timeout_s) + s.connect((self.cfg.host, self.cfg.port)) + self._sock = s + logger.info("已重新连接到查询端口 %s:%s", self.cfg.host, self.cfg.port) + + def reconnect_navigation(self) -> None: + """ + 功能: + 重新连接到AGV导航端口, 先关闭已有连接再创建新连接 + """ + if self._sock_nav is not None: + try: + self._sock_nav.close() + except Exception: + pass + self._sock_nav = None + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(self.cfg.timeout_s) + s.connect((self.cfg.host, self.cfg.port_navigation)) + self._sock_nav = s + logger.info("已重新连接到导航端口 %s:%s", self.cfg.host, self.cfg.port_navigation) + + def _send_and_recv_json( + self, + cmd_id: int, + payload_obj: Optional[Dict[str, Any]] = None, + expect_cmd_id: Optional[int] = None, + use_navigation_port: bool = False, + ) -> Dict[str, Any]: + """ + 功能: + 发送JSON请求并接收JSON响应, 参考厂家协议实现 + 参数: + cmd_id: 命令ID (消息类型) + payload_obj: 可选的请求负载对象 + expect_cmd_id: 期望的响应命令ID + use_navigation_port: 是否使用导航专用端口 + 返回: + Dict[str, Any], 响应数据字典 + """ + # 根据命令类型选择socket + if use_navigation_port: + if self._sock_nav is None: + raise RuntimeError("未连接到导航端口, 请先调用connect_navigation()") + sock = self._sock_nav + else: + if self._sock is None: + raise RuntimeError("未连接, 请先调用connect()") + sock = self._sock + + # 构造JSON负载 + payload_bytes = b"" + if payload_obj is not None: + payload_bytes = json.dumps(payload_obj, separators=(",", ":"), ensure_ascii=True).encode("ascii") + + # 使用当前请求序列号构造请求帧 + current_req_id = self._req_id + frame = _build_frame(cmd_id, payload_bytes, req_id=current_req_id) + self._req_id += 1 # 请求序列号递增 + + if self.cfg.debug_hex: + logger.info("发送请求 [序列号=%d 命令=0x%04X 负载长度=%d 端口=%s]", + current_req_id, cmd_id, len(payload_bytes), + "导航" if use_navigation_port else "查询") + logger.info("发送帧: %s", _hexdump(frame, 256)) + if payload_obj: + logger.info("发送负载: %s", json.dumps(payload_obj, ensure_ascii=False)) + + # 发送请求 + sock.sendall(frame) + + # 接收响应头部 + rsp_hdr = _recv_exact(sock, FRAME_HEAD_SIZE) + rsp_len, rsp_cmd, rsp_seq = _parse_frame_header(rsp_hdr) + + if self.cfg.debug_hex: + logger.info("接收响应 [序列号=%d 命令=0x%04X 负载长度=%d]", + rsp_seq, rsp_cmd, rsp_len) + logger.info("接收头部: %s", _hexdump(rsp_hdr, 64)) + + # 验证响应命令ID + if expect_cmd_id is not None and rsp_cmd != expect_cmd_id: + raise ValueError(f"响应命令ID不匹配: 0x{rsp_cmd:04X} (期望 0x{expect_cmd_id:04X})") + + # 接收响应负载 + rsp_payload = _recv_exact(sock, rsp_len) if rsp_len > 0 else b"" + + if self.cfg.debug_hex and rsp_payload: + logger.info("接收负载(前512字节): %s", _hexdump(rsp_payload, 512)) + + # 解析JSON负载 + data = _decode_json_payload(rsp_payload) + + if self.cfg.debug_hex and data: + logger.info("解析响应: %s", json.dumps(data, ensure_ascii=False, indent=2)) + + # 特殊处理move_status_info字段(可能是嵌套JSON字符串) + if "move_status_info" in data: + data["move_status_info"] = _try_parse_json_string(data["move_status_info"]) + + return data + + def query_agv_nav_status(self, simple: bool = False) -> Dict[str, Any]: + """ + 功能: + 查询AGV当前导航状态 + 参数: + simple: True表示只返回任务状态, False返回完整信息 + 返回: + Dict[str, Any], 包含导航状态的响应字典 + """ + payload = {"simple": True} if simple else None + return self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_STATUS_TASK, + payload_obj=payload, + expect_cmd_id=RSP_CMD_ROBOT_STATUS_TASK, + ) + + def query_robot_location(self) -> Dict[str, Any]: + """ + 功能: + 查询机器人当前位置信息 + 返回: + Dict[str, Any], 包含位置信息的响应字典, 包括: + - x: X坐标 + - y: Y坐标 + - angle: 角度(弧度) + - confidence: 定位置信度 + - current_station: 当前站点名称 + - last_station: 上一个站点名称 + """ + return self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_STATUS_LOC, + payload_obj=None, + expect_cmd_id=RSP_CMD_ROBOT_STATUS_LOC, + ) + + def query_battery_status(self, simple: bool = True) -> Dict[str, Any]: + """ + 功能: + 查询机器人电池状态 + 参数: + simple: True表示只返回电池电量, False返回完整信息, 默认True + 返回: + Dict[str, Any], 包含电池状态的响应字典, 包括: + - battery_level: 电池电量, 范围[0, 1] + - battery_temp: 电池温度, 单位℃ (仅完整模式) + - charging: 是否正在充电 (仅完整模式) + - voltage: 电压, 单位V (仅完整模式) + - current: 电流, 单位A (仅完整模式) + - max_charge_voltage: 允许充电的最大电压, 单位V (仅完整模式) + - max_charge_current: 允许充电的最大电流, 单位A (仅完整模式) + - manual_charge: 是否连接手动充电器 (仅完整模式) + - auto_charge: 是否连接自动充电桩 (仅完整模式) + - battery_cycle: 电池循环次数 (仅完整模式) + - ret_code: API错误码 + - err_msg: 错误信息 + """ + payload = {"simple": True} if simple else {"simple": False} + return self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_STATUS_BATTERY, + payload_obj=payload, + expect_cmd_id=RSP_CMD_ROBOT_STATUS_BATTERY, + ) + + def send_navigate_command( + self, + target_id: str, + source_id: str = "SELF_POSITION", + task_id: Optional[str] = None, + angle: Optional[float] = None, + method: Optional[str] = None, + max_speed: Optional[float] = None, + max_wspeed: Optional[float] = None, + max_acc: Optional[float] = None, + max_wacc: Optional[float] = None, + duration: Optional[int] = None, + orientation: Optional[float] = None, + spin: Optional[bool] = None, + delay: Optional[int] = None, + start_rot_dir: Optional[int] = None, + end_rot_dir: Optional[int] = None, + reach_dist: Optional[float] = None, + reach_angle: Optional[float] = None, + ) -> Dict[str, Any]: + """ + 功能: + 发送路径导航命令, 控制AGV前往指定目标站点, 发送完命令立即返回, 不等待导航完成 + 参数: + target_id: 目标站点名称, 必填 + source_id: 起始站点名称, 固定为"SELF_POSITION"表示当前位置 + task_id: 任务编号, 可选 + angle: 目标站点角度值, 单位rad, 可选 + method: 运动方式, "forward"(正走)或"backward"(倒走), 可选 + max_speed: 最大速度, 单位m/s, 可选 + max_wspeed: 最大角速度, 单位rad/s, 可选 + max_acc: 最大加速度, 单位m/s^2, 可选 + max_wacc: 最大角加速度, 单位rad/s^2, 可选 + duration: 导航结束后等待时间, 单位ms, 可选 + orientation: 全向车保持的角度, 可选 + spin: 是否随动, 可选 + delay: 延迟结束导航状态的时间, 单位ms, 可选 + start_rot_dir: 起步原地旋转方向(-1顺时针, 0近方向, 1逆时针), 可选 + end_rot_dir: 到点原地旋转方向(-1顺时针, 0近方向, 1逆时针), 可选 + reach_dist: 到点位置精度, 单位m, 可选 + reach_angle: 到点角度精度, 单位rad, 可选 + 返回: + Dict[str, Any], 包含导航命令发送响应的字典 + """ + # 确保导航端口已连接 + self.connect_navigation() + + payload = { + "id": target_id, + "source_id": source_id, + } + + # 只添加用户提供的可选参数 + if task_id is not None: + payload["task_id"] = task_id + if angle is not None: + payload["angle"] = angle + if method is not None: + payload["method"] = method + # 速度和加速度参数, 使用配置文件中的默认值 + payload["max_speed"] = max_speed if max_speed is not None else AGV_MAX_SPEED + payload["max_wspeed"] = max_wspeed if max_wspeed is not None else AGV_MAX_WSPEED + payload["max_acc"] = max_acc if max_acc is not None else AGV_MAX_ACC + payload["max_wacc"] = max_wacc if max_wacc is not None else AGV_MAX_WACC + if duration is not None: + payload["duration"] = duration + if orientation is not None: + payload["orientation"] = orientation + if spin is not None: + payload["spin"] = spin + if delay is not None: + payload["delay"] = delay + if start_rot_dir is not None: + payload["start_rot_dir"] = start_rot_dir + if end_rot_dir is not None: + payload["end_rot_dir"] = end_rot_dir + if reach_dist is not None: + payload["reach_dist"] = reach_dist + if reach_angle is not None: + payload["reach_angle"] = reach_angle + + # 发送导航指令并返回响应 + response = self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_TASK_GOTARGET, + payload_obj=payload, + expect_cmd_id=RSP_CMD_ROBOT_TASK_GOTARGET, + use_navigation_port=True, + ) + + logger.info("导航指令已发送, 目标站点: %s", target_id) + return response + + def navigate_to_target( + self, + target_id: str, + source_id: str = "SELF_POSITION", + task_id: Optional[str] = None, + angle: Optional[float] = None, + method: Optional[str] = None, + max_speed: Optional[float] = None, + max_wspeed: Optional[float] = None, + max_acc: Optional[float] = None, + max_wacc: Optional[float] = None, + duration: Optional[int] = None, + orientation: Optional[float] = None, + spin: Optional[bool] = None, + delay: Optional[int] = None, + start_rot_dir: Optional[int] = None, + end_rot_dir: Optional[int] = None, + reach_dist: Optional[float] = None, + reach_angle: Optional[float] = None, + ) -> Dict[str, Any]: + """ + 功能: + 路径导航, 控制AGV前往指定目标站点, 并等待导航完成 + 参数: + target_id: 目标站点名称, 必填 + source_id: 起始站点名称, 固定为"SELF_POSITION"表示当前位置 + task_id: 任务编号, 可选 + angle: 目标站点角度值, 单位rad, 可选 + method: 运动方式, "forward"(正走)或"backward"(倒走), 可选 + max_speed: 最大速度, 单位m/s, 可选 + max_wspeed: 最大角速度, 单位rad/s, 可选 + max_acc: 最大加速度, 单位m/s^2, 可选 + max_wacc: 最大角加速度, 单位rad/s^2, 可选 + duration: 导航结束后等待时间, 单位ms, 可选 + orientation: 全向车保持的角度, 可选 + spin: 是否随动, 可选 + delay: 延迟结束导航状态的时间, 单位ms, 可选 + start_rot_dir: 起步原地旋转方向(-1顺时针, 0近方向, 1逆时针), 可选 + end_rot_dir: 到点原地旋转方向(-1顺时针, 0近方向, 1逆时针), 可选 + reach_dist: 到点位置精度, 单位m, 可选 + reach_angle: 到点角度精度, 单位rad, 可选 + 返回: + Dict[str, Any], 包含最终导航状态的字典 + """ + # 确保导航端口已连接 + self.connect_navigation() + # 确保查询端口已连接, 用于状态查询 + self.connect() + + payload = { + "id": target_id, + "source_id": source_id, + } + + # 只添加用户提供的可选参数 + if task_id is not None: + payload["task_id"] = task_id + if angle is not None: + payload["angle"] = angle + if method is not None: + payload["method"] = method + # 速度和加速度参数, 使用配置文件中的默认值 + payload["max_speed"] = max_speed if max_speed is not None else AGV_MAX_SPEED + payload["max_wspeed"] = max_wspeed if max_wspeed is not None else AGV_MAX_WSPEED + payload["max_acc"] = max_acc if max_acc is not None else AGV_MAX_ACC + payload["max_wacc"] = max_wacc if max_wacc is not None else AGV_MAX_WACC + if duration is not None: + payload["duration"] = duration + if orientation is not None: + payload["orientation"] = orientation + if spin is not None: + payload["spin"] = spin + if delay is not None: + payload["delay"] = delay + if start_rot_dir is not None: + payload["start_rot_dir"] = start_rot_dir + if end_rot_dir is not None: + payload["end_rot_dir"] = end_rot_dir + if reach_dist is not None: + payload["reach_dist"] = reach_dist + if reach_angle is not None: + payload["reach_angle"] = reach_angle + + # 发送导航指令 + self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_TASK_GOTARGET, + payload_obj=payload, + expect_cmd_id=RSP_CMD_ROBOT_TASK_GOTARGET, + use_navigation_port=True, + ) + + logger.info("导航指令已发送, 目标站点: %s", target_id) + + # 循环查询导航状态, 直到完成 + max_reconnect_attempts = 5 # 最大连续重连次数 + consecutive_errors = 0 # 连续错误计数器 + + while True: + time.sleep(0.5) # 每0.5秒查询一次, 避免频繁查询 + + try: + status_data = self.query_agv_nav_status(simple=False) + # 查询成功, 重置连续错误计数 + consecutive_errors = 0 + + task_status = status_data.get("task_status") + + if task_status is None: + logger.warning("未获取到任务状态, 继续查询") + continue + + status_name = TASK_STATUS_MAP.get(task_status, "UNKNOWN") + logger.debug("当前导航状态: %s (%d)", status_name, task_status) + + if task_status == 4: # COMPLETED + logger.info("已运动到目标点位: %s", target_id) + return status_data + elif task_status == 5: # FAILED + logger.error("导航失败, 目标站点: %s", target_id) + return status_data + elif task_status == 6: # CANCELED + logger.warning("导航已取消, 目标站点: %s", target_id) + return status_data + + except (ConnectionError, ConnectionResetError, OSError) as e: + consecutive_errors += 1 + logger.warning( + "查询导航状态时连接异常(第%d次): %s", + consecutive_errors, e + ) + + if consecutive_errors >= max_reconnect_attempts: + logger.error( + "连续%d次连接异常, 放弃导航状态查询, 目标站点: %s", + consecutive_errors, target_id + ) + raise ConnectionError( + "导航状态查询连续%d次连接失败, 放弃查询" % consecutive_errors + ) from e + + # 尝试重新连接查询端口 + try: + logger.info("正在尝试重新连接查询端口...") + self.reconnect() + except Exception as reconnect_err: + logger.error("重新连接失败: %s", reconnect_err) + + except Exception as e: + consecutive_errors += 1 + logger.error( + "查询导航状态时发生非连接错误(第%d次): %s", + consecutive_errors, e + ) + + if consecutive_errors >= max_reconnect_attempts: + logger.error( + "连续%d次错误, 放弃导航状态查询, 目标站点: %s", + consecutive_errors, target_id + ) + raise + + def play_sound( + self, + target_id: str = "SELF_POSITION", + source_id: str = "SELF_POSITION", + task_id: Optional[str] = None, + sound_name: Optional[str] = None, + loop: bool = False, + stop: bool = False, + ) -> Dict[str, Any]: + """ + 功能: + 控制AGV音频播放 + 参数: + target_id: 目标站点名称, 默认"SELF_POSITION"表示原地执行 + source_id: 起始站点名称, 默认"SELF_POSITION" + task_id: 任务编号, 可选 + sound_name: 音频名称, 可选 + loop: True表示循环播放, False表示只播放一次, 默认False + stop: True表示停止播放, False表示不处理, 默认False + 返回: + Dict[str, Any], 包含音频控制响应的字典 + """ + payload = { + "id": target_id, + "source_id": source_id, + "operation": "sound", + "sounds_args": {}, + } + + if task_id is not None: + payload["task_id"] = task_id + + # 构建sounds_args参数 + if sound_name is not None: + payload["sounds_args"]["name"] = sound_name + payload["sounds_args"]["loop"] = 1 if loop else 0 + payload["sounds_args"]["stop"] = 1 if stop else 0 + + return self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_TASK_GOTARGET, + payload_obj=payload, + expect_cmd_id=RSP_CMD_ROBOT_TASK_GOTARGET, + use_navigation_port=True, + ) + + def rotate_in_place( + self, + move_angle: float, + speed_w: float = 1.57, + loc_mode: int = 1, + ) -> Dict[str, Any]: + """ + 功能: + 控制AGV原地旋转 + 参数: + move_angle: 旋转角度, 单位rad, 必填 + speed_w: 角速度, 单位rad/s, 默认1.57(约90度/秒) + loc_mode: 定位模式, 1表示激光定位, 0表示里程定位, 默认1 + 返回: + Dict[str, Any], 包含旋转响应的字典 + """ + payload = { + "move_angle": move_angle, + "speed_w": speed_w, + "skill_name": "GoByOdometer", + "loc_mode": loc_mode, + } + + return self._send_and_recv_json( + cmd_id=REQ_CMD_ROBOT_TASK_GOTARGET, + payload_obj=payload, + expect_cmd_id=RSP_CMD_ROBOT_TASK_GOTARGET, + use_navigation_port=True, + ) + + @staticmethod + def pretty_status(data: Dict[str, Any]) -> str: + """ + 功能: + 格式化状态数据为可读字符串 + 参数: + data: 状态数据字典 + 返回: + str, 格式化后的状态信息 + """ + ts = data.get("task_status") + tt = data.get("task_type") + + lines = [] + if ts is not None: + lines.append(f"任务状态: {ts} ({TASK_STATUS_MAP.get(ts, 'UNKNOWN')})") + if tt is not None: + lines.append(f"任务类型: {tt} ({TASK_TYPE_MAP.get(tt, 'UNKNOWN')})") + if "target_id" in data: + lines.append(f"目标ID: {data.get('target_id')}") + if "target_point" in data: + lines.append(f"目标点: {data.get('target_point')}") + if "finished_path" in data: + lines.append(f"已完成路径: {data.get('finished_path')}") + if "unfinished_path" in data: + lines.append(f"未完成路径: {data.get('unfinished_path')}") + if "move_status_info" in data: + lines.append(f"移动状态信息: {data.get('move_status_info')}") + if "containers" in data: + lines.append(f"容器信息: {data.get('containers')}") + if "ret_code" in data: + lines.append(f"返回码: {data.get('ret_code')}") + if "err_msg" in data: + lines.append(f"错误信息: {data.get('err_msg')}") + + return "\n".join(lines) if lines else json.dumps(data, ensure_ascii=False, indent=2) + + +def main() -> None: + """ + 功能: + 交互式测试主函数 + """ + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") + + driver = AGVDriver(AGVDriverConfig( + host=AGV_HOST, + port=AGV_PORT, + port_navigation=AGV_PORT_NAVIGATION, + timeout_s=AGV_TIMEOUT, + debug_hex=False, + )) + + print("=" * 60) + print("AGV导航状态查询交互式测试") + print("=" * 60) + print(f"当前配置: {AGV_HOST}:{AGV_PORT}") + print("=" * 60) + + try: + driver.connect() + print("连接成功\n") + + while True: + print("\n请选择操作:") + print("1. 查询完整导航状态") + print("2. 查询简单导航状态") + print("3. 路径导航到目标站点") + print("4. 查询机器人位置") + print("5. 播放音乐") + print("6. 停止音乐") + print("7. 原地旋转") + print("8. 开启十六进制调试模式") + print("9. 关闭十六进制调试模式") + print("10. 查询电池状态(简单)") + print("11. 查询电池状态(完整)") + print("0. 退出") + + choice = input("\n请输入选项 (0-11): ").strip() + + if choice == "0": + print("退出测试") + break + elif choice == "1": + print("\n正在查询完整导航状态...") + try: + data = driver.query_agv_nav_status(simple=False) + print("\n=== 查询结果 ===") + print(AGVDriver.pretty_status(data)) + except Exception as e: + logger.error("查询失败: %s", e) + elif choice == "2": + print("\n正在查询简单导航状态...") + try: + data = driver.query_agv_nav_status(simple=True) + print("\n=== 查询结果 ===") + print(AGVDriver.pretty_status(data)) + except Exception as e: + logger.error("查询失败: %s", e) + elif choice == "3": + target = input("请输入目标站点名称: ").strip() + if not target: + print("目标站点名称不能为空") + continue + print(f"\n正在导航到目标站点 {target}...") + try: + data = driver.navigate_to_target(target_id=target) + print("\n=== 导航响应 ===") + print(json.dumps(data, ensure_ascii=False, indent=2)) + except Exception as e: + logger.error("导航失败: %s", e) + elif choice == "4": + print("\n正在查询机器人位置...") + try: + data = driver.query_robot_location() + print("\n=== 位置信息 ===") + print(f"X坐标: {data.get('x')}") + print(f"Y坐标: {data.get('y')}") + print(f"角度: {data.get('angle')} rad") + print(f"置信度: {data.get('confidence')}") + print(f"当前站点: {data.get('current_station')}") + print(f"上一站点: {data.get('last_station')}") + except Exception as e: + logger.error("查询位置失败: %s", e) + elif choice == "5": + sound_name = input("请输入音频名称 (默认: navigation): ").strip() or "navigation" + loop_input = input("是否循环播放? (y/n, 默认: n): ").strip().lower() + loop = loop_input == "y" + print(f"\n正在播放音乐 {sound_name} (循环: {loop})...") + try: + data = driver.play_sound(sound_name=sound_name, loop=loop) + print("\n=== 音乐控制响应 ===") + print(json.dumps(data, ensure_ascii=False, indent=2)) + except Exception as e: + logger.error("播放音乐失败: %s", e) + elif choice == "6": + print("\n正在停止音乐...") + try: + data = driver.play_sound(stop=True) + print("\n=== 音乐控制响应 ===") + print(json.dumps(data, ensure_ascii=False, indent=2)) + except Exception as e: + logger.error("停止音乐失败: %s", e) + elif choice == "7": + angle_input = input("请输入旋转角度(度, 默认: 90): ").strip() + try: + angle_deg = float(angle_input) if angle_input else 90.0 + angle_rad = angle_deg * 3.14159265359 / 180.0 # 转换为弧度 + print(f"\n正在原地旋转 {angle_deg} 度 ({angle_rad:.4f} 弧度)...") + data = driver.rotate_in_place(move_angle=angle_rad) + print("\n=== 旋转响应 ===") + print(json.dumps(data, ensure_ascii=False, indent=2)) + except ValueError: + print("无效的角度值") + except Exception as e: + logger.error("旋转失败: %s", e) + elif choice == "8": + driver.cfg.debug_hex = True + print("已开启十六进制调试模式") + elif choice == "9": + driver.cfg.debug_hex = False + print("已关闭十六进制调试模式") + elif choice == "10": + print("\n正在查询电池状态(简单)...") + try: + data = driver.query_battery_status(simple=True) + print("\n=== 电池状态 ===") + battery_level = data.get("battery_level") + if battery_level is not None: + print(f"电池电量: {battery_level * 100:.1f}%") + else: + print("未获取到电池电量") + if "ret_code" in data: + print(f"返回码: {data.get('ret_code')}") + if "err_msg" in data: + print(f"错误信息: {data.get('err_msg')}") + except Exception as e: + logger.error("查询电池状态失败: %s", e) + elif choice == "11": + print("\n正在查询电池状态(完整)...") + try: + data = driver.query_battery_status(simple=False) + print("\n=== 电池状态 ===") + battery_level = data.get("battery_level") + if battery_level is not None: + print(f"电池电量: {battery_level * 100:.1f}%") + if "battery_temp" in data: + print(f"电池温度: {data.get('battery_temp')}℃") + if "charging" in data: + print(f"正在充电: {'是' if data.get('charging') else '否'}") + if "voltage" in data: + print(f"电压: {data.get('voltage')}V") + if "current" in data: + print(f"电流: {data.get('current')}A") + if "max_charge_voltage" in data: + max_v = data.get("max_charge_voltage") + print(f"最大充电电压: {max_v}V" if max_v != -1 else "最大充电电压: 不支持") + if "max_charge_current" in data: + max_c = data.get("max_charge_current") + print(f"最大充电电流: {max_c}A" if max_c != -1 else "最大充电电流: 不支持") + if "manual_charge" in data: + print(f"手动充电器连接: {'是' if data.get('manual_charge') else '否'}") + if "auto_charge" in data: + print(f"自动充电桩连接: {'是' if data.get('auto_charge') else '否'}") + if "battery_cycle" in data: + print(f"电池循环次数: {data.get('battery_cycle')}") + if "ret_code" in data: + print(f"返回码: {data.get('ret_code')}") + if "err_msg" in data: + print(f"错误信息: {data.get('err_msg')}") + except Exception as e: + logger.error("查询电池状态失败: %s", e) + else: + print("无效选项, 请重新输入") + + except KeyboardInterrupt: + print("\n\n用户中断") + except Exception as e: + logger.error("连接失败: %s", e) + finally: + driver.close() + print("连接已关闭") + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_agv/driver/arm_driver.py b/unilabos/devices/eit_agv/driver/arm_driver.py new file mode 100644 index 00000000..0f1fd21a --- /dev/null +++ b/unilabos/devices/eit_agv/driver/arm_driver.py @@ -0,0 +1,1387 @@ +# coding:utf-8 +""" +功能: + AGV机械臂驱动模块, 封装DucoCobot机械臂的核心功能 + 主要用于托盘搬运和物料转移任务 +""" + +import logging +from .DucoCobot.DucoCobot import DucoCobot +from .DucoCobot.gen_py.robot.ttypes import Op, PointOp + +# 配置日志 +logger = logging.getLogger(__name__) + + +class ArmDriver: + """ + 功能: + AGV机械臂驱动类, 提供托盘搬运和物料转移所需的核心功能 + """ + + def __init__(self, ip=None, port=None, timeout=None): + """ + 功能: + 初始化机械臂驱动 + 参数: + ip: 机械臂IP地址, 默认使用配置文件中的值 + port: 机械臂端口, 默认使用配置文件中的值 + timeout: socket超时时间, 单位毫秒, 默认使用配置文件中的值 + """ + self.robot = DucoCobot(ip, port, timeout) + self.is_connected = False + self.ip = self.robot.ip + self.port = self.robot.port + self.timeout = self.robot.timeout + self.current_gripper = "gripper_type_a" # 当前安装的夹爪名称,目前夹爪有问题,先默认是type as + + # 运动参数最大值配置 + self.max_joint_velocity = 2.5 * 3.14159 # 最大关节角速度 rad/s + self.max_joint_acceleration = 3.93 # 最大关节角加速度 rad/s² + self.max_linear_velocity = 2 # 最大末端线速度 m/s + self.max_linear_acceleration = 2 # 最大末端线加速度 m/s² + logger.debug("AGV机械臂驱动初始化完成") + + # ==================== 连接管理 ==================== + + def connect(self): + """ + 功能: + 连接机械臂 + 返回: + bool, True表示连接成功, False表示连接失败 + """ + result = self.robot.open() + if result == 0: + self.is_connected = True + logger.debug("机械臂连接成功") + return True + else: + self.is_connected = False + logger.error("机械臂连接失败") + return False + + def disconnect(self): + """ + 功能: + 断开机械臂连接 + 返回: + bool, True表示断开成功, False表示断开失败 + """ + result = self.robot.close() + if result == 0: + self.is_connected = False + logger.debug("机械臂断开连接成功") + return True + else: + logger.error("机械臂断开连接失败") + return False + + def reconnect(self): + """ + 功能: + 重新连接机械臂 + 返回: + bool, True表示重连成功, False表示重连失败 + """ + logger.warning("尝试重新连接机械臂") + # 先断开旧连接 + try: + self.robot.close() + except Exception: + pass + + # 重新创建连接对象 + self.robot = DucoCobot(self.ip, self.port, self.timeout) + + # 尝试连接 + return self.connect() + + # ==================== 电源和使能控制 ==================== + + def power_on(self, block=True): + """ + 功能: + 机械臂上电 + 参数: + block: 是否阻塞, True表示阻塞执行, False表示非阻塞 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("机械臂上电") + return self.robot.power_on(block) + + def power_off(self, block=True): + """ + 功能: + 机械臂下电 + 参数: + block: 是否阻塞, True表示阻塞执行, False表示非阻塞 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("机械臂下电") + return self.robot.power_off(block) + + def enable(self, block=True): + """ + 功能: + 机械臂上使能 + 参数: + block: 是否阻塞, True表示阻塞执行, False表示非阻塞 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("机械臂上使能") + return self.robot.enable(block) + + def disable(self, block=True): + """ + 功能: + 机械臂下使能 + 参数: + block: 是否阻塞, True表示阻塞执行, False表示非阻塞 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("机械臂下使能") + return self.robot.disable(block) + + # ==================== Op 和 PointOp 辅助方法 ==================== + + def create_op(self, + time_or_dist_1=0, trig_io_1=0, trig_value_1=False, + trig_time_1=0.0, trig_dist_1=0.0, trig_event_1="", + time_or_dist_2=0, trig_io_2=0, trig_value_2=False, + trig_time_2=0.0, trig_dist_2=0.0, trig_event_2="", + time_or_dist_3=0, trig_io_3=0, trig_value_3=False, + trig_time_3=0.0, trig_dist_3=0.0, trig_event_3=""): + """ + 功能: + 创建Op对象, 用于定义运动过程中的触发事件(轨迹起点/终点/暂停或停止) + 参数(按协议枚举): + time_or_dist_x: + 0: 不启用 + 1: 时间触发 + 2: 距离触发 (第3组目前协议说明不支持距离触发) + trig_io_x: + 触发IO或寄存器索引号;不启用时建议填0作为占位 + trig_value_x: + IO时False=低电平, True=高电平;寄存器时会按类型转换后输出 + trig_time_x: + 时间触发参数(ms);不启用时建议填0 + trig_dist_x: + 距离触发参数(m);不启用时建议填0 (第3组无效) + trig_event_x: + 自定义事件名;若不为空则优先触发事件,不触发IO/寄存器;默认必须为空字符串 + 返回: + Op对象 + """ + return Op( + time_or_dist_1, trig_io_1, trig_value_1, trig_time_1, trig_dist_1, trig_event_1, + time_or_dist_2, trig_io_2, trig_value_2, trig_time_2, trig_dist_2, trig_event_2, + time_or_dist_3, trig_io_3, trig_value_3, trig_time_3, trig_dist_3, trig_event_3 + ) + + def create_point_op(self, pose, op=None): + """ + 功能: + 创建PointOp对象, 用于定义带触发事件的运动点 + 参数: + pose: 位姿[x,y,z,rx,ry,rz], 位置单位mm, 姿态单位rad + op: Op对象, 定义该点的触发事件, 为None时使用默认Op + 返回: + PointOp对象 + """ + if op is None: + op = self.create_op() + # 将位置从mm转换为m + pose_m = [pose[0]/1000.0, pose[1]/1000.0, pose[2]/1000.0, pose[3], pose[4], pose[5]] + return PointOp(pose_m, op) + + # ==================== 基础运动控制 ==================== + def move_to_joints(self, joints_list, v, a, r=0, block=True, def_acc=False, retry=True): + """ + 功能: + 控制机械臂从当前状态按照关节运动方式移动到目标关节角状态 + 参数: + joints_list: 目标关节角度列表[q1,q2,q3,q4,q5,q6], 范围[-2*PI, 2*PI]即[-6.28, 6.28], 单位rad + v: 速度百分比, 范围(0, 1], 相对于最大关节角速度的比例 + a: 加速度百分比, 范围(0, 1], 相对于最大关节角加速度的比例 + r: 轨迹融合半径, 单位m, 默认值为0表示无融合. 当数值大于0时表示与下一条运动融合, 产生融合运动时机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + block: 是否阻塞型指令, True表示阻塞执行, False表示非阻塞立即返回 + def_acc: 是否使用默认加速度, 默认为False. 当开启默认加速度时执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动从而提升节拍 + retry: 连接断开时是否自动重试, 默认True + 返回: + 阻塞执行时返回任务结束状态(无融合为Finished, 有融合为Interrupt), 非阻塞执行时返回任务ID, 用户可以调用get_task_state(id)函数查询当前任务的执行状态 + """ + import math + + # 复制关节角度列表, 避免修改原始数据 + adjusted_joints = list(joints_list) + + # 检查并调整j6角度到合理范围[-4.3, 1.7] + if adjusted_joints[5] > 1.7: + # j6超过上限, 减去2π + adjusted_joints[5] -= 2 * math.pi + logger.debug(f"j6角度超过上限, 调整前: {joints_list[5]:.3f} rad, 调整后: {adjusted_joints[5]:.3f} rad") + elif adjusted_joints[5] < -4.3: + # j6低于下限, 加上2π + adjusted_joints[5] += 2 * math.pi + logger.debug(f"j6角度低于下限, 调整前: {joints_list[5]:.3f} rad, 调整后: {adjusted_joints[5]:.3f} rad") + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_joint_velocity + actual_a = a * self.max_joint_acceleration + logger.debug(f"关节运动到目标位置: {adjusted_joints}, 速度: {v:.1%} ({actual_v:.3f} rad/s), 加速度: {a:.1%} ({actual_a:.3f} rad/s²)") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.movej2(adjusted_joints, actual_v, actual_a, r, block, op, def_acc) + + def move_to_pose(self, pose, v, a, r=0, q_near=None, tool="", wobj="", block=True, def_acc=False): + """ + 功能: + 控制机械臂从当前状态按照各关节相位同步运动方式移动到末端目标位姿 + 参数: + pose: 目标机器人工具在参考机器人工件坐标系中的位姿[x,y,z,rx,ry,rz], 位置单位mm, 姿态以Rx,Ry,Rz表示范围[-2*PI, 2*PI]即[-6.28, 6.28], 单位rad + v: 速度百分比, 范围(0, 1], 相对于最大关节角速度的比例 + a: 加速度百分比, 范围(0, 1], 相对于最大关节角加速度的比例 + r: 轨迹融合半径, 单位mm, 默认值为0表示无融合. 当数值大于0时表示与下一条运动融合, 产生融合运动时机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + q_near: 目标点附近位置对应的关节角度, 用于确定逆运动学选解, 为空时使用当前位置 + tool: 工具坐标系名称, 为空时使用当前工具 + wobj: 工件坐标系名称, 为空时使用当前工件坐标系 + block: 是否阻塞执行, True表示等待运动完成, False表示立即返回 + def_acc: 是否使用默认加速度, 默认为False. 当开启默认加速度时执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动从而提升节拍 + 返回: + 阻塞执行时返回任务结束状态(无融合为Finished, 有融合为Interrupt), 非阻塞执行时返回任务ID, 用户可以调用get_task_state(id)函数查询当前任务的执行状态 + """ + # 将位置从mm转换为m + pose_m = [pose[0]/1000.0, pose[1]/1000.0, pose[2]/1000.0, pose[3], pose[4], pose[5]] + r_m = r / 1000.0 # 融合半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_joint_velocity + actual_a = a * self.max_joint_acceleration + logger.debug(f"关节运动到目标位姿: {pose}mm, 速度: {v:.1%} ({actual_v:.3f} rad/s), 加速度: {a:.1%} ({actual_a:.3f} rad/s²)") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.movej_pose2(pose_m, actual_v, actual_a, r_m, q_near, tool, wobj, block, op, def_acc) + + def move_linear(self, pose, v, a, r=0, q_near=None, tool="", wobj="", block=True, def_acc=False): + """ + 功能: + 控制机械臂末端从当前状态按直线路径移动到目标位姿 + 参数: + pose: 目标工具在工件坐标系下的位姿[x,y,z,rx,ry,rz], 位置单位mm, 姿态范围[-2*PI, 2*PI] rad + v: 速度百分比, 范围(0, 1], 相对于最大末端线速度的比例 + a: 加速度百分比, 范围(0, 1], 相对于最大末端线加速度的比例 + r: 轨迹融合半径, 单位mm, 0 表示无融合, 大于 0 时与下一条运动融合 + q_near: 目标点附近的关节角度, 用于校验逆运动学选解空间, None 时使用当前关节 + tool: 使用的工具名称, 为空时默认当前工具 + wobj: 使用的工件坐标系名称, 为空时默认当前工件坐标系 + block: 指令是否阻塞, False 表示非阻塞立即返回 + def_acc: 是否使用默认加速度, True 时忽略 a 由系统计算可用最大加速度 + 返回: + 阻塞执行返回任务结束状态(Finished 无融合或 Interrupt 有融合), 非阻塞执行返回任务ID, 可调用 get_task_state(id) 查询状态 + """ + # 将位置从mm转换为m + pose_m = [pose[0]/1000.0, pose[1]/1000.0, pose[2]/1000.0, pose[3], pose[4], pose[5]] + r_m = r / 1000.0 # 融合半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_linear_velocity + actual_a = a * self.max_linear_acceleration + logger.debug(f"直线运动到目标位姿: {pose}mm, 速度: {v:.1%} ({actual_v:.3f} m/s), 加速度: {a:.1%} ({actual_a:.3f} m/s²)") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.movel(pose_m, actual_v, actual_a, r_m, q_near, tool, wobj, block, op, def_acc) + + def movec(self, p1, p2, v, a, r=0, mode=0, q_near=None, tool="", wobj="", block=True, def_acc=True, arc_rad=0): + """ + 功能: + 控制机械臂做圆弧运动, 起始点为当前位姿点, 途径p1点, 终点为p2点 + 参数: + p1: 圆弧运动过程中任意机器人工具在参考机器人工件坐标系中的位姿中间点, 位置单位mm, 姿态以Rx,Ry,Rz表示范围[-2*PI, 2*PI], 单位rad + p2: 目标机器人工具在参考机器人工件坐标系中的位姿, 位置单位mm, 姿态以Rx,Ry,Rz表示范围[-2*PI, 2*PI], 单位rad + v: 速度百分比, 范围(0, 1], 相对于最大末端线速度的比例 + a: 加速度百分比, 范围(0, 1], 相对于最大末端线加速度的比例 + r: 轨迹融合半径, 单位mm, 默认值为0, 表示无融合. 当数值大于0时表示与下一条运动融合. 产生融合运动时, 机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + mode: 姿态控制模式 + 0: 姿态与终点保持一致, 即机器人会以p2点的姿态为目标姿态, 平滑运动到目标姿态 + 1: 姿态与起点保持一致, 即机器人会以开始执行movec函数时机器人末端工具坐标系在工件坐标系中的姿态为准, 始终保持该姿态值 + 2: 姿态受圆心约束, 即机器人会以开始执行movec函数时机器人末端工具坐标系与目标圆弧路径起点处切线方向间关系为参考, 在圆弧运动过程中始终保持末端工具与圆弧实时运动所处位置切线方向参考关系 + q_near: 目标点附近位置对应的关节角度, 用于校验机器人运动过程中逆运动学选解空间 + tool: 设置使用的工具的名称, 为空时默认为当前使用的工具 + wobj: 设置使用的工件坐标系的名称, 为空时默认为当前使用的工件坐标系 + block: 指令是否阻塞型指令, 如果为False表示非阻塞指令, 指令会立即返回 + def_acc: 是否使用默认加速度, 默认为True. 当开启默认加速度时, 执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动, 从而提升节拍 + arc_rad: 圆弧半径, 单位mm + 返回: + 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. 当配置为非阻塞执行, 返回值代表当前任务的id信息, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + """ + # 将位置从mm转换为m + p1_m = [p1[0]/1000.0, p1[1]/1000.0, p1[2]/1000.0, p1[3], p1[4], p1[5]] + p2_m = [p2[0]/1000.0, p2[1]/1000.0, p2[2]/1000.0, p2[3], p2[4], p2[5]] + r_m = r / 1000.0 # 融合半径从mm转换为m + arc_rad_m = arc_rad / 1000.0 # 圆弧半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_linear_velocity + actual_a = a * self.max_linear_acceleration + logger.debug(f"圆弧运动: p1={p1}mm, p2={p2}mm, 速度: {v:.1%} ({actual_v:.3f} m/s), 加速度: {a:.1%} ({actual_a:.3f} m/s²), 模式: {mode}") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.movec(p1_m, p2_m, actual_v, actual_a, r_m, mode, q_near, tool, wobj, block, op, def_acc, arc_rad_m) + + def move_circle(self, p1, p2, v, a, rad=0, mode=1, q_near=None, tool="", wobj="", block=True, def_acc=True): + """ + 功能: + 控制机械臂做圆周运动, 起始点为当前位姿点, 途径p1点和p2点 + 参数: + p1: 圆周运动过程中任意机器人工具在参考机器人工件坐标系中的位姿中间点1, 位置单位mm, 姿态以Rx,Ry,Rz表示范围[-2*PI, 2*PI], 单位rad + p2: 圆周运动过程中任意机器人工具在参考机器人工件坐标系中的位姿中间点2, 最终以机器人初始运动位置-p1-p2的顺序决定最终整圆轨迹, 位置单位mm, 姿态以Rx,Ry,Rz表示范围[-2*PI, 2*PI], 单位rad + v: 速度百分比, 范围(0, 1], 相对于最大末端线速度的比例 + a: 加速度百分比, 范围(0, 1], 相对于最大末端线加速度的比例 + rad: 轨迹融合半径, 单位mm, 默认值为0, 表示无融合. 当数值大于0时表示与下一条运动融合. 产生融合运动时, 机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + mode: 姿态控制模式 + 1: 姿态与起点保持一致, 即机器人会以开始执行movec函数时机器人末端工具坐标系在工件坐标系中的姿态为准, 始终保持该姿态值 + 2: 姿态受圆心约束, 即机器人会以开始执行movec函数时机器人末端工具坐标系与目标圆弧路径起点处切线方向间关系为参考, 在圆弧运动过程中始终保持末端工具与圆弧实时运动所处位置切线方向参考关系 + q_near: 目标点附近位置对应的关节角度, 用于校验机器人运动过程中逆运动学选解空间 + tool: 设置使用的工具的名称, 默认为当前使用的工具 + wobj: 设置使用的工件坐标系的名称, 默认为当前使用的工件坐标系 + block: 指令是否阻塞型指令, 如果为False表示非阻塞指令, 指令会立即返回, 默认为阻塞 + def_acc: 是否使用默认加速度, 默认为True. 当开启默认加速度时, 执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动, 从而提升节拍 + 返回: + 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. 当配置为非阻塞执行, 返回值代表当前任务的id信息, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + """ + # 将位置从mm转换为m + p1_m = [p1[0]/1000.0, p1[1]/1000.0, p1[2]/1000.0, p1[3], p1[4], p1[5]] + p2_m = [p2[0]/1000.0, p2[1]/1000.0, p2[2]/1000.0, p2[3], p2[4], p2[5]] + rad_m = rad / 1000.0 # 融合半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_linear_velocity + actual_a = a * self.max_linear_acceleration + logger.debug(f"圆周运动: p1={p1}mm, p2={p2}mm, 速度: {v:.1%} ({actual_v:.3f} m/s), 加速度: {a:.1%} ({actual_a:.3f} m/s²), 模式: {mode}") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.move_circle(p1_m, p2_m, actual_v, actual_a, rad_m, mode, q_near, tool, wobj, block, op, def_acc) + + def move_tcp_offset(self, pose_offset, v, a, r=0, tool="", block=True, def_acc=False): + """ + 功能: + 控制机械臂沿工具坐标系直线移动一个增量. 偏移量将会转换为齐次变换矩阵右乘于当前机器人末端位姿之上 + 参数: + pose_offset: pose数据类型或长度为6的number型数组, 表示工具坐标系下的位姿偏移量[x,y,z,rx,ry,rz], 位置单位mm, 姿态单位rad + v: 速度百分比, 范围(0, 1], 相对于最大末端线速度的比例, 当x、y、z均为0时, 线速度按比例换算成角速度 + a: 加速度百分比, 范围(0, 1], 相对于最大末端线加速度的比例 + r: 轨迹融合半径, 单位mm, 默认值为0, 表示无融合. 当数值大于0时表示与下一条运动融合. 产生融合运动时, 机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + tool: 设置使用的工具的名称, 为空时默认为当前使用的工具 + block: 指令是否阻塞型指令, 如果为False表示非阻塞指令, 指令会立即返回 + def_acc: 是否使用默认加速度, 默认为False. 当开启默认加速度时, 执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动, 从而提升节拍 + 返回: + 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. 当配置为非阻塞执行, 返回值代表当前任务的id信息, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + """ + # 将位置从mm转换为m + pose_offset_m = [pose_offset[0]/1000.0, pose_offset[1]/1000.0, pose_offset[2]/1000.0, + pose_offset[3], pose_offset[4], pose_offset[5]] + r_m = r / 1000.0 # 融合半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_linear_velocity + actual_a = a * self.max_linear_acceleration + logger.debug(f"沿工具坐标系移动: {pose_offset}mm, 速度: {v:.1%} ({actual_v:.3f} m/s), 加速度: {a:.1%} ({actual_a:.3f} m/s²)") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.tcp_move(pose_offset_m, actual_v, actual_a, r_m, tool, block, op, def_acc) + + def move_tcp_2p(self, p1, p2, v, a, r=0, tool="", wobj="", block=True, def_acc=False): + """ + 功能: + 控制机器人沿工具坐标系直线移动一个增量, 增量为p1与p2点之间的差, 运动的目标点为: 当前点*p1^-1*p2 + 参数: + p1: 表示工具坐标系下的位姿偏移量计算点1[x,y,z,rx,ry,rz], 位置单位mm, 姿态单位rad + p2: 表示工具坐标系下的位姿偏移量计算点2[x,y,z,rx,ry,rz], 位置单位mm, 姿态单位rad + v: 速度百分比, 范围(0, 1], 相对于最大末端线速度的比例, 当x、y、z均为0时, 线速度按比例换算成角速度 + a: 加速度百分比, 范围(0, 1], 相对于最大末端线加速度的比例 + r: 轨迹融合半径, 单位mm, 默认值为0, 表示无融合. 当数值大于0时表示与下一条运动融合. 产生融合运动时, 机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + tool: 设置使用的工具的名称, 默认为当前使用的工具 + wobj: 设置使用的工件坐标系的名称, 默认为当前使用的工件坐标系 + block: 指令是否阻塞型指令, 如果为False表示非阻塞指令, 指令会立即返回 + def_acc: 是否使用默认加速度, 默认为False. 当开启默认加速度时, 执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动, 从而提升节拍 + 返回: + 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. 当配置为非阻塞执行, 返回值代表当前任务的id信息, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + """ + # 将位置从mm转换为m + p1_m = [p1[0]/1000.0, p1[1]/1000.0, p1[2]/1000.0, p1[3], p1[4], p1[5]] + p2_m = [p2[0]/1000.0, p2[1]/1000.0, p2[2]/1000.0, p2[3], p2[4], p2[5]] + r_m = r / 1000.0 # 融合半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_linear_velocity + actual_a = a * self.max_linear_acceleration + logger.debug(f"沿工具坐标系移动(两点法): p1={p1}mm, p2={p2}mm, 速度: {v:.1%} ({actual_v:.3f} m/s), 加速度: {a:.1%} ({actual_a:.3f} m/s²)") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.tcp_move_2p(p1_m, p2_m, actual_v, actual_a, r_m, tool, wobj, block, op, def_acc) + + def move_wobj_offset(self, pose_offset, v, a, r=0, wobj="", block=True, def_acc=False): + """ + 功能: + 控制机械臂沿工件坐标系直线移动一个增量 + 参数: + pose_offset: pose数据类型或长度为6的number型数组, 表示工件坐标系下的位姿偏移量[x,y,z,rx,ry,rz], 位置单位mm, 姿态单位rad + v: 速度百分比, 范围(0, 1], 相对于最大末端线速度的比例, 当x、y、z均为0时, 线速度按比例换算成角速度 + a: 加速度百分比, 范围(0, 1], 相对于最大末端线加速度的比例 + r: 轨迹融合半径, 单位mm, 默认值为0, 表示无融合. 当数值大于0时表示与下一条运动融合. 产生融合运动时, 机器人程序会根据融合预读取设定参数在运动指令执行过程中尝试提前获取后续运动函数 + wobj: 设置使用的工件的名称, 为空时默认为当前使用的工件 + block: 指令是否阻塞型指令, 如果为False表示非阻塞指令, 指令会立即返回 + def_acc: 是否使用默认加速度, 默认为False. 当开启默认加速度时, 执行运动时最大加速度参数不再生效, 系统将会根据实际工况计算机器人可以产生的最大加速度指令进行运动, 从而提升节拍 + 返回: + 当配置为阻塞执行, 返回值代表当前任务结束时的状态, 若无融合为Finished, 若有融合为Interrupt. 当配置为非阻塞执行, 返回值代表当前任务的id信息, 用户可以调用get_noneblock_taskstate(id)函数查询当前任务的执行状态 + """ + # 将位置从mm转换为m + pose_offset_m = [pose_offset[0]/1000.0, pose_offset[1]/1000.0, pose_offset[2]/1000.0, + pose_offset[3], pose_offset[4], pose_offset[5]] + r_m = r / 1000.0 # 融合半径从mm转换为m + + # 将百分比转换为实际速度和加速度值 + actual_v = v * self.max_linear_velocity + actual_a = a * self.max_linear_acceleration + logger.debug(f"沿工件坐标系移动: {pose_offset}mm, 速度: {v:.1%} ({actual_v:.3f} m/s), 加速度: {a:.1%} ({actual_a:.3f} m/s²)") + op = self.create_op() # 创建完整的Op对象,这个必须有 + return self.robot.wobj_move(pose_offset_m, actual_v, actual_a, r_m, wobj, block, op, def_acc) + + # ==================== 任务控制 ==================== + def run_program(self, program_name, block=True): + """ + 功能: + 运行程序脚本 + 参数: + program_name: 脚本程序名称 + block: 是否阻塞执行, True表示等待程序执行完成, False表示立即返回 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.debug(f"运行程序脚本: {program_name}") + return self.robot.run_program(program_name, block) + + def stop(self, block=True): + """ + 功能: + 停止所有任务 + 参数: + block: 是否阻塞执行 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.warning("停止所有任务") + return self.robot.stop(block) + + def pause(self, block=True): + """ + 功能: + 暂停所有任务 + 参数: + block: 是否阻塞执行 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("暂停所有任务") + return self.robot.pause(block) + + def resume(self, block=True): + """ + 功能: + 恢复所有暂停的任务 + 参数: + block: 是否阻塞执行 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("恢复所有任务") + return self.robot.resume(block) + + # ==================== 坐标系设置 ==================== + + def set_tool(self, name, tool_offset, payload, inertia_tensor): + """ + 功能: + 设置工具坐标系参数 + 参数: + name: 工具坐标系名称 + tool_offset: 工具TCP偏移量[x,y,z,rx,ry,rz], 单位mm, rad + payload: 末端负载[mass,x_cog,y_cog,z_cog], 单位kg, mm + inertia_tensor: 惯量矩阵参数[xx,xy,xz,yy,yz,zz], 单位kg*m^2 + 返回: + 任务结束时的状态 + """ + # 将工具偏移量从mm转换为m + tool_offset_m = [tool_offset[0]/1000.0, tool_offset[1]/1000.0, tool_offset[2]/1000.0, + tool_offset[3], tool_offset[4], tool_offset[5]] + # 将负载质心从mm转换为m + payload_m = [payload[0], payload[1]/1000.0, payload[2]/1000.0, payload[3]/1000.0] + + logger.info(f"设置工具坐标系: {name}") + return self.robot.set_tool_data(name, tool_offset_m, payload_m, inertia_tensor) + + def set_workobject(self, name, wobj): + """ + 功能: + 设置工件坐标系 + 参数: + name: 工件坐标系名称 + wobj: 工件坐标系相对于基坐标系的位姿[x,y,z,rx,ry,rz], 单位mm, rad + 返回: + 任务结束时的状态 + """ + # 将位置从mm转换为m + wobj_m = [wobj[0]/1000.0, wobj[1]/1000.0, wobj[2]/1000.0, wobj[3], wobj[4], wobj[5]] + + logger.info(f"设置工件坐标系: {name}") + return self.robot.set_wobj(name, wobj_m) + + def get_tcp_offset(self): + """ + 功能: + 获取当前TCP偏移量 + 返回: + TCP偏移量[x,y,z,rx,ry,rz], 单位mm, rad + """ + offset_m = self.robot.get_tcp_offset() + # 将位置从m转换为mm + if offset_m is not None and len(offset_m) >= 6: + return [offset_m[0]*1000.0, offset_m[1]*1000.0, offset_m[2]*1000.0, + offset_m[3], offset_m[4], offset_m[5]] + return offset_m + + def set_wobj_offset(self, wobj, active=True): + """ + 功能: + 设置工件坐标系偏移量 + 参数: + wobj: 工件坐标系偏移量[x,y,z,rx,ry,rz], 单位mm, rad + active: 是否启用 + 返回: + 任务结束时的状态 + """ + # 将位置从mm转换为m + wobj_m = [wobj[0]/1000.0, wobj[1]/1000.0, wobj[2]/1000.0, wobj[3], wobj[4], wobj[5]] + + logger.info(f"设置工件坐标系偏移: {wobj}mm, 启用: {active}") + return self.robot.set_wobj_offset(wobj_m, active) + + # ==================== 运动学计算 ==================== + + def calculate_forward_kinematics(self, joints_position, tool="", wobj=""): + """ + 功能: + 计算正运动学, 根据关节角度计算末端位姿 + 参数: + joints_position: 关节角度列表, 单位rad + tool: 工具坐标系名称, 为空时使用当前工具 + wobj: 工件坐标系名称, 为空时使用当前工件坐标系 + 返回: + 末端位姿[x,y,z,rx,ry,rz], 单位m, rad + """ + pose_m = self.robot.cal_fkine(joints_position, tool, wobj) + return pose_m + + def calculate_inverse_kinematics(self, pose, q_near=None, tool="", wobj=""): + """ + 功能: + 计算逆运动学, 根据末端位姿计算关节角度 + 参数: + pose: 末端位姿[x,y,z,rx,ry,rz], 单位m, rad + q_near: 参考关节角度, 用于选解, 为空时使用当前关节角度 + tool: 工具坐标系名称, 为空时使用当前工具 + wobj: 工件坐标系名称, 为空时使用当前工件坐标系 + 返回: + 关节角度列表[q1,q2,q3,q4,q5,q6], 单位rad + """ + return self.robot.cal_ikine(pose, q_near, tool, wobj) + + # ==================== 状态查询 ==================== + + def get_robot_state(self): + """ + 功能: + 获取机器人状态 + 返回: + 状态列表[机器人状态, 程序状态, 安全控制器状态, 操作模式] + """ + return self.robot.get_robot_state() + + def get_tcp_pose(self): + """ + 功能: + 获取当前TCP位姿 + 返回: + TCP位姿[x,y,z,rx,ry,rz], 单位mm, rad + """ + pose_m = self.robot.get_tcp_pose() + # 将位置从m转换为mm + if pose_m is not None and len(pose_m) >= 6: + return [pose_m[0]*1000.0, pose_m[1]*1000.0, pose_m[2]*1000.0, + pose_m[3], pose_m[4], pose_m[5]] + return pose_m + + def get_joints_position(self): + """ + 功能: + 获取当前关节角度 + 返回: + 关节角度列表[q1,q2,q3,q4,q5,q6], 单位rad + """ + return self.robot.get_actual_joints_position() + + def get_tcp_speed(self): + """ + 功能: + 获取当前TCP速度 + 返回: + TCP速度列表, 单位m/s, rad/s + """ + return self.robot.get_tcp_speed() + + def get_tcp_force(self): + """ + 功能: + 获取当前末端力矩信息 + 返回: + 末端力矩[Fx,Fy,Fz,Mx,My,Mz], 单位N, N.m + """ + return self.robot.get_tcp_force() + + def is_moving(self): + """ + 功能: + 判断机械臂是否在运动 + 返回: + bool, True表示正在运动, False表示静止 + """ + return self.robot.robotmoving() + + # ==================== IO控制 ==================== + + def set_digital_output(self, num, value, block=True): + """ + 功能: + 设置控制柜数字IO输出 + 参数: + num: IO输出口序号, 范围1-16 + value: True为高电平, False为低电平 + block: 是否阻塞执行 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info(f"设置控制柜IO{num}输出: {value}") + return self.robot.set_standard_digital_out(num, value, block) + + def set_tool_digital_output(self, num, value, block=True): + """ + 功能: + 设置末端数字IO输出 + 参数: + num: 末端IO输出口序号, 范围1-2 + value: True为高电平, False为低电平 + block: 是否阻塞执行 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info(f"设置末端IO{num}输出: {value}") + return self.robot.set_tool_digital_out(num, value, block) + + def get_digital_input(self, num): + """ + 功能: + 读取控制柜数字IO输入 + 参数: + num: IO输入口序号, 范围1-16 + 返回: + bool, True为高电平, False为低电平 + """ + return self.robot.get_standard_digital_in(num) + + def get_tool_digital_input(self, num): + """ + 功能: + 读取末端数字IO输入 + 参数: + num: 末端IO输入口序号, 范围1-2 + 返回: + bool, True为高电平, False为低电平 + """ + return self.robot.get_tool_digital_in(num) + + # ==================== 快换和夹爪控制 ==================== + + def release_quick_change(self, block=True): + """ + 功能: + 松开快换装置 + 参数: + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("松开快换装置") + return self.robot.set_standard_digital_out(1, True, block) + + def lock_quick_change(self, block=True): + """ + 功能: + 夹紧快换装置 + 参数: + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.info("夹紧快换装置") + return self.robot.set_standard_digital_out(1, False, block) + + def open_gripper(self, block=True): + """ + 功能: + 张开夹爪 + 参数: + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.debug("张开夹爪") + return self.robot.set_standard_digital_out(2, True, block) + + def close_gripper(self, block=True): + """ + 功能: + 闭合夹爪 + 参数: + block: 是否阻塞执行, True表示等待完成, False表示立即返回 + 返回: + 阻塞执行返回任务状态, 非阻塞执行返回任务ID + """ + logger.debug("闭合夹爪") + return self.robot.set_standard_digital_out(2, False, block) + + # ==================== 夹爪状态检测 ==================== + + def get_gripper_state(self): + """ + 功能: + 获取夹爪当前状态 + 返回: + str, 夹爪状态: + "opened": 张开到位 + "gripped": 夹紧到位(夹到物料) + "empty": 空夹(未夹到物料) + "unknown": 未知状态 + """ + di10 = self.robot.get_standard_digital_in(10) + di11 = self.robot.get_standard_digital_in(11) + + if di10 == True and di11 == False: + # 张开到位 或 空夹(未夹到物料), 两者DI信号相同 + logger.info("夹爪状态: 张开到位/空夹") + return "opened" + elif di10 == False and di11 == True: + logger.info("夹爪状态: 夹紧到位(夹到物料)") + return "gripped" + else: + logger.warning(f"夹爪状态未知: DI10={di10}, DI11={di11}") + return "unknown" + + def is_gripper_opened(self): + """ + 功能: + 检测夹爪是否张开到位 + 返回: + bool, True表示张开到位, False表示未张开 + """ + di10 = self.robot.get_standard_digital_in(10) + di11 = self.robot.get_standard_digital_in(11) + return di10 == True and di11 == False + + def is_gripper_gripped(self): + """ + 功能: + 检测夹爪是否夹紧到位(夹到物料) + 返回: + bool, True表示夹紧到位且夹到物料, False表示未夹紧或未夹到物料 + """ + di10 = self.robot.get_standard_digital_in(10) + di11 = self.robot.get_standard_digital_in(11) + return di10 == False and di11 == True + + def is_gripper_empty(self): + """ + 功能: + 检测夹爪是否空夹(闭合但未夹到物料) + 注意: 空夹和张开到位的DI信号相同, 需要结合实际操作流程判断 + 返回: + bool, True表示空夹, False表示非空夹状态 + """ + di10 = self.robot.get_standard_digital_in(10) + di11 = self.robot.get_standard_digital_in(11) + # 空夹信号与张开到位相同, 返回相同结果 + return di10 == True and di11 == False + + # ==================== 料盘检测 ==================== + + def check_slot_has_material(self, slot_num): + """ + 功能: + 检测指定料位是否有料 + 参数: + slot_num: 料位编号, 范围1-7 + 1-3: 快换料位(bg1-bg3, 对应DI3-DI5) + 4-7: 托盘料位(bg4-bg7, 对应DI6-DI9) + 返回: + bool, True表示有料, False表示无料 + """ + if slot_num < 1 or slot_num > 7: + logger.error(f"无效的料位编号: {slot_num}, 有效范围1-7") + return False + + # 料位编号与DI端口的映射: slot_num + 2 = DI端口号 + di_port = slot_num + 2 + di_value = self.robot.get_standard_digital_in(di_port) + + # DI为false表示有料, true表示无料 + has_material = not di_value + slot_type = "快换" if slot_num <= 3 else "托盘" + logger.info(f"料位bg{slot_num}({slot_type})检测: {'有料' if has_material else '无料'}") + return has_material + + def check_quick_change_slot(self, slot_num): + """ + 功能: + 检测快换料位是否有料 + 参数: + slot_num: 快换料位编号, 范围1-3(对应bg1-bg3) + 返回: + bool, True表示有料, False表示无料 + """ + if slot_num < 1 or slot_num > 3: + logger.error(f"无效的快换料位编号: {slot_num}, 有效范围1-3") + return False + return self.check_slot_has_material(slot_num) + + def check_tray_slot(self, slot_num): + """ + 功能: + 检测托盘料位是否有料 + 参数: + slot_num: 托盘料位编号, 范围1-4(对应bg4-bg7) + 返回: + bool, True表示有料, False表示无料 + """ + if slot_num < 1 or slot_num > 4: + logger.error(f"无效的托盘料位编号: {slot_num}, 有效范围1-4") + return False + # 托盘料位1-4对应bg4-bg7 + return self.check_slot_has_material(slot_num + 3) + + def get_all_slots_status(self): + """ + 功能: + 获取所有料位的状态 + 返回: + dict, 包含所有料位状态: + { + "quick_change": [bg1, bg2, bg3], # 快换料位状态, True有料/False无料 + "tray": [bg4, bg5, bg6, bg7] # 托盘料位状态, True有料/False无料 + } + """ + quick_change_status = [] + tray_status = [] + + # 检测快换料位 bg1-bg3 (DI3-DI5) + for i in range(1, 4): + di_value = self.robot.get_standard_digital_in(i + 2) + quick_change_status.append(not di_value) + + # 检测托盘料位 bg4-bg7 (DI6-DI9) + for i in range(4, 8): + di_value = self.robot.get_standard_digital_in(i + 2) + tray_status.append(not di_value) + + result = { + "quick_change": quick_change_status, + "tray": tray_status + } + + logger.info(f"所有料位状态: 快换{quick_change_status}, 托盘{tray_status}") + return result + + # ==================== 夹爪状态管理 ==================== + + def get_current_gripper(self): + """ + 功能: + 获取当前安装的夹爪名称 + 返回: + str或None, 当前夹爪名称, 未安装时返回None + """ + return self.current_gripper + + def set_current_gripper(self, gripper_name): + """ + 功能: + 设置当前安装的夹爪名称(仅更新状态, 不执行物理操作) + 参数: + gripper_name: 夹爪名称, 为None表示未安装夹爪 + """ + self.current_gripper = gripper_name + if gripper_name is not None: + logger.info(f"当前夹爪已设置为: {gripper_name}") + else: + logger.info("当前夹爪已清除") + + # ==================== 负载管理 ==================== + + def set_payload(self, mass, x_cog, y_cog, z_cog): + """ + 功能: + 设置抓取负载参数 + 参数: + mass: 负载质量, 范围[0, 35], 单位kg + x_cog: 质心x坐标, 相对于工具坐标系, 单位mm + y_cog: 质心y坐标, 相对于工具坐标系, 单位mm + z_cog: 质心z坐标, 相对于工具坐标系, 单位mm + 返回: + 任务结束时的状态 + """ + # 将质心坐标从mm转换为m + x_cog_m = x_cog / 1000.0 + y_cog_m = y_cog / 1000.0 + z_cog_m = z_cog / 1000.0 + + logger.info(f"设置负载: 质量={mass}kg, 质心=({x_cog},{y_cog},{z_cog})mm") + return self.robot.set_load_data([mass, x_cog_m, y_cog_m, z_cog_m]) + + # ==================== 速度控制 ==================== + + def set_max_joint_velocity(self, velocity): + """ + 功能: + 设置最大关节角速度 + 参数: + velocity: 最大关节角速度, 单位rad/s + """ + self.max_joint_velocity = velocity + logger.info(f"设置最大关节角速度: {velocity:.3f} rad/s") + + def set_max_joint_acceleration(self, acceleration): + """ + 功能: + 设置最大关节角加速度 + 参数: + acceleration: 最大关节角加速度, 单位rad/s² + """ + self.max_joint_acceleration = acceleration + logger.info(f"设置最大关节角加速度: {acceleration:.3f} rad/s²") + + def set_max_linear_velocity(self, velocity): + """ + 功能: + 设置最大末端线速度 + 参数: + velocity: 最大末端线速度, 单位m/s + """ + self.max_linear_velocity = velocity + logger.info(f"设置最大末端线速度: {velocity:.3f} m/s") + + def set_max_linear_acceleration(self, acceleration): + """ + 功能: + 设置最大末端线加速度 + 参数: + acceleration: 最大末端线加速度, 单位m/s² + """ + self.max_linear_acceleration = acceleration + logger.info(f"设置最大末端线加速度: {acceleration:.3f} m/s²") + + def get_max_velocities(self): + """ + 功能: + 获取当前最大速度配置 + 返回: + dict, 包含最大速度配置: + { + "joint_velocity": 最大关节角速度(rad/s), + "joint_acceleration": 最大关节角加速度(rad/s²), + "linear_velocity": 最大末端线速度(m/s), + "linear_acceleration": 最大末端线加速度(m/s²) + } + """ + return { + "joint_velocity": self.max_joint_velocity, + "joint_acceleration": self.max_joint_acceleration, + "linear_velocity": self.max_linear_velocity, + "linear_acceleration": self.max_linear_acceleration + } + + def set_speed_ratio(self, ratio): + """ + 功能: + 设置全局速度比例 + 参数: + ratio: 速度比例, 范围(0,100], 单位% + 返回: + 任务结束时的状态 + """ + logger.info(f"设置全局速度比例: {ratio}%") + return self.robot.speed(ratio) + + # ==================== 碰撞检测 ==================== + + def set_collision_level(self, level): + """ + 功能: + 设置碰撞检测等级 + 参数: + level: 碰撞检测等级, 0:关闭, 1-5:等级1到5 + 返回: + 任务结束时的状态 + """ + logger.info(f"设置碰撞检测等级: {level}") + return self.robot.collision_detect(level) + + # ==================== 错误处理 ==================== + + def get_last_error(self): + """ + 功能: + 获取最后一次错误信息 + 返回: + 错误信息 + """ + return self.robot.get_last_error() + + def clear_error(self): + """ + 功能: + 清除错误信息 + 返回: + 任务结束时的状态 + """ + logger.info("清除错误信息") + return self.robot.clear_error_message() + + # ==================== 任务状态查询 ==================== + + def get_task_state(self, task_id): + """ + 功能: + 查询非阻塞任务的执行状态 + 参数: + task_id: 任务ID + 返回: + 任务状态 + """ + return self.robot.get_noneblock_taskstate(task_id) + + # ==================== 系统变量管理 ==================== + + def get_system_value_double(self, name): + """ + 功能: + 获取number类型系统变量 + 参数: + name: 系统变量名称 + 返回: + float, number类型系统变量的值 + """ + logger.debug(f"获取系统变量(double): {name}") + return self.robot.get_system_value_double(name) + + def get_system_value_lists(self, name): + """ + 功能: + 获取pose_list/joint_list类型系统变量 + 参数: + name: 系统变量名称 + 返回: + list, pose_list或joint_list类型系统变量的值 + """ + logger.debug(f"获取系统变量(lists): {name}") + return self.robot.get_system_value_lists(name) + + +def main(): + """ + 功能: + 交互式测试机械臂驱动的主要接口 + """ + # 配置日志输出到控制台 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + print("=" * 60) + print("机械臂驱动交互式测试程序") + print("=" * 60) + + # 初始化机械臂驱动 + arm = ArmDriver() + + # 连接机械臂 + print("\n正在连接机械臂...") + if not arm.connect(): + print("连接失败, 程序退出") + return + + # 上电 + print("\n正在上电...") + result = arm.power_on(block=True) + print(f"上电结果: {result}") + + # 使能 + print("\n正在使能...") + result = arm.enable(block=True) + print(f"使能结果: {result}") + + # 获取当前状态 + print("\n当前机械臂状态:") + state = arm.get_robot_state() + print(f" 机器人状态: {state}") + joints = arm.get_joints_position() + print(f" 当前关节角度: {joints}") + pose = arm.get_tcp_pose() + print(f" 当前TCP位姿: {pose}") + + # 交互式测试循环 + while True: + print("\n" + "=" * 60) + print("请选择要测试的功能:") + print("1. 测试 move_to_joints (关节运动)") + print("2. 测试 move_to_pose (位姿运动)") + print("3. 测试 run_program (运行程序)") + print("4. 查看当前状态") + print("5. 测试快换控制") + print("6. 测试夹爪控制") + print("7. 查看夹爪状态") + print("8. 查看料盘状态") + print("0. 退出程序") + print("=" * 60) + + choice = input("请输入选项 (0-8): ").strip() + + if choice == "1": + # 测试关节运动 + print("\n--- 测试关节运动 ---") + print("请输入目标关节角度 (6个值, 单位rad, 用逗号分隔)") + print("有效范围: 每个关节角度范围[-2*PI, 2*PI] rad, 即[-6.28, 6.28]") + print("示例: 0,-0.5,0.5,0,0.5,0") + joints_input = input("关节角度: ").strip() + + try: + joints_list = [float(x) for x in joints_input.split(',')] + if len(joints_list) != 6: + print("错误: 必须输入6个关节角度值") + continue + + v = input("速度百分比 (范围0-1, 例如0.3表示30%, 默认0.5): ").strip() + v = float(v) if v else 0.5 + + a = input("加速度百分比 (范围0-1, 例如0.5表示50%, 默认0.5): ").strip() + a = float(a) if a else 0.5 + + print(f"\n开始运动到关节位置: {joints_list}") + result = arm.move_to_joints(joints_list, v=v, a=a, block=True) + print(f"运动结果: {result}") + + # 显示运动后的位置 + current_joints = arm.get_joints_position() + print(f"运动后关节角度: {current_joints}") + + except ValueError: + print("错误: 输入格式不正确") + except Exception as e: + print(f"错误: {e}") + + elif choice == "2": + # 测试位姿运动 + print("\n--- 测试位姿运动 ---") + print("请输入目标位姿 (6个值: x,y,z,rx,ry,rz, 位置单位m, 姿态单位rad, 用逗号分隔)") + print("示例: 0.3,0.2,0.4,3.14,0,0") + pose_input = input("目标位姿: ").strip() + + try: + pose = [float(x) for x in pose_input.split(',')] + if len(pose) != 6: + print("错误: 必须输入6个位姿值") + continue + + v = input("速度百分比 (范围0-1, 例如0.3表示30%, 默认0.5): ").strip() + v = float(v) if v else 0.5 + + a = input("加速度百分比 (范围0-1, 例如0.5表示50%, 默认0.5): ").strip() + a = float(a) if a else 0.5 + + print(f"\n开始运动到目标位姿: {pose}") + result = arm.move_to_pose(pose, v=v, a=a, block=True) + print(f"运动结果: {result}") + + # 显示运动后的位置 + current_pose = arm.get_tcp_pose() + print(f"运动后TCP位姿: {current_pose}") + + except ValueError: + print("错误: 输入格式不正确") + except Exception as e: + print(f"错误: {e}") + + elif choice == "3": + # 测试运行程序 + print("\n--- 测试运行程序 ---") + program_name = input("请输入程序名称: ").strip() + + if not program_name: + print("错误: 程序名称不能为空") + continue + + try: + print(f"\n开始运行程序: {program_name}") + result = arm.run_program(program_name, block=True) + print(f"程序运行结果: {result}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "4": + # 查看当前状态 + print("\n--- 当前机械臂状态 ---") + try: + state = arm.get_robot_state() + print(f"机器人状态: {state}") + + joints = arm.get_joints_position() + print(f"当前关节角度: {joints}") + + pose = arm.get_tcp_pose() + print(f"当前TCP位姿: {pose}") + + is_moving = arm.is_moving() + print(f"是否在运动: {is_moving}") + + tcp_speed = arm.get_tcp_speed() + print(f"TCP速度: {tcp_speed}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "5": + # 测试快换控制 + print("\n--- 测试快换控制 ---") + print("1. 松开快换") + print("2. 夹紧快换") + sub_choice = input("请选择操作 (1-2): ").strip() + + try: + if sub_choice == "1": + print("正在松开快换...") + result = arm.release_quick_change(block=True) + print(f"松开快换结果: {result}") + elif sub_choice == "2": + print("正在夹紧快换...") + result = arm.lock_quick_change(block=True) + print(f"夹紧快换结果: {result}") + else: + print("无效的选项") + except Exception as e: + print(f"错误: {e}") + + elif choice == "6": + # 测试夹爪控制 + print("\n--- 测试夹爪控制 ---") + print("1. 张开夹爪") + print("2. 闭合夹爪") + sub_choice = input("请选择操作 (1-2): ").strip() + + try: + if sub_choice == "1": + print("正在张开夹爪...") + result = arm.open_gripper(block=True) + print(f"张开夹爪结果: {result}") + elif sub_choice == "2": + print("正在闭合夹爪...") + result = arm.close_gripper(block=True) + print(f"闭合夹爪结果: {result}") + else: + print("无效的选项") + except Exception as e: + print(f"错误: {e}") + + elif choice == "7": + # 查看夹爪状态 + print("\n--- 夹爪状态 ---") + try: + state = arm.get_gripper_state() + print(f"夹爪状态: {state}") + + is_opened = arm.is_gripper_opened() + print(f"是否张开到位: {is_opened}") + + is_gripped = arm.is_gripper_gripped() + print(f"是否夹紧到位(有物料): {is_gripped}") + + except Exception as e: + print(f"错误: {e}") + + elif choice == "8": + # 查看料盘状态 + print("\n--- 料盘状态 ---") + print("1. 查看所有料位状态") + print("2. 查看指定快换料位") + print("3. 查看指定托盘料位") + sub_choice = input("请选择操作 (1-3): ").strip() + + try: + if sub_choice == "1": + status = arm.get_all_slots_status() + print("\n快换料位状态 (bg1-bg3):") + for i, has_material in enumerate(status["quick_change"], 1): + print(f" bg{i}: {'有料' if has_material else '无料'}") + print("\n托盘料位状态 (bg4-bg7):") + for i, has_material in enumerate(status["tray"], 4): + print(f" bg{i}: {'有料' if has_material else '无料'}") + + elif sub_choice == "2": + slot_num = input("请输入快换料位编号 (1-3): ").strip() + slot_num = int(slot_num) + has_material = arm.check_quick_change_slot(slot_num) + print(f"快换料位bg{slot_num}: {'有料' if has_material else '无料'}") + + elif sub_choice == "3": + slot_num = input("请输入托盘料位编号 (1-4, 对应bg4-bg7): ").strip() + slot_num = int(slot_num) + has_material = arm.check_tray_slot(slot_num) + print(f"托盘料位bg{slot_num + 3}: {'有料' if has_material else '无料'}") + + else: + print("无效的选项") + except ValueError: + print("错误: 请输入有效的数字") + except Exception as e: + print(f"错误: {e}") + + elif choice == "0": + # 退出程序 + print("\n正在退出...") + break + + else: + print("无效的选项, 请重新输入") + + # # 下使能和下电 + # print("\n正在下使能...") + # arm.disable(block=True) + + # print("正在下电...") + # arm.power_off(block=True) + + # 断开连接 + print("正在断开连接...") + arm.disconnect() + + print("\n程序已退出") + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_agv/program_file/agv_get_bg4.json b/unilabos/devices/eit_agv/program_file/agv_get_bg4.json new file mode 100644 index 00000000..2b5cf247 --- /dev/null +++ b/unilabos/devices/eit_agv/program_file/agv_get_bg4.json @@ -0,0 +1,524 @@ +{ + "name": "agv_get_bg4.jspf", + "modules": [ + { + "tag": "Main", + "name": "Main", + "repeat": false, + "tasks": [ + { + "id": "vKaXivk-8h4q", + "tag": "Start", + "expanded": false, + "disabled": true, + "params": { + "pose": { + "type": "p", + "val": [0.17455244, 0.18023264, -1.98811173, 0.2387163, 1.56881213, -1.32311332], + "name": "", + "isEdited": false, + "index": -1, + "varType": -1 + }, + "v": 30 + } + }, + { + "id": "9rAdrjI4DhZp", + "tag": "Move", + "expanded": false, + "disabled": true, + "params": { + "v": 0.15, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.698132, + "acc_joint": 9999, + "rad": 0, + "comment": "dmw" + }, + "tasks": [ + { + "id": "GrPHhhxHgWex", + "tag": "MoveJ2", + "expanded": false, + "disabled": true, + "params": { + "type": "joint", + "pose": { + "type": "p", + "val": [0.17456442, 0.18022066, -1.98809981, 0.23872828, 1.56881213, -1.32312536], + "name": "wp0", + "isEdited": false, + "index": -1, + "varType": -1 + }, + "q_near": [], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "u5-CSJ2jPxT0", + "tag": "Move", + "expanded": true, + "disabled": true, + "params": { + "v": 0.2, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 1.047198, + "acc_joint": 9999, + "rad": 0, + "comment": "zjw1" + }, + "tasks": [ + { + "id": "4O2HBCmfc9F7", + "tag": "MoveJ2", + "expanded": true, + "disabled": true, + "params": { + "type": "joint", + "pose": { + "type": "v", + "val": "g_zjw1", + "name": "wp1", + "isEdited": false, + "index": -1, + "varType": 5 + }, + "q_near": [], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "eY9XtPH36CJk", + "tag": "Move", + "expanded": true, + "disabled": false, + "params": { + "v": 0.15, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.698132, + "acc_joint": 9999, + "rad": 0, + "comment": "zjw1" + }, + "tasks": [ + { + "id": "8pWgx_FfmoZr", + "tag": "MoveJ2", + "expanded": true, + "disabled": false, + "params": { + "type": "pose", + "pose": { + "type": "p", + "val": [-0.56604033, 0.19143068, 0.37644481, -3.14154505, 3.011e-05, -0.06976842], + "name": "wp1", + "isEdited": false, + "index": -1, + "varType": -1 + }, + "q_near": [ + -0.04861779, + 0.06705363, + -2.16321325, + 0.52702081, + 1.56871628, + 1.59493756 + ], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "hlfoNPfTzhEc", + "tag": "Set", + "expanded": false, + "disabled": false, + "params": { + "type": "do", + "var": 2, + "val": true, + "mode": 0, + "freq": 0, + "duty_cycle": 0, + "comment": "", + "expr": "" + } + }, + { + "id": "uTcOk-9fzEpV", + "tag": "Move", + "expanded": true, + "disabled": false, + "params": { + "v": 0.1, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.523599, + "acc_joint": 9999, + "rad": 0, + "comment": "yzbg4" + }, + "tasks": [ + { + "id": "GDBgc8XE-xO0", + "tag": "MoveJ2", + "expanded": true, + "disabled": false, + "params": { + "type": "pose", + "pose": { + "type": "p", + "val": [-0.68082439, 0.43859979, 0.19124405, -3.14153742, -1.43e-06, -0.03048259], + "name": "wp2", + "isEdited": false, + "index": -1, + "varType": 5 + }, + "q_near": [ + -0.36858463, + -0.46552532, + -1.90460574, + 0.80098015, + 1.5682609, + 1.23472774 + ], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "8GsAFDS0lH6d", + "tag": "Move", + "expanded": true, + "disabled": false, + "params": { + "v": 0.1, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.523599, + "acc_joint": 9999, + "rad": 0, + "comment": "zbg4" + }, + "tasks": [ + { + "id": "tMaa1fPTC4HU", + "tag": "MoveL", + "expanded": true, + "disabled": false, + "params": { + "pose": { + "type": "v", + "val": "g_bg4", + "name": "wp3", + "isEdited": false, + "index": -1, + "varType": 4 + }, + "q_near": [], + "user": "default", + "tcp": "default", + "v": 0.1, + "acc": 1, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "QadZGei499T1", + "tag": "Set", + "expanded": false, + "disabled": false, + "params": { + "type": "do", + "var": 2, + "val": false, + "mode": 0, + "freq": 0, + "duty_cycle": 0, + "comment": "", + "expr": "" + } + }, + { + "id": "WjZnhCL1mqT1", + "tag": "Wait", + "expanded": false, + "disabled": false, + "params": { + "type": "delay", + "var": "", + "val": 1000, + "check_timeout": false, + "timeout": 60000, + "timeout_ret": "", + "comment": "", + "expr": "" + } + }, + { + "id": "yyiMdNjzGbLq", + "tag": "Move", + "expanded": false, + "disabled": false, + "params": { + "v": 0.1, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.523599, + "acc_joint": 9999, + "rad": 0, + "comment": "yzbg4" + }, + "tasks": [ + { + "id": "vIwq9sGFv19W", + "tag": "MoveJ2", + "expanded": false, + "disabled": false, + "params": { + "type": "joint", + "pose": { + "type": "v", + "val": "g_ybg4", + "name": "wp4", + "isEdited": false, + "index": -1, + "varType": 5 + }, + "q_near": [], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "Q1J8vElKGG7V", + "tag": "Move", + "expanded": true, + "disabled": false, + "params": { + "v": 0.15, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.698132, + "acc_joint": 9999, + "rad": 0, + "comment": "zjw1" + }, + "tasks": [ + { + "id": "udWboKg_6hyp", + "tag": "MoveJ2", + "expanded": true, + "disabled": false, + "params": { + "type": "joint", + "pose": { + "type": "p", + "val": [-0.04861779, 0.06705363, -2.16323733, 0.52702081, 1.56872832, 1.59493756], + "name": "wp2", + "isEdited": false, + "index": -1, + "varType": -1 + }, + "q_near": [], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "0Yx0THQDM09m", + "tag": "Move", + "expanded": true, + "disabled": true, + "params": { + "v": 0.2, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 1.047198, + "acc_joint": 9999, + "rad": 0, + "comment": "zjw1" + }, + "tasks": [ + { + "id": "rBWZdb6R0l0h", + "tag": "MoveJ2", + "expanded": true, + "disabled": true, + "params": { + "type": "joint", + "pose": { + "type": "v", + "val": "g_zjw1", + "name": "wp2", + "isEdited": false, + "index": -1, + "varType": 5 + }, + "q_near": [], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + }, + { + "id": "aJazbMhOmeaH", + "tag": "Move", + "expanded": false, + "disabled": true, + "params": { + "v": 0.1, + "acc": 9999, + "user": "default", + "tcp": "default", + "v_joint": 0.523599, + "acc_joint": 9999, + "rad": 0, + "comment": "dmw" + }, + "tasks": [ + { + "id": "3MxktoRWGCf_", + "tag": "MoveJ2", + "expanded": false, + "disabled": true, + "params": { + "type": "joint", + "pose": { + "type": "p", + "val": [0.17456442, 0.18022066, -1.98809981, 0.23872828, 1.56881213, -1.32312536], + "name": "wp6", + "isEdited": false, + "index": -1, + "varType": -1 + }, + "q_near": [], + "user": "", + "tcp": "", + "v": 0.523599, + "acc": 5.23599, + "rad": 0, + "block": true, + "use_op": false, + "op": "{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}", + "use_parent_param": true, + "use_parent_coord": true, + "use_parent_rad": true, + "comment": "" + } + } + ] + } + ], + "expanded": true + } + ], + "variables": [] +} \ No newline at end of file diff --git a/unilabos/devices/eit_agv/program_file/agv_get_bg4.jspf b/unilabos/devices/eit_agv/program_file/agv_get_bg4.jspf new file mode 100644 index 00000000..599d4a66 --- /dev/null +++ b/unilabos/devices/eit_agv/program_file/agv_get_bg4.jspf @@ -0,0 +1 @@ +{"name":"agv_get_bg4.jspf","modules":[{"tag":"Main","name":"Main","repeat":false,"tasks":[{"id":"vKaXivk-8h4q","tag":"Start","expanded":false,"disabled":true,"params":{"pose":{"type":"p","val":[0.17455244,0.18023264,-1.98811173,0.2387163,1.56881213,-1.32311332],"name":"","isEdited":false,"index":-1,"varType":-1},"v":30}},{"id":"9rAdrjI4DhZp","tag":"Move","expanded":false,"disabled":true,"params":{"v":0.15,"acc":9999,"user":"default","tcp":"default","v_joint":0.698132,"acc_joint":9999,"rad":0,"comment":"dmw"},"tasks":[{"id":"GrPHhhxHgWex","tag":"MoveJ2","expanded":false,"disabled":true,"params":{"type":"joint","pose":{"type":"p","val":[0.17456442,0.18022066,-1.98809981,0.23872828,1.56881213,-1.32312536],"name":"wp0","isEdited":false,"index":-1,"varType":-1},"q_near":[],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"u5-CSJ2jPxT0","tag":"Move","expanded":true,"disabled":true,"params":{"v":0.2,"acc":9999,"user":"default","tcp":"default","v_joint":1.047198,"acc_joint":9999,"rad":0,"comment":"zjw1"},"tasks":[{"id":"4O2HBCmfc9F7","tag":"MoveJ2","expanded":true,"disabled":true,"params":{"type":"joint","pose":{"type":"v","val":"g_zjw1","name":"wp1","isEdited":false,"index":-1,"varType":5},"q_near":[],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"eY9XtPH36CJk","tag":"Move","expanded":true,"disabled":false,"params":{"v":0.15,"acc":9999,"user":"default","tcp":"default","v_joint":0.698132,"acc_joint":9999,"rad":0,"comment":"zjw1"},"tasks":[{"id":"8pWgx_FfmoZr","tag":"MoveJ2","expanded":true,"disabled":false,"params":{"type":"pose","pose":{"type":"p","val":[-0.56604033,0.19143068,0.37644481,-3.14154505,0.00003011,-0.06976842],"name":"wp1","isEdited":false,"index":-1,"varType":-1},"q_near":[-0.04861779,0.06705363,-2.16321325,0.52702081,1.56871628,1.59493756],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"hlfoNPfTzhEc","tag":"Set","expanded":false,"disabled":false,"params":{"type":"do","var":2,"val":true,"mode":0,"freq":0,"duty_cycle":0,"comment":"","expr":""}},{"id":"uTcOk-9fzEpV","tag":"Move","expanded":true,"disabled":false,"params":{"v":0.1,"acc":9999,"user":"default","tcp":"default","v_joint":0.523599,"acc_joint":9999,"rad":0,"comment":"yzbg4"},"tasks":[{"id":"GDBgc8XE-xO0","tag":"MoveJ2","expanded":true,"disabled":false,"params":{"type":"pose","pose":{"type":"p","val":[-0.68082439,0.43859979,0.19124405,-3.14153742,-0.00000143,-0.03048259],"name":"wp2","isEdited":false,"index":-1,"varType":5},"q_near":[-0.36858463,-0.46552532,-1.90460574,0.80098015,1.5682609,1.23472774],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"8GsAFDS0lH6d","tag":"Move","expanded":true,"disabled":false,"params":{"v":0.1,"acc":9999,"user":"default","tcp":"default","v_joint":0.523599,"acc_joint":9999,"rad":0,"comment":"zbg4"},"tasks":[{"id":"tMaa1fPTC4HU","tag":"MoveL","expanded":true,"disabled":false,"params":{"pose":{"type":"v","val":"g_bg4","name":"wp3","isEdited":false,"index":-1,"varType":4},"q_near":[],"user":"default","tcp":"default","v":0.1,"acc":1,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"QadZGei499T1","tag":"Set","expanded":false,"disabled":false,"params":{"type":"do","var":2,"val":false,"mode":0,"freq":0,"duty_cycle":0,"comment":"","expr":""}},{"id":"WjZnhCL1mqT1","tag":"Wait","expanded":false,"disabled":false,"params":{"type":"delay","var":"","val":1000,"check_timeout":false,"timeout":60000,"timeout_ret":"","comment":"","expr":""}},{"id":"yyiMdNjzGbLq","tag":"Move","expanded":false,"disabled":false,"params":{"v":0.1,"acc":9999,"user":"default","tcp":"default","v_joint":0.523599,"acc_joint":9999,"rad":0,"comment":"yzbg4"},"tasks":[{"id":"vIwq9sGFv19W","tag":"MoveJ2","expanded":false,"disabled":false,"params":{"type":"joint","pose":{"type":"v","val":"g_ybg4","name":"wp4","isEdited":false,"index":-1,"varType":5},"q_near":[],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"Q1J8vElKGG7V","tag":"Move","expanded":true,"disabled":false,"params":{"v":0.15,"acc":9999,"user":"default","tcp":"default","v_joint":0.698132,"acc_joint":9999,"rad":0,"comment":"zjw1"},"tasks":[{"id":"udWboKg_6hyp","tag":"MoveJ2","expanded":true,"disabled":false,"params":{"type":"joint","pose":{"type":"p","val":[-0.04861779,0.06705363,-2.16323733,0.52702081,1.56872832,1.59493756],"name":"wp2","isEdited":false,"index":-1,"varType":-1},"q_near":[],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"0Yx0THQDM09m","tag":"Move","expanded":true,"disabled":true,"params":{"v":0.2,"acc":9999,"user":"default","tcp":"default","v_joint":1.047198,"acc_joint":9999,"rad":0,"comment":"zjw1"},"tasks":[{"id":"rBWZdb6R0l0h","tag":"MoveJ2","expanded":true,"disabled":true,"params":{"type":"joint","pose":{"type":"v","val":"g_zjw1","name":"wp2","isEdited":false,"index":-1,"varType":5},"q_near":[],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]},{"id":"aJazbMhOmeaH","tag":"Move","expanded":false,"disabled":true,"params":{"v":0.1,"acc":9999,"user":"default","tcp":"default","v_joint":0.523599,"acc_joint":9999,"rad":0,"comment":"dmw"},"tasks":[{"id":"3MxktoRWGCf_","tag":"MoveJ2","expanded":false,"disabled":true,"params":{"type":"joint","pose":{"type":"p","val":[0.17456442,0.18022066,-1.98809981,0.23872828,1.56881213,-1.32312536],"name":"wp6","isEdited":false,"index":-1,"varType":-1},"q_near":[],"user":"","tcp":"","v":0.523599,"acc":5.23599,"rad":0,"block":true,"use_op":false,"op":"{0,1,false,0,0,\"\",0,1,false,0,0,\"\"}","use_parent_param":true,"use_parent_coord":true,"use_parent_rad":true,"comment":""}}]}],"expanded":true}],"variables":[]} \ No newline at end of file diff --git a/unilabos/devices/eit_agv/program_file/coordinates_export.json b/unilabos/devices/eit_agv/program_file/coordinates_export.json new file mode 100644 index 00000000..95711e3c --- /dev/null +++ b/unilabos/devices/eit_agv/program_file/coordinates_export.json @@ -0,0 +1,126 @@ +{ + "agv_get_bg4.json": [ + { + "file": "agv_get_bg4.json", + "module": "Main", + "task_id": "8pWgx_FfmoZr", + "motion_type": "MoveJ2", + "coordinate_type": "pose", + "point_name": "wp1", + "original_values": [-0.56604033, 0.19143068, 0.37644481, -3.14154505, 3.011e-05, -0.06976842], + "joint_values": [-0.048617786464671645, 0.06705366739438279, -2.163213336133411, 0.5270208121484441, 1.5687163687561494, 1.5949375598416589], + "pose_values": [-566.0403299999999, 191.43068, 376.44481, -3.14154505, 3.011e-05, -0.06976842], + "comment": "" + }, + { + "file": "agv_get_bg4.json", + "module": "Main", + "task_id": "GDBgc8XE-xO0", + "motion_type": "MoveJ2", + "coordinate_type": "pose", + "point_name": "wp2", + "original_values": [-0.68082439, 0.43859979, 0.19124405, -3.14153742, -1.43e-06, -0.03048259], + "joint_values": [-0.3685845782057751, -0.4655253426249802, -1.9046056892587984, 0.8009802328117164, 1.5682608058399132, 1.2347277520369864], + "pose_values": [-680.82439, 438.59979, 191.24405, -3.14153742, -1.43e-06, -0.03048259], + "comment": "" + }, + { + "file": "agv_get_bg4.json", + "module": "Main", + "task_id": "tMaa1fPTC4HU", + "motion_type": "MoveL", + "coordinate_type": "variable", + "variable_name": "g_bg4", + "point_name": "wp3", + "joint_values": null, + "pose_values": null, + "comment": "" + }, + { + "file": "agv_get_bg4.json", + "module": "Main", + "task_id": "vIwq9sGFv19W", + "motion_type": "MoveJ2", + "coordinate_type": "variable", + "variable_name": "g_ybg4", + "point_name": "wp4", + "joint_values": null, + "pose_values": null, + "comment": "" + }, + { + "file": "agv_get_bg4.json", + "module": "Main", + "task_id": "udWboKg_6hyp", + "motion_type": "MoveJ2", + "coordinate_type": "joint", + "point_name": "wp2", + "original_values": [-0.04861779, 0.06705363, -2.16323733, 0.52702081, 1.56872832, 1.59493756], + "joint_values": [-0.04861779, 0.06705363, -2.16323733, 0.52702081, 1.56872832, 1.59493756], + "pose_values": [-566.0309346041028, 191.42888797524637, 376.4300093659625, -3.1415564525657915, 5.7943065643071945e-06, -0.06976837173083164], + "comment": "" + } + ], + "agv_get_bg4.jspf": [ + { + "file": "agv_get_bg4.jspf", + "module": "Main", + "task_id": "8pWgx_FfmoZr", + "motion_type": "MoveJ2", + "coordinate_type": "pose", + "point_name": "wp1", + "original_values": [-0.56604033, 0.19143068, 0.37644481, -3.14154505, 3.011e-05, -0.06976842], + "joint_values": [-0.048617786464671645, 0.06705366739438279, -2.163213336133411, 0.5270208121484441, 1.5687163687561494, 1.5949375598416589], + "pose_values": [-566.0403299999999, 191.43068, 376.44481, -3.14154505, 3.011e-05, -0.06976842], + "comment": "" + }, + { + "file": "agv_get_bg4.jspf", + "module": "Main", + "task_id": "GDBgc8XE-xO0", + "motion_type": "MoveJ2", + "coordinate_type": "pose", + "point_name": "wp2", + "original_values": [-0.68082439, 0.43859979, 0.19124405, -3.14153742, -1.43e-06, -0.03048259], + "joint_values": [-0.3685845782057751, -0.4655253426249802, -1.9046056892587984, 0.8009802328117164, 1.5682608058399132, 1.2347277520369864], + "pose_values": [-680.82439, 438.59979, 191.24405, -3.14153742, -1.43e-06, -0.03048259], + "comment": "" + }, + { + "file": "agv_get_bg4.jspf", + "module": "Main", + "task_id": "tMaa1fPTC4HU", + "motion_type": "MoveL", + "coordinate_type": "variable", + "variable_name": "g_bg4", + "point_name": "wp3", + "joint_values": null, + "pose_values": null, + "comment": "" + }, + { + "file": "agv_get_bg4.jspf", + "module": "Main", + "task_id": "vIwq9sGFv19W", + "motion_type": "MoveJ2", + "coordinate_type": "variable", + "variable_name": "g_ybg4", + "point_name": "wp4", + "joint_values": null, + "pose_values": null, + "comment": "" + }, + { + "file": "agv_get_bg4.jspf", + "module": "Main", + "task_id": "udWboKg_6hyp", + "motion_type": "MoveJ2", + "coordinate_type": "joint", + "point_name": "wp2", + "original_values": [-0.04861779, 0.06705363, -2.16323733, 0.52702081, 1.56872832, 1.59493756], + "joint_values": [-0.04861779, 0.06705363, -2.16323733, 0.52702081, 1.56872832, 1.59493756], + "pose_values": [-566.0309346041028, 191.42888797524637, 376.4300093659625, -3.1415564525657915, 5.7943065643071945e-06, -0.06976837173083164], + "comment": "" + } + ] +} \ No newline at end of file diff --git a/unilabos/devices/eit_agv/start_auto_charge.bat b/unilabos/devices/eit_agv/start_auto_charge.bat new file mode 100644 index 00000000..f2175a2c --- /dev/null +++ b/unilabos/devices/eit_agv/start_auto_charge.bat @@ -0,0 +1,37 @@ +@echo off +title AGV �Զ������ +echo ============================================ +echo AGV �Զ������ +echo ============================================ +echo. +echo ˵��: ������������AGV����״̬ +echo AGV��CP6���վʱ�Զ�ִ�г���� +echo AGVִ������ʱ�Զ��ȴ����Ժ����� +echo. +echo ����: --interval-hours ��CP6��ɼ���ĵȴ�ʱ��(Сʱ), Ĭ��1 +echo --retry-minutes ����CP6ʱ�����Լ��(����), Ĭ��5 +echo. +echo �� Ctrl+C ֹͣ��� +echo ============================================ +echo. + +:: ���� conda base ���� +call conda activate base +if errorlevel 1 ( + echo [����] conda base ��������ʧ��, ����conda�Ƿ��Ѱ�װ����ʼ�� + pause + exit /b 1 +) + +:: �л���ģ���Ŀ¼(eit_agv�ĸ�Ŀ¼) +cd /d d:\Uni-Lab-OS\unilabos\devices + +:: ��ģ�鷽ʽ����, ȷ����Ե����������� +:: %* ͸�����������в���, ����: start_auto_charge.bat --interval-hours 2 +python -m eit_agv.controller.auto_charge_monitor %* + +echo. +echo ============================================ +echo ��������˳� +echo ============================================ +pause diff --git a/unilabos/devices/eit_agv/start_auto_charge_pp5_cp6.bat b/unilabos/devices/eit_agv/start_auto_charge_pp5_cp6.bat new file mode 100644 index 00000000..ead560ac --- /dev/null +++ b/unilabos/devices/eit_agv/start_auto_charge_pp5_cp6.bat @@ -0,0 +1,29 @@ +@echo off +setlocal + +set "CONDA_EXE=C:\ProgramData\miniforge3\Scripts\conda.exe" +if not exist "%CONDA_EXE%" ( + where conda >nul 2>nul + if errorlevel 1 ( + echo [ERROR] conda executable not found + pause + exit /b 1 + ) + set "CONDA_EXE=conda" +) + +cd /d d:\Uni-Lab-OS\unilabos\devices + +"%CONDA_EXE%" run --no-capture-output -n base python -m eit_agv.controller.auto_charge_pp5_cp6_monitor %* +if errorlevel 1 ( + set "EXIT_CODE=%ERRORLEVEL%" + echo [ERROR] monitor exited with code %EXIT_CODE% + pause + exit /b %EXIT_CODE% +) + +if not "%1"=="" goto :eof + +echo [INFO] monitor exited normally +pause +goto :eof diff --git a/unilabos/devices/eit_agv/tests/auto_charge_check_regression_suite.py b/unilabos/devices/eit_agv/tests/auto_charge_check_regression_suite.py new file mode 100644 index 00000000..0b1fc86f --- /dev/null +++ b/unilabos/devices/eit_agv/tests/auto_charge_check_regression_suite.py @@ -0,0 +1,249 @@ +# coding: utf-8 +""" +功能: + 覆盖 auto_charge_check 的核心回归场景, 验证低电量时的充电状态保护逻辑, 供旧测试入口转发调用. +参数: + 无. +返回: + 无. 通过 unittest 执行断言. +""" + +import unittest +from unittest.mock import call, patch + + +_ARM_DRIVER_PATH = "eit_agv.controller.agv_controller.ArmDriver" +_POS_MGR_PATH = "eit_agv.controller.agv_controller.PositionManager" + + +def _make_controller(): + """ + 功能: + 创建 AGVController 实例, 并在构造阶段替换硬件相关依赖, 避免连接真实设备. + 参数: + 无. + 返回: + AGVController, 可用于自动充电逻辑测试的控制器实例. + """ + with patch(_ARM_DRIVER_PATH), patch(_POS_MGR_PATH): + from eit_agv.controller.agv_controller import AGVController + + return AGVController() + + +class TestAutoChargeCheckRegression(unittest.TestCase): + """ + 功能: + 自动充电检查回归测试套件. + 参数: + 无. + 返回: + 无. + """ + + def setUp(self): + """ + 功能: + 为每个测试用例创建独立的控制器实例. + 参数: + 无. + 返回: + 无. + """ + self.controller = _make_controller() + + def test_cp6_sufficient_battery_no_navigation(self): + """ + 功能: + 验证 AGV 在 CP6 且电量充足时, 不触发任何导航动作. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.6, "ret_code": 0}, + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "battery_sufficient") + self.assertAlmostEqual(result["battery_level"], 0.6) + mock_query_battery.assert_called_once_with(simple=True) + mock_safe_nav.assert_not_called() + + def test_cp6_low_battery_already_charging_skip_navigation(self): + """ + 功能: + 验证 AGV 在 CP6 且电量低于50%但已经在充电时, 不执行进出站动作. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + side_effect=[ + {"battery_level": 0.45, "ret_code": 0}, + {"battery_level": 0.45, "charging": True, "ret_code": 0}, + ], + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "already_charging") + self.assertAlmostEqual(result["battery_level"], 0.45) + self.assertIs(result["charging"], True) + self.assertEqual( + mock_query_battery.call_args_list, + [call(simple=True), call(simple=False)], + ) + mock_safe_nav.assert_not_called() + + def test_cp6_low_battery_not_charging_runs_redock_cycle(self): + """ + 功能: + 验证 AGV 在 CP6 且低电量未充电时, 会执行 PP5 到 CP6 的重对接流程. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + side_effect=[ + {"task_status": 0, "task_status_name": "NONE"}, + {"task_status": 0, "task_status_name": "NONE"}, + ], + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + side_effect=[ + {"battery_level": 0.45, "ret_code": 0}, + {"battery_level": 0.45, "charging": False, "ret_code": 0}, + {"battery_level": 0.45, "charging": True, "ret_code": 0}, + ], + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + side_effect=[{"ret_code": 0}, {"ret_code": 0}], + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "charge_cycle_completed") + self.assertAlmostEqual(result["battery_level"], 0.45) + self.assertIs(result["charging"], True) + self.assertEqual( + mock_query_battery.call_args_list, + [call(simple=True), call(simple=False), call(simple=False)], + ) + self.assertEqual( + mock_safe_nav.call_args_list, + [call("PP5"), call("CP6")], + ) + + def test_cp6_low_battery_unknown_charging_state_skip_navigation(self): + """ + 功能: + 验证 AGV 在 CP6 且低电量但无法确认充电状态时, 保守跳过进出站. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + side_effect=[ + {"battery_level": 0.45, "ret_code": 0}, + {"battery_level": 0.45, "ret_code": 0}, + ], + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "charging_state_unknown") + self.assertAlmostEqual(result["battery_level"], 0.45) + self.assertEqual( + mock_query_battery.call_args_list, + [call(simple=True), call(simple=False)], + ) + mock_safe_nav.assert_not_called() + + def test_nav_busy_skips_charge_check(self): + """ + 功能: + 验证 AGV 导航忙碌时, 自动充电检查直接跳过. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 2, "task_status_name": "RUNNING"}, + ), patch.object( + self.controller, + "query_current_station", + ) as mock_query_station, patch.object( + self.controller, + "query_battery_status", + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "skipped_busy_nav") + self.assertEqual(result["nav_task_status"], 2) + mock_query_station.assert_not_called() + mock_query_battery.assert_not_called() + mock_safe_nav.assert_not_called() + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/unilabos/devices/eit_agv/tests/test_agv_comm_retry.py b/unilabos/devices/eit_agv/tests/test_agv_comm_retry.py new file mode 100644 index 00000000..532a3091 --- /dev/null +++ b/unilabos/devices/eit_agv/tests/test_agv_comm_retry.py @@ -0,0 +1,634 @@ +# coding: utf-8 +""" +功能: + AGV通信重试与重连机制的单元测试 + 覆盖场景: + 1. 控制器层 _query_with_retry 通用重试逻辑 + 2. query_current_station / query_battery_status / query_nav_task_status 重试 + 3. 驱动层 navigate_to_target 轮询循环的socket重连逻辑 + 4. AGVDriver.reconnect 方法本身的行为 + 5. auto_charge_check 在重试机制下的集成验证 + +运行方式(从 devices/ 目录): + python -m pytest eit_agv/tests/test_agv_comm_retry.py -v +""" + +import socket +import unittest +from unittest.mock import MagicMock, patch + +# patch路径, 与现有测试保持一致 +_ARM_DRIVER_PATH = "eit_agv.controller.agv_controller.ArmDriver" +_POS_MGR_PATH = "eit_agv.controller.agv_controller.PositionManager" +_AGV_DRIVER_CLS_PATH = "eit_agv.controller.agv_controller.AGVDriver" +_CONTROLLER_SLEEP_PATH = "eit_agv.controller.agv_controller.time.sleep" +_DRIVER_SLEEP_PATH = "eit_agv.driver.agv_driver.time.sleep" + + +def _make_controller(): + """ + 功能: + 创建AGVController实例, 替换硬件依赖, 避免连接真实设备 + 返回: + AGVController实例 + """ + with patch(_ARM_DRIVER_PATH), patch(_POS_MGR_PATH): + from eit_agv.controller.agv_controller import AGVController + return AGVController() + + +# ===================================================================== +# 控制器层: _query_with_retry 通用重试机制 +# ===================================================================== + +class TestQueryWithRetry(unittest.TestCase): + """测试 _query_with_retry 通用重试逻辑""" + + def setUp(self): + self.controller = _make_controller() + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_first_attempt_succeeds_no_sleep(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 首次查询成功 + 预期: + 直接返回结果, 不调用sleep(无需退避等待) + """ + mock_driver = MagicMock() + mock_driver_cls.return_value = mock_driver + + query_result = {"task_status": 0} + query_func = MagicMock(return_value=query_result) + + result = self.controller._query_with_retry(query_func, "测试查询") + + self.assertEqual(result, query_result) + query_func.assert_called_once() + mock_sleep.assert_not_called() + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_retry_on_timeout_then_succeeds(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 第一次connect超时, 第二次查询成功 + 预期: + 返回第二次的结果, sleep被调用一次(退避等待) + """ + # 第一个driver: connect超时 + mock_driver_fail = MagicMock() + mock_driver_fail.connect.side_effect = socket.timeout("timed out") + # 第二个driver: 正常 + mock_driver_ok = MagicMock() + mock_driver_cls.side_effect = [mock_driver_fail, mock_driver_ok] + + query_result = {"station_id": "CP6"} + # query_func只在第二次被调用时能成功(第一次connect就抛异常了) + query_func = MagicMock(return_value=query_result) + + result = self.controller._query_with_retry(query_func, "测试查询") + + self.assertEqual(result, query_result) + # sleep在重试前调用了一次 + mock_sleep.assert_called_once() + # 两个driver都被close了 + mock_driver_fail.close.assert_called_once() + mock_driver_ok.close.assert_called_once() + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_all_retries_exhausted_returns_none(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 所有重试(默认3次)都超时 + 预期: + 返回None + """ + mock_driver = MagicMock() + mock_driver.connect.side_effect = socket.timeout("timed out") + mock_driver_cls.return_value = mock_driver + + query_func = MagicMock() + + result = self.controller._query_with_retry(query_func, "测试查询") + + self.assertIsNone(result) + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_exponential_backoff_delay(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 3次重试均失败 + 预期: + sleep的退避时间为 1.0s, 2.0s (指数退避, 最后一次失败不sleep) + """ + mock_driver = MagicMock() + mock_driver.connect.side_effect = socket.timeout("timed out") + mock_driver_cls.return_value = mock_driver + + query_func = MagicMock() + + self.controller._query_with_retry( + query_func, "测试查询", max_retries=3, retry_delay=1.0 + ) + + # 第1次失败 → sleep(1.0), 第2次失败 → sleep(2.0), 第3次失败 → 不sleep + self.assertEqual(mock_sleep.call_count, 2) + mock_sleep.assert_any_call(1.0) + mock_sleep.assert_any_call(2.0) + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_query_func_exception_triggers_retry(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + connect成功, 但query_func第一次抛异常, 第二次返回结果 + 预期: + 重试后返回正确结果 + """ + mock_driver = MagicMock() + mock_driver_cls.return_value = mock_driver + + query_func = MagicMock( + side_effect=[RuntimeError("AGV返回错误"), {"result": "ok"}] + ) + + result = self.controller._query_with_retry(query_func, "测试查询") + + self.assertEqual(result, {"result": "ok"}) + self.assertEqual(query_func.call_count, 2) + + +# ===================================================================== +# 控制器层: 三个查询方法各自的重试验证 +# ===================================================================== + +class TestQueryCurrentStationRetry(unittest.TestCase): + """测试 query_current_station 的重试机制""" + + def setUp(self): + self.controller = _make_controller() + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_station_query_retry_success_on_second_attempt(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 第一次connect超时, 第二次查询到CP6 + 预期: + 返回CP6站点信息 + """ + mock_driver_fail = MagicMock() + mock_driver_fail.connect.side_effect = socket.timeout("timed out") + + mock_driver_ok = MagicMock() + mock_driver_ok.query_robot_location.return_value = { + "current_station": "CP6" + } + + mock_driver_cls.side_effect = [mock_driver_fail, mock_driver_ok] + + result = self.controller.query_current_station() + + self.assertIsNotNone(result) + self.assertEqual(result["station_id"], "CP6") + self.assertEqual(result["station_name"], "charging_station") + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_station_query_all_fail_returns_none(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 所有重试均超时 + 预期: + 返回None + """ + mock_driver = MagicMock() + mock_driver.connect.side_effect = socket.timeout("timed out") + mock_driver_cls.return_value = mock_driver + + result = self.controller.query_current_station() + + self.assertIsNone(result) + + +class TestQueryBatteryStatusRetry(unittest.TestCase): + """测试 query_battery_status 的重试机制""" + + def setUp(self): + self.controller = _make_controller() + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_battery_query_retry_success(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 第一次超时, 第二次成功返回电量60% + 预期: + 返回包含电池信息的dict + """ + mock_driver_fail = MagicMock() + mock_driver_fail.connect.side_effect = socket.timeout("timed out") + + mock_driver_ok = MagicMock() + mock_driver_ok.query_battery_status.return_value = { + "battery_level": 0.6, "ret_code": 0 + } + + mock_driver_cls.side_effect = [mock_driver_fail, mock_driver_ok] + + result = self.controller.query_battery_status(simple=True) + + self.assertIsNotNone(result) + self.assertAlmostEqual(result["battery_level"], 0.6) + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_battery_query_ret_code_error_triggers_retry(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 第一次返回ret_code非0(业务错误), 第二次正常 + 预期: + 业务错误也触发重试, 最终返回正确结果 + """ + mock_driver_1 = MagicMock() + mock_driver_1.query_battery_status.return_value = { + "ret_code": -1, "err_msg": "AGV忙碌" + } + + mock_driver_2 = MagicMock() + mock_driver_2.query_battery_status.return_value = { + "battery_level": 0.45, "ret_code": 0 + } + + mock_driver_cls.side_effect = [mock_driver_1, mock_driver_2] + + result = self.controller.query_battery_status(simple=True) + + self.assertIsNotNone(result) + self.assertAlmostEqual(result["battery_level"], 0.45) + + +class TestQueryNavTaskStatusRetry(unittest.TestCase): + """测试 query_nav_task_status 的重试机制""" + + def setUp(self): + self.controller = _make_controller() + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_nav_status_retry_success(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 第一次超时, 第二次查询到NONE状态 + 预期: + 返回task_status=0 + """ + mock_driver_fail = MagicMock() + mock_driver_fail.connect.side_effect = socket.timeout("timed out") + + mock_driver_ok = MagicMock() + mock_driver_ok.query_agv_nav_status.return_value = {"task_status": 0} + + mock_driver_cls.side_effect = [mock_driver_fail, mock_driver_ok] + + result = self.controller.query_nav_task_status() + + self.assertIsNotNone(result) + self.assertEqual(result["task_status"], 0) + self.assertEqual(result["task_status_name"], "NONE") + + @patch(_CONTROLLER_SLEEP_PATH) + @patch(_AGV_DRIVER_CLS_PATH) + def test_nav_status_returns_none_triggers_retry(self, mock_driver_cls, mock_sleep): + """ + 验收条件: + 第一次query_agv_nav_status返回None, 第二次正常 + 预期: + None结果被当作异常触发重试, 最终返回正确结果 + """ + mock_driver_1 = MagicMock() + mock_driver_1.query_agv_nav_status.return_value = None + + mock_driver_2 = MagicMock() + mock_driver_2.query_agv_nav_status.return_value = {"task_status": 2} + + mock_driver_cls.side_effect = [mock_driver_1, mock_driver_2] + + result = self.controller.query_nav_task_status() + + self.assertIsNotNone(result) + self.assertEqual(result["task_status"], 2) + self.assertEqual(result["task_status_name"], "RUNNING") + + +# ===================================================================== +# 驱动层: AGVDriver.reconnect 方法 +# ===================================================================== + +class TestReconnectMethod(unittest.TestCase): + """测试 AGVDriver.reconnect 方法""" + + @patch("eit_agv.driver.agv_driver.socket.socket") + def test_reconnect_closes_old_socket_and_creates_new(self, mock_socket_cls): + """ + 验收条件: + 已有旧socket的情况下调用reconnect + 预期: + 旧socket被关闭, 新socket被创建并连接 + """ + from eit_agv.driver.agv_driver import AGVDriver, AGVDriverConfig + + driver = AGVDriver(AGVDriverConfig( + host="127.0.0.1", + port=19204, + port_navigation=19206, + timeout_s=1.0, + )) + + # 模拟旧socket + old_sock = MagicMock() + driver._sock = old_sock + + # 新socket + new_sock = MagicMock() + mock_socket_cls.return_value = new_sock + + driver.reconnect() + + old_sock.close.assert_called_once() + new_sock.settimeout.assert_called_once_with(1.0) + new_sock.connect.assert_called_once_with(("127.0.0.1", 19204)) + self.assertIs(driver._sock, new_sock) + + @patch("eit_agv.driver.agv_driver.socket.socket") + def test_reconnect_without_existing_socket(self, mock_socket_cls): + """ + 验收条件: + 无旧socket时调用reconnect + 预期: + 直接创建新连接, 不抛异常 + """ + from eit_agv.driver.agv_driver import AGVDriver, AGVDriverConfig + + driver = AGVDriver(AGVDriverConfig( + host="127.0.0.1", + port=19204, + port_navigation=19206, + timeout_s=1.0, + )) + driver._sock = None + + new_sock = MagicMock() + mock_socket_cls.return_value = new_sock + + driver.reconnect() + + new_sock.connect.assert_called_once_with(("127.0.0.1", 19204)) + self.assertIs(driver._sock, new_sock) + + @patch("eit_agv.driver.agv_driver.socket.socket") + def test_reconnect_navigation_closes_old_and_creates_new(self, mock_socket_cls): + """ + 验收条件: + 已有旧导航socket的情况下调用reconnect_navigation + 预期: + 旧导航socket被关闭, 新导航socket被创建并连接到导航端口 + """ + from eit_agv.driver.agv_driver import AGVDriver, AGVDriverConfig + + driver = AGVDriver(AGVDriverConfig( + host="127.0.0.1", + port=19204, + port_navigation=19206, + timeout_s=1.0, + )) + + old_nav_sock = MagicMock() + driver._sock_nav = old_nav_sock + + new_sock = MagicMock() + mock_socket_cls.return_value = new_sock + + driver.reconnect_navigation() + + old_nav_sock.close.assert_called_once() + new_sock.connect.assert_called_once_with(("127.0.0.1", 19206)) + self.assertIs(driver._sock_nav, new_sock) + + +# ===================================================================== +# 驱动层: navigate_to_target 轮询循环重连 +# ===================================================================== + +class TestNavigateToTargetReconnect(unittest.TestCase): + """测试 navigate_to_target 轮询循环中的socket重连逻辑""" + + def _make_driver(self): + """ + 功能: + 创建AGVDriver测试实例 + 返回: + AGVDriver实例 + """ + from eit_agv.driver.agv_driver import AGVDriver, AGVDriverConfig + driver = AGVDriver(AGVDriverConfig( + host="127.0.0.1", + port=19204, + port_navigation=19206, + timeout_s=1.0, + debug_hex=False, + )) + return driver + + @patch(_DRIVER_SLEEP_PATH) + def test_reconnect_on_connection_reset_then_succeeds(self, mock_sleep): + """ + 验收条件: + 轮询过程中连接被重置(WinError 10054), 重连后查询成功 + 预期: + 调用reconnect一次, 最终返回COMPLETED状态 + """ + driver = self._make_driver() + driver._sock_nav = MagicMock() + driver._sock = MagicMock() + + nav_response = {"ret_code": 0} + completed_status = {"task_status": 4, "target_id": "LM1"} + + with patch.object(driver, "_send_and_recv_json") as mock_send_recv, \ + patch.object(driver, "connect"), \ + patch.object(driver, "connect_navigation"), \ + patch.object(driver, "reconnect") as mock_reconnect: + + mock_send_recv.side_effect = [ + nav_response, # 发送导航指令成功 + ConnectionResetError("[WinError 10054]"), # 查询状态时连接被重置 + completed_status, # 重连后查询成功 + ] + + result = driver.navigate_to_target(target_id="LM1") + + self.assertEqual(result["task_status"], 4) + mock_reconnect.assert_called_once() + + @patch(_DRIVER_SLEEP_PATH) + def test_reconnect_exhausted_raises_connection_error(self, mock_sleep): + """ + 验收条件: + 连续多次重连全部失败 + 预期: + 抛出ConnectionError, 不会无限循环 + """ + driver = self._make_driver() + driver._sock_nav = MagicMock() + driver._sock = MagicMock() + + with patch.object(driver, "_send_and_recv_json") as mock_send_recv, \ + patch.object(driver, "connect"), \ + patch.object(driver, "connect_navigation"), \ + patch.object(driver, "reconnect", side_effect=OSError("连接失败")): + + mock_send_recv.side_effect = [ + {"ret_code": 0}, # 导航指令发送成功 + ] + [ConnectionResetError("[WinError 10054]")] * 10 + + with self.assertRaises(ConnectionError): + driver.navigate_to_target(target_id="LM1") + + @patch(_DRIVER_SLEEP_PATH) + def test_intermittent_errors_reset_counter_on_success(self, mock_sleep): + """ + 验收条件: + 间歇性连接错误: 错误→成功(RUNNING)→错误→成功(COMPLETED) + 预期: + 成功查询会重置连续错误计数, 不会累积触发放弃 + """ + driver = self._make_driver() + driver._sock_nav = MagicMock() + driver._sock = MagicMock() + + running_status = {"task_status": 2} # RUNNING + completed_status = {"task_status": 4} # COMPLETED + + with patch.object(driver, "_send_and_recv_json") as mock_send_recv, \ + patch.object(driver, "connect"), \ + patch.object(driver, "connect_navigation"), \ + patch.object(driver, "reconnect") as mock_reconnect: + + mock_send_recv.side_effect = [ + {"ret_code": 0}, # 导航指令发送 + ConnectionResetError("reset 1"), # 第1次连接错误 + running_status, # 重连后查询成功(计数归零) + ConnectionResetError("reset 2"), # 第2次连接错误(从0开始) + completed_status, # 重连后导航完成 + ] + + result = driver.navigate_to_target(target_id="LM1") + + self.assertEqual(result["task_status"], 4) + self.assertEqual(mock_reconnect.call_count, 2) + + @patch(_DRIVER_SLEEP_PATH) + def test_non_connection_error_also_counted(self, mock_sleep): + """ + 验收条件: + 非连接类异常(如ValueError)连续发生达到上限 + 预期: + 同样触发终止, 抛出异常 + """ + driver = self._make_driver() + driver._sock_nav = MagicMock() + driver._sock = MagicMock() + + with patch.object(driver, "_send_and_recv_json") as mock_send_recv, \ + patch.object(driver, "connect"), \ + patch.object(driver, "connect_navigation"): + + mock_send_recv.side_effect = [ + {"ret_code": 0}, # 导航指令发送成功 + ] + [ValueError("解析响应失败")] * 10 + + with self.assertRaises(ValueError): + driver.navigate_to_target(target_id="LM1") + + +# ===================================================================== +# 集成层: auto_charge_check 在重试机制下的行为 +# ===================================================================== + +class TestAutoChargeCheckWithRetry(unittest.TestCase): + """验证 auto_charge_check 在查询方法带重试机制后仍能正常工作""" + + def setUp(self): + self.controller = _make_controller() + + def test_auto_charge_check_normal_flow(self): + """ + 验收条件: + 所有查询方法正常返回(内部可能经历了重试, 但外部看不到) + 预期: + auto_charge_check 正常返回 battery_sufficient + """ + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"} + ), patch.object( + self.controller, "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"} + ), patch.object( + self.controller, "query_battery_status", + return_value={"battery_level": 0.6, "ret_code": 0} + ): + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "battery_sufficient") + + def test_auto_charge_check_nav_query_none_continues(self): + """ + 验收条件: + query_nav_task_status 所有重试耗尽返回None + 预期: + nav_status为None时不进入busy分支, 继续查询位置和电量, 正常完成检查 + """ + with patch.object( + self.controller, "query_nav_task_status", + return_value=None + ), patch.object( + self.controller, "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"} + ), patch.object( + self.controller, "query_battery_status", + return_value={"battery_level": 0.6, "ret_code": 0} + ): + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "battery_sufficient") + + def test_auto_charge_check_station_query_none_returns_error(self): + """ + 验收条件: + query_current_station 所有重试耗尽返回None + 预期: + auto_charge_check 返回 error + query_location + """ + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"} + ), patch.object( + self.controller, "query_current_station", + return_value=None + ): + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "error") + self.assertEqual(result["action"], "query_location") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/unilabos/devices/eit_agv/tests/test_auto_charge_check.py b/unilabos/devices/eit_agv/tests/test_auto_charge_check.py new file mode 100644 index 00000000..b3008bf2 --- /dev/null +++ b/unilabos/devices/eit_agv/tests/test_auto_charge_check.py @@ -0,0 +1,322 @@ +# coding: utf-8 +""" +功能: + auto_charge_check 验收测试 + 覆盖5个测试场景: + 1. 空闲 + 电量60% → battery_sufficient, 无移动 + 2. 空闲 + 电量45% + 不在CP6 → low_battery_return_to_cp6 + 3. 任务锁有效 + 电量45% → skipped / task_locked + 4. 仅导航忙碌 + 电量45% → skipped / skipped_busy_nav + 5. 锁文件心跳超时 → 视为过期清理, 后续正常判定 + +运行方式(从 devices/ 目录): + python -m pytest eit_agv/tests/test_auto_charge_check.py -v +""" + +import unittest + +def load_tests(loader, tests, pattern): + """ + 功能: + 兼容旧测试模块路径, 并避免在 unittest discover 时重复加载新回归用例. + 参数: + loader: unittest 测试加载器. + tests: 默认测试集合. + pattern: 测试发现模式. + 返回: + unittest.TestSuite, 直接运行旧模块时转发到新回归用例, discover 时返回空集合. + """ + if pattern is not None: + return loader.suiteClass() + + from eit_agv.tests import test_auto_charge_check_regression + + return loader.loadTestsFromModule(test_auto_charge_check_regression) + +import json +import os +import tempfile +import time +import unittest +from unittest.mock import MagicMock, patch + + +# 以下 patch 在 AGVController 被导入前替换驱动, 避免连接真实设备 +_ARM_DRIVER_PATH = "eit_agv.controller.agv_controller.ArmDriver" +_POS_MGR_PATH = "eit_agv.controller.agv_controller.PositionManager" + + +def _make_controller(lock_file_path: str): + """ + 功能: + 创建 AGVController 实例, 将机械臂驱动和位置管理器全部 mock 掉 + 参数: + lock_file_path: 测试用临时锁文件路径 + 返回: + AGVController 实例 + """ + with patch(_ARM_DRIVER_PATH), patch(_POS_MGR_PATH): + from eit_agv.controller.agv_controller import AGVController + return AGVController(lock_file_path=lock_file_path) + + +class TestAutoChargeCheck(unittest.TestCase): + """auto_charge_check 验收测试套件""" + + def setUp(self): + # 每个用例使用独立临时目录, 保证锁文件互不干扰 + self.tmp_dir = tempfile.mkdtemp() + self.lock_file = os.path.join(self.tmp_dir, "agv_task.lock.json") + self.controller = _make_controller(self.lock_file) + + def tearDown(self): + # 确保锁已释放, 清理临时文件 + try: + self.controller.release_task_lock() + except Exception: + pass + if os.path.exists(self.lock_file): + os.remove(self.lock_file) + os.rmdir(self.tmp_dir) + + # ------------------------------------------------------------------ + # 场景1: 空闲 + 电量60% + # ------------------------------------------------------------------ + + def test_scenario1_idle_sufficient_battery(self): + """ + 验收条件: + 空闲(无锁, 导航NONE) + 在CP6 + 电量60% + 预期: + 返回 action=battery_sufficient, 不调用任何导航方法 + """ + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"} + ), patch.object( + self.controller, "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"} + ), patch.object( + self.controller, "query_battery_status", + return_value={"battery_level": 0.6, "ret_code": 0} + ), patch.object( + self.controller, "go_to_charging_station" + ) as mock_go, patch.object( + self.controller, "safe_navigate_to_station" + ) as mock_safe_nav: + + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "battery_sufficient") + self.assertAlmostEqual(result["battery_level"], 0.6) + # 不允许发出任何导航命令 + mock_go.assert_not_called() + mock_safe_nav.assert_not_called() + + # ------------------------------------------------------------------ + # 场景2: 空闲 + 电量45% + 不在CP6 + # ------------------------------------------------------------------ + + def test_scenario2_low_battery_not_at_cp6(self): + """ + 验收条件: + 空闲(无锁, 导航NONE) + 在LM1 + 电量45% + 预期: + 返回 action=low_battery_return_to_cp6, 调用 go_to_charging_station + """ + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"} + ), patch.object( + self.controller, "query_current_station", + return_value={"station_id": "LM1", "station_name": "synthesis_station"} + ), patch.object( + self.controller, "query_battery_status", + return_value={"battery_level": 0.45, "ret_code": 0} + ), patch.object( + self.controller, "go_to_charging_station", + return_value={"ret_code": 0} + ) as mock_go, patch.object( + self.controller, "safe_navigate_to_station" + ) as mock_safe_nav: + + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "low_battery_return_to_cp6") + self.assertAlmostEqual(result["battery_level"], 0.45) + # 必须调用了导航回CP6 + mock_go.assert_called_once() + # 不应触发PP5充电循环 + mock_safe_nav.assert_not_called() + + # ------------------------------------------------------------------ + # 场景3: 执行 batch_transfer_materials 期间 + 电量45% + # ------------------------------------------------------------------ + + def test_scenario3_task_locked_skip(self): + """ + 验收条件: + 任务锁有效(batch_transfer_materials) + 电量45% + 预期: + 返回 status=skipped, action=task_locked + 不发送任何导航命令 + """ + # 模拟任务正在执行, 持有锁 + self.controller.acquire_task_lock("batch_transfer_materials") + + with patch.object( + self.controller, "go_to_charging_station" + ) as mock_go, patch.object( + self.controller, "safe_navigate_to_station" + ) as mock_safe_nav: + + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "task_locked") + self.assertEqual(result["task_name"], "batch_transfer_materials") + mock_go.assert_not_called() + mock_safe_nav.assert_not_called() + + # ------------------------------------------------------------------ + # 场景4: 仅导航忙碌(无锁文件) + 电量45% + # ------------------------------------------------------------------ + + def test_scenario4_nav_busy_no_lock(self): + """ + 验收条件: + 无锁文件, 但 AGV 导航状态为 RUNNING(2) + 电量45% + 预期: + 返回 status=skipped, action=skipped_busy_nav + 不发送任何导航命令 + """ + # 确认无锁文件 + self.assertFalse(os.path.exists(self.lock_file)) + + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 2, "task_status_name": "RUNNING"} + ), patch.object( + self.controller, "go_to_charging_station" + ) as mock_go, patch.object( + self.controller, "safe_navigate_to_station" + ) as mock_safe_nav: + + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "skipped_busy_nav") + self.assertEqual(result["nav_task_status"], 2) + mock_go.assert_not_called() + mock_safe_nav.assert_not_called() + + def test_scenario4_nav_waiting_no_lock(self): + """导航状态 WAITING(1) 同样触发 skipped_busy_nav""" + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 1, "task_status_name": "WAITING"} + ), patch.object(self.controller, "go_to_charging_station") as mock_go: + + result = self.controller.auto_charge_check() + + self.assertEqual(result["action"], "skipped_busy_nav") + mock_go.assert_not_called() + + # ------------------------------------------------------------------ + # 场景5: 锁文件存在但心跳超时 + # ------------------------------------------------------------------ + + def test_scenario5_expired_lock_cleared_and_charged(self): + """ + 验收条件: + 锁文件存在, 但 heartbeat_ts 超过 heartbeat_timeout(90s) + AGV 在 CP6, 电量60% + 预期: + 锁被视为过期并自动清理 + 后续按正常逻辑判定 → battery_sufficient + 锁文件不再存在 + """ + # 手动写入一个已过期的锁文件(心跳200秒前) + expired_lock = { + "pid": 99999, + "task_name": "old_stale_task", + "start_ts": time.time() - 400, + "heartbeat_ts": time.time() - 200, # 默认 timeout=90s, 200s > 90s + } + with open(self.lock_file, "w", encoding="utf-8") as f: + json.dump(expired_lock, f) + + # 锁文件确实存在 + self.assertTrue(os.path.exists(self.lock_file)) + + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"} + ), patch.object( + self.controller, "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"} + ), patch.object( + self.controller, "query_battery_status", + return_value={"battery_level": 0.6, "ret_code": 0} + ), patch.object( + self.controller, "go_to_charging_station" + ) as mock_go: + + result = self.controller.auto_charge_check() + + # 过期锁已被自动清理 + self.assertFalse( + os.path.exists(self.lock_file), + "过期锁文件应被自动清理" + ) + # 按正常逻辑判定: 电量充足 + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "battery_sufficient") + mock_go.assert_not_called() + + def test_scenario5_expired_lock_cleared_then_charge(self): + """ + 验收条件: + 锁文件过期, AGV 在 CP6, 电量45% + 预期: + 锁清理后触发充电循环(charge_cycle_completed) + """ + expired_lock = { + "pid": 99999, + "task_name": "old_stale_task", + "start_ts": time.time() - 400, + "heartbeat_ts": time.time() - 200, + } + with open(self.lock_file, "w", encoding="utf-8") as f: + json.dump(expired_lock, f) + + with patch.object( + self.controller, "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"} + ), patch.object( + self.controller, "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"} + ), patch.object( + self.controller, "query_battery_status", + return_value={"battery_level": 0.45, "ret_code": 0} + ), patch.object( + self.controller, "safe_navigate_to_station", + return_value={"ret_code": 0} + ) as mock_safe_nav: + + result = self.controller.auto_charge_check() + + self.assertFalse(os.path.exists(self.lock_file), "过期锁文件应被自动清理") + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "charge_cycle_completed") + # PP5 和 CP6 各调用一次 + self.assertEqual(mock_safe_nav.call_count, 2) + calls = [c.args[0] for c in mock_safe_nav.call_args_list] + self.assertIn("PP5", calls) + self.assertIn("CP6", calls) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/unilabos/devices/eit_agv/tests/test_auto_charge_check_regression.py b/unilabos/devices/eit_agv/tests/test_auto_charge_check_regression.py new file mode 100644 index 00000000..eaeea30d --- /dev/null +++ b/unilabos/devices/eit_agv/tests/test_auto_charge_check_regression.py @@ -0,0 +1,249 @@ +# coding: utf-8 +""" +功能: + 覆盖 auto_charge_check 的核心回归场景, 验证低电量时的充电状态保护逻辑. +参数: + 无. +返回: + 无. 通过 unittest 执行断言. +""" + +import unittest +from unittest.mock import call, patch + + +_ARM_DRIVER_PATH = "eit_agv.controller.agv_controller.ArmDriver" +_POS_MGR_PATH = "eit_agv.controller.agv_controller.PositionManager" + + +def _make_controller(): + """ + 功能: + 创建 AGVController 实例, 并在构造阶段替换硬件相关依赖, 避免连接真实设备. + 参数: + 无. + 返回: + AGVController, 可用于自动充电逻辑测试的控制器实例. + """ + with patch(_ARM_DRIVER_PATH), patch(_POS_MGR_PATH): + from eit_agv.controller.agv_controller import AGVController + + return AGVController() + + +class TestAutoChargeCheckRegression(unittest.TestCase): + """ + 功能: + 自动充电检查回归测试套件. + 参数: + 无. + 返回: + 无. + """ + + def setUp(self): + """ + 功能: + 为每个测试用例创建独立的控制器实例. + 参数: + 无. + 返回: + 无. + """ + self.controller = _make_controller() + + def test_cp6_sufficient_battery_no_navigation(self): + """ + 功能: + 验证 AGV 在 CP6 且电量充足时, 不触发任何导航动作. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.6, "ret_code": 0}, + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "battery_sufficient") + self.assertAlmostEqual(result["battery_level"], 0.6) + mock_query_battery.assert_called_once_with(simple=True) + mock_safe_nav.assert_not_called() + + def test_cp6_low_battery_already_charging_skip_navigation(self): + """ + 功能: + 验证 AGV 在 CP6 且电量低于50%但已经在充电时, 不执行进出站动作. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + side_effect=[ + {"battery_level": 0.45, "ret_code": 0}, + {"battery_level": 0.45, "charging": True, "ret_code": 0}, + ], + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "already_charging") + self.assertAlmostEqual(result["battery_level"], 0.45) + self.assertIs(result["charging"], True) + self.assertEqual( + mock_query_battery.call_args_list, + [call(simple=True), call(simple=False)], + ) + mock_safe_nav.assert_not_called() + + def test_cp6_low_battery_not_charging_runs_redock_cycle(self): + """ + 功能: + 验证 AGV 在 CP6 且低电量未充电时, 会执行 PP5 到 CP6 的重对接流程. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + side_effect=[ + {"task_status": 0, "task_status_name": "NONE"}, + {"task_status": 0, "task_status_name": "NONE"}, + ], + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + side_effect=[ + {"battery_level": 0.45, "ret_code": 0}, + {"battery_level": 0.45, "charging": False, "ret_code": 0}, + {"battery_level": 0.45, "charging": True, "ret_code": 0}, + ], + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + side_effect=[{"ret_code": 0}, {"ret_code": 0}], + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "charge_cycle_completed") + self.assertAlmostEqual(result["battery_level"], 0.45) + self.assertIs(result["charging"], True) + self.assertEqual( + mock_query_battery.call_args_list, + [call(simple=True), call(simple=False), call(simple=False)], + ) + self.assertEqual( + mock_safe_nav.call_args_list, + [call("PP5"), call("CP6")], + ) + + def test_cp6_low_battery_unknown_charging_state_skip_navigation(self): + """ + 功能: + 验证 AGV 在 CP6 且低电量但无法确认充电状态时, 保守跳过进出站. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + side_effect=[ + {"battery_level": 0.45, "ret_code": 0}, + {"battery_level": 0.45, "ret_code": 0}, + ], + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "charging_state_unknown") + self.assertAlmostEqual(result["battery_level"], 0.45) + self.assertEqual( + mock_query_battery.call_args_list, + [call(simple=True), call(simple=False)], + ) + mock_safe_nav.assert_not_called() + + def test_nav_busy_skips_charge_check(self): + """ + 功能: + 验证 AGV 导航忙碌时, 自动充电检查直接跳过. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 2, "task_status_name": "RUNNING"}, + ), patch.object( + self.controller, + "query_current_station", + ) as mock_query_station, patch.object( + self.controller, + "query_battery_status", + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "skipped_busy_nav") + self.assertEqual(result["nav_task_status"], 2) + mock_query_station.assert_not_called() + mock_query_battery.assert_not_called() + mock_safe_nav.assert_not_called() + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/unilabos/devices/eit_agv/tests/test_auto_charge_pp5_cp6_check_regression.py b/unilabos/devices/eit_agv/tests/test_auto_charge_pp5_cp6_check_regression.py new file mode 100644 index 00000000..7d81a4f4 --- /dev/null +++ b/unilabos/devices/eit_agv/tests/test_auto_charge_pp5_cp6_check_regression.py @@ -0,0 +1,363 @@ +# coding: utf-8 +""" +功能: + 覆盖 auto_charge_pp5_cp6_check 的核心回归场景, 验证PP5/CP6待命充电切换逻辑. +参数: + 无. +返回: + 无, 通过 unittest 执行断言. +""" + +import unittest +from unittest.mock import patch + + +_ARM_DRIVER_PATH = "eit_agv.controller.agv_controller.ArmDriver" +_POS_MGR_PATH = "eit_agv.controller.agv_controller.PositionManager" + + +def _make_controller(): + """ + 功能: + 创建 AGVController 实例, 并替换硬件相关依赖, 避免连接真实设备. + 参数: + 无. + 返回: + AGVController, 可用于PP5/CP6自动充电逻辑测试的控制器实例. + """ + with patch(_ARM_DRIVER_PATH), patch(_POS_MGR_PATH): + from eit_agv.controller.agv_controller import AGVController + + return AGVController() + + +class TestAutoChargePp5Cp6CheckRegression(unittest.TestCase): + """ + 功能: + PP5/CP6自动充电检查回归测试套件. + 参数: + 无. + 返回: + 无. + """ + + def setUp(self): + """ + 功能: + 为每个测试用例创建独立的控制器实例. + 参数: + 无. + 返回: + 无. + """ + self.controller = _make_controller() + + def test_nav_busy_skips_check(self): + """ + 功能: + 验证导航忙碌时直接跳过PP5/CP6充电检查. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 2, "task_status_name": "RUNNING"}, + ), patch.object( + self.controller, + "query_current_station", + ) as mock_query_station, patch.object( + self.controller, + "query_battery_status", + ) as mock_query_battery: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "skipped_busy_nav") + mock_query_station.assert_not_called() + mock_query_battery.assert_not_called() + + def test_station_query_failure_returns_error(self): + """ + 功能: + 验证站点查询失败时返回 query_location 错误. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value=None, + ): + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "error") + self.assertEqual(result["action"], "query_location") + + def test_battery_query_failure_returns_error(self): + """ + 功能: + 验证电量查询失败时返回 query_battery 错误. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "PP5", "station_name": "charging_transition_point"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value=None, + ): + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "error") + self.assertEqual(result["action"], "query_battery") + self.assertEqual(result["current_station"], "PP5") + + def test_pp5_low_battery_moves_to_cp6(self): + """ + 功能: + 验证AGV在PP5且电量低于50%时, 会移动到CP6充电. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "PP5", "station_name": "charging_transition_point"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.45, "ret_code": 0}, + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + return_value={"ret_code": 0}, + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "pp5_to_cp6_for_charge") + self.assertAlmostEqual(result["battery_level"], 0.45) + mock_query_battery.assert_called_once_with(simple=True) + mock_safe_nav.assert_called_once_with("CP6") + + def test_pp5_threshold_battery_stays_put(self): + """ + 功能: + 验证AGV在PP5且电量等于50%时, 继续在PP5待命. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "PP5", "station_name": "charging_transition_point"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.5, "ret_code": 0}, + ), patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "standby_at_pp5") + self.assertAlmostEqual(result["battery_level"], 0.5) + mock_safe_nav.assert_not_called() + + def test_cp6_high_battery_moves_to_pp5(self): + """ + 功能: + 验证AGV在CP6且电量高于90%时, 会返回PP5待命. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.91, "ret_code": 0}, + ) as mock_query_battery, patch.object( + self.controller, + "safe_navigate_to_station", + return_value={"ret_code": 0}, + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "cp6_to_pp5_after_charge") + self.assertAlmostEqual(result["battery_level"], 0.91) + mock_query_battery.assert_called_once_with(simple=True) + mock_safe_nav.assert_called_once_with("PP5") + + def test_cp6_threshold_battery_stays_put(self): + """ + 功能: + 验证AGV在CP6且电量等于90%时, 继续在CP6待命. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.9, "ret_code": 0}, + ), patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "success") + self.assertEqual(result["action"], "standby_at_cp6") + self.assertAlmostEqual(result["battery_level"], 0.9) + mock_safe_nav.assert_not_called() + + def test_non_pp5_cp6_station_skips_check(self): + """ + 功能: + 验证AGV不在PP5或CP6时, 视为工作途中并跳过检查. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "LM1", "station_name": "synthesis_station"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.7, "ret_code": 0}, + ), patch.object( + self.controller, + "safe_navigate_to_station", + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "skipped") + self.assertEqual(result["action"], "working_in_progress") + self.assertEqual(result["current_station"], "LM1") + mock_safe_nav.assert_not_called() + + def test_pp5_low_battery_move_to_cp6_failure(self): + """ + 功能: + 验证AGV在PP5且低电量时, 如果进入CP6失败则返回 move_to_cp6 错误. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "PP5", "station_name": "charging_transition_point"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.2, "ret_code": 0}, + ), patch.object( + self.controller, + "safe_navigate_to_station", + return_value=None, + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "error") + self.assertEqual(result["action"], "move_to_cp6") + mock_safe_nav.assert_called_once_with("CP6") + + def test_cp6_high_battery_move_to_pp5_failure(self): + """ + 功能: + 验证AGV在CP6且高电量时, 如果返回PP5失败则返回 move_to_pp5 错误. + 参数: + 无. + 返回: + 无. + """ + with patch.object( + self.controller, + "query_nav_task_status", + return_value={"task_status": 0, "task_status_name": "NONE"}, + ), patch.object( + self.controller, + "query_current_station", + return_value={"station_id": "CP6", "station_name": "charging_station"}, + ), patch.object( + self.controller, + "query_battery_status", + return_value={"battery_level": 0.95, "ret_code": 0}, + ), patch.object( + self.controller, + "safe_navigate_to_station", + return_value=None, + ) as mock_safe_nav: + result = self.controller.auto_charge_pp5_cp6_check() + + self.assertEqual(result["status"], "error") + self.assertEqual(result["action"], "move_to_pp5") + mock_safe_nav.assert_called_once_with("PP5") + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/unilabos/devices/eit_agv/tests/test_transfer_analysis_to_shelf.py b/unilabos/devices/eit_agv/tests/test_transfer_analysis_to_shelf.py new file mode 100644 index 00000000..0e364493 --- /dev/null +++ b/unilabos/devices/eit_agv/tests/test_transfer_analysis_to_shelf.py @@ -0,0 +1,294 @@ +# coding: utf-8 +""" +功能: + 覆盖 transfer_analysis_to_shelf 的核心流程, 验证分析站落架后自动回充逻辑. +参数: + 无. +返回: + 无. 通过 unittest 执行断言. +""" + +import unittest +from unittest.mock import MagicMock, patch + + +_ARM_DRIVER_PATH = "eit_agv.controller.agv_controller.ArmDriver" +_POS_MGR_PATH = "eit_agv.controller.agv_controller.PositionManager" +_SHELF_MANAGER_PATH = "eit_agv.controller.agv_controller.ShelfManager" +_ZHIDA_CLIENT_PATH = "unilabos.devices.eit_analysis_station.driver.zhida_driver.ZhidaClient" +_SLEEP_PATH = "eit_agv.controller.agv_controller.time.sleep" + + +def _make_status_detail(raw_status: str, base_status: str = None, sub_status: str = "") -> dict: + """ + 功能: + 构造智达状态明细字典, 统一测试里的状态 mock 结构. + 参数: + raw_status: 原始状态字符串. + base_status: 主状态, None 表示与原始状态一致. + sub_status: 子状态字符串. + 返回: + Dict, 包含 raw_status/base_status/sub_status 三个字段. + """ + if base_status is None: + base_status = raw_status + return { + "raw_status": raw_status, + "base_status": base_status, + "sub_status": sub_status, + } + + +def _make_controller(): + """ + 功能: + 创建 AGVController 实例, 并替换硬件与货架管理依赖, 避免连接真实设备和写入真实状态. + 参数: + 无. + 返回: + AGVController, 用于测试分析站落架流程的控制器实例. + """ + with patch(_ARM_DRIVER_PATH), patch(_POS_MGR_PATH), patch(_SHELF_MANAGER_PATH): + from eit_agv.controller.agv_controller import AGVController + + return AGVController() + + +class TestTransferAnalysisToShelf(unittest.TestCase): + """ + 功能: + 分析完成样品转运到货架并自动回充的测试套件. + 参数: + 无. + 返回: + 无. + """ + + def setUp(self): + """ + 功能: + 为每个测试用例准备独立的 AGVController 和常用 mock. + 参数: + 无. + 返回: + 无. + """ + self.controller = _make_controller() + self.controller.batch_transfer_materials = MagicMock(return_value=True) + self.controller.go_to_charging_station = MagicMock(return_value={"ret_code": 0}) + self.controller.shelf_manager.find_empty_slots.return_value = ["shelf_tray_1-1"] + + @patch(_ZHIDA_CLIENT_PATH) + def test_transfer_success_and_return_to_charging_station(self, mock_zhida_client): + """ + 功能: + 验证分析站空闲且转运成功时, 会更新货架状态并返回充电站. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Idle") + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, True) + self.controller.batch_transfer_materials.assert_called_once_with( + [ + { + "source_tray": "analysis_station_tray_1-2", + "target_tray": "shelf_tray_1-1", + "material_type": "FLASH_FILTER_OUTER_BOTTLE_TRAY", + } + ], + block=True, + ) + self.controller.shelf_manager.place_material.assert_called_once_with( + slot_name="shelf_tray_1-1", + material_type="FLASH_FILTER_OUTER_BOTTLE_TRAY", + source="analysis_station_tray_1-2", + description="分析完成样品(自动转运)", + ) + self.controller.go_to_charging_station.assert_called_once_with(block=True) + client.connect.assert_called_once_with() + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + def test_transfer_success_but_charging_station_failed_returns_false(self, mock_zhida_client): + """ + 功能: + 验证货架落位成功但回充失败时, 函数返回 False 且不回滚货架状态. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Idle") + self.controller.go_to_charging_station.return_value = None + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, False) + self.controller.shelf_manager.place_material.assert_called_once() + self.controller.go_to_charging_station.assert_called_once_with(block=True) + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + def test_transfer_success_but_charging_station_exception_returns_false(self, mock_zhida_client): + """ + 功能: + 验证货架落位成功但回充抛出异常时, 函数返回 False 且记录错误日志. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Idle") + self.controller.go_to_charging_station.side_effect = RuntimeError("导航异常") + + with self.assertLogs("eit_agv.controller.agv_controller", level="ERROR") as log_context: + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, False) + self.assertTrue( + any("返回充电站异常" in message for message in log_context.output) + ) + self.controller.shelf_manager.place_material.assert_called_once() + self.controller.go_to_charging_station.assert_called_once_with(block=True) + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + def test_batch_transfer_failure_skips_return_to_charging_station(self, mock_zhida_client): + """ + 功能: + 验证批量转运失败时, 不更新货架状态, 也不触发回充. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Idle") + self.controller.batch_transfer_materials.return_value = False + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, False) + self.controller.batch_transfer_materials.assert_called_once() + self.controller.shelf_manager.place_material.assert_not_called() + self.controller.go_to_charging_station.assert_not_called() + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + def test_insufficient_empty_slots_skips_transfer_and_return(self, mock_zhida_client): + """ + 功能: + 验证货架空位不足时, 不执行转运, 也不触发回充. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Idle") + self.controller.shelf_manager.find_empty_slots.return_value = [] + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, False) + self.controller.batch_transfer_materials.assert_not_called() + self.controller.go_to_charging_station.assert_not_called() + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + @patch(_SLEEP_PATH, return_value=None) + def test_status_timeout_skips_transfer_and_return(self, _mock_sleep, mock_zhida_client): + """ + 功能: + 验证分析站长时间未进入 Idle 时, 轮询超时后直接返回 False. + 参数: + _mock_sleep: sleep 补丁对象. + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Busy") + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"], + poll_interval=1.0, + poll_timeout=2.0, + ) + + self.assertIs(result, False) + self.controller.shelf_manager.find_empty_slots.assert_not_called() + self.controller.batch_transfer_materials.assert_not_called() + self.controller.go_to_charging_station.assert_not_called() + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + def test_composite_idle_status_allows_transfer(self, mock_zhida_client): + """ + 功能: + 验证复合状态 Idle#SeqRun:Error 会按 Idle 主状态放行转运. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail( + raw_status="Idle#SeqRun:Error", + base_status="Idle", + sub_status="SeqRun:Error", + ) + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, True) + self.controller.batch_transfer_materials.assert_called_once() + self.controller.go_to_charging_station.assert_called_once_with(block=True) + client.close.assert_called_once_with() + + @patch(_ZHIDA_CLIENT_PATH) + def test_error_status_skips_transfer_and_return(self, mock_zhida_client): + """ + 功能: + 验证分析站出现 Error 状态时, 直接终止流程且不触发回充. + 参数: + mock_zhida_client: 智达客户端补丁对象. + 返回: + 无. + """ + client = mock_zhida_client.return_value + client.get_status_detail.return_value = _make_status_detail("Error") + + result = self.controller.transfer_analysis_to_shelf( + source_trays=["analysis_station_tray_1-2"] + ) + + self.assertIs(result, False) + self.controller.shelf_manager.find_empty_slots.assert_not_called() + self.controller.batch_transfer_materials.assert_not_called() + self.controller.go_to_charging_station.assert_not_called() + client.close.assert_called_once_with() + + +if __name__ == "__main__": + unittest.main() diff --git a/unilabos/devices/eit_agv/utils/coordinate_extractor.py b/unilabos/devices/eit_agv/utils/coordinate_extractor.py new file mode 100644 index 00000000..f0801532 --- /dev/null +++ b/unilabos/devices/eit_agv/utils/coordinate_extractor.py @@ -0,0 +1,428 @@ +# coding:utf-8 +""" +功能: + 从program_file目录中的JSON文件提取所有启用的运动函数坐标, + 并利用arm_driver中的正运动学函数将关节坐标转换为位姿坐标 +""" + +import os +import json +import logging +from typing import Dict, List, Any, Optional + +# 配置日志 +logger = logging.getLogger(__name__) + + +class ProgramCoordinateExtractor: + """ + 功能: + 提取程序文件中的运动坐标并进行坐标转换 + """ + + # 运动函数标签列表 + MOTION_TAGS = ["MoveJ2", "MoveL", "MoveJ", "Start"] + + def __init__(self, program_dir: str = None, arm_driver=None): + """ + 功能: + 初始化坐标提取器 + 参数: + program_dir: 程序文件目录路径, 默认为当前目录下的program_file + arm_driver: ArmDriver实例, 用于坐标转换, 为None时仅提取不转换 + """ + if program_dir is None: + # 默认路径: 项目根目录下的program_file + current_dir = os.path.dirname(os.path.abspath(__file__)) + program_dir = os.path.join(os.path.dirname(current_dir), "program_file") + + self.program_dir = program_dir + self.arm_driver = arm_driver + logger.info(f"坐标提取器初始化, 程序目录: {self.program_dir}") + + def extract_all_files(self) -> Dict[str, List[Dict[str, Any]]]: + """ + 功能: + 提取program_file目录下所有JSON和JSPF文件中的启用运动坐标 + 返回: + Dict, 键为文件名, 值为该文件中提取的运动坐标列表 + """ + results = {} + + if not os.path.exists(self.program_dir): + logger.error(f"程序目录不存在: {self.program_dir}") + return results + + # 遍历目录下所有JSON和JSPF文件 + for filename in os.listdir(self.program_dir): + if filename.endswith(".json") or filename.endswith(".jspf"): + filepath = os.path.join(self.program_dir, filename) + try: + file_results = self.extract_from_file(filepath) + if file_results: + results[filename] = file_results + logger.info(f"从 {filename} 提取了 {len(file_results)} 个运动坐标") + except Exception as e: + logger.error(f"处理文件 {filename} 时出错: {e}") + + return results + + def extract_from_file(self, filepath: str) -> List[Dict[str, Any]]: + """ + 功能: + 从单个JSON文件中提取启用的运动坐标 + 参数: + filepath: JSON文件路径 + 返回: + List, 包含所有启用运动函数的坐标信息 + """ + with open(filepath, "r", encoding="utf-8") as f: + data = json.load(f) + + results = [] + filename = os.path.basename(filepath) + + # 遍历所有模块 + for module in data.get("modules", []): + module_name = module.get("name", "Unknown") + tasks = module.get("tasks", []) + self._extract_from_tasks(tasks, results, filename, module_name) + + return results + + def _extract_from_tasks(self, tasks: List[Dict], results: List[Dict], + filename: str, module_name: str, parent_disabled: bool = False): + """ + 功能: + 递归提取任务列表中的运动坐标 + 参数: + tasks: 任务列表 + results: 结果列表(会被修改) + filename: 文件名 + module_name: 模块名 + parent_disabled: 父级是否被禁用 + """ + for task in tasks: + task_tag = task.get("tag", "") + task_id = task.get("id", "") + # 如果父级禁用或当前任务禁用, 则跳过 + is_disabled = parent_disabled or task.get("disabled", False) + + # 检查是否为运动函数 + if task_tag in self.MOTION_TAGS and not is_disabled: + params = task.get("params", {}) + pose_data = params.get("pose", {}) + + # 只处理有具体坐标值的(type为"p"), 跳过变量引用(type为"v") + if pose_data.get("type") == "p": + coord_info = self._extract_coordinate_info( + task, pose_data, filename, module_name + ) + if coord_info is not None: + results.append(coord_info) + elif pose_data.get("type") == "v": + # 记录变量引用信息 + var_info = { + "file": filename, + "module": module_name, + "task_id": task_id, + "motion_type": task_tag, + "coordinate_type": "variable", + "variable_name": pose_data.get("val", ""), + "point_name": pose_data.get("name", ""), + "joint_values": None, + "pose_values": None, + "comment": params.get("comment", "") + } + results.append(var_info) + + # 递归处理子任务 + sub_tasks = task.get("tasks", []) + if sub_tasks: + self._extract_from_tasks(sub_tasks, results, filename, module_name, is_disabled) + + def _extract_coordinate_info(self, task: Dict, pose_data: Dict, + filename: str, module_name: str) -> Optional[Dict[str, Any]]: + """ + 功能: + 提取单个运动任务的坐标信息 + 参数: + task: 任务字典 + pose_data: 位姿数据字典 + filename: 文件名 + module_name: 模块名 + 返回: + Dict, 包含坐标信息的字典, 提取失败返回None + """ + task_tag = task.get("tag", "") + task_id = task.get("id", "") + params = task.get("params", {}) + + # 获取坐标值 + coord_values = pose_data.get("val", []) + point_name = pose_data.get("name", "") + + if not coord_values or len(coord_values) != 6: + return None + + # 判断坐标类型: MoveJ2使用关节坐标, MoveL使用位姿坐标 + # 根据params中的type字段判断 + coord_type_param = params.get("type", "") + + # MoveL默认是位姿坐标, MoveJ2根据type参数判断 + if task_tag == "MoveL": + is_joint = False + elif task_tag in ["MoveJ2", "MoveJ"]: + is_joint = (coord_type_param == "joint") + elif task_tag == "Start": + is_joint = False # Start通常是位姿坐标 + else: + is_joint = False + + result = { + "file": filename, + "module": module_name, + "task_id": task_id, + "motion_type": task_tag, + "coordinate_type": "joint" if is_joint else "pose", + "point_name": point_name, + "original_values": coord_values, + "joint_values": None, + "pose_values": None, + "comment": params.get("comment", "") + } + + if is_joint: + result["joint_values"] = coord_values + # 如果有arm_driver, 进行正运动学转换 + if self.arm_driver is not None: + try: + pose = self.arm_driver.calculate_forward_kinematics(coord_values) + if pose: + # 将位姿坐标的前3个值(x,y,z)从米转换为毫米 + pose_mm = list(pose) + pose_mm[0] *= 1000 # x: m -> mm + pose_mm[1] *= 1000 # y: m -> mm + pose_mm[2] *= 1000 # z: m -> mm + result["pose_values"] = pose_mm + else: + result["pose_values"] = None + except Exception as e: + logger.warning(f"正运动学计算失败 ({point_name}): {e}") + result["pose_values"] = None + else: + # 将位姿坐标的前3个值(x,y,z)从米转换为毫米 + pose_mm = coord_values.copy() + pose_mm[0] *= 1000 # x: m -> mm + pose_mm[1] *= 1000 # y: m -> mm + pose_mm[2] *= 1000 # z: m -> mm + result["pose_values"] = pose_mm + # 如果有arm_driver, 进行逆运动学转换 + if self.arm_driver is not None: + try: + joints = self.arm_driver.calculate_inverse_kinematics(coord_values) + result["joint_values"] = list(joints) if joints else None + except Exception as e: + logger.warning(f"逆运动学计算失败 ({point_name}): {e}") + result["joint_values"] = None + + return result + + def convert_joints_to_pose(self, joint_values: List[float]) -> Optional[List[float]]: + """ + 功能: + 将关节坐标转换为位姿坐标 + 参数: + joint_values: 关节角度列表[q1,q2,q3,q4,q5,q6], 单位rad + 返回: + 位姿列表[x,y,z,rx,ry,rz], 单位m和rad, 转换失败返回None + """ + if self.arm_driver is None: + logger.warning("未设置arm_driver, 无法进行坐标转换") + return None + + try: + pose = self.arm_driver.calculate_forward_kinematics(joint_values) + return list(pose) if pose else None + except Exception as e: + logger.error(f"正运动学计算失败: {e}") + return None + + def generate_report(self, results: Dict[str, List[Dict[str, Any]]]) -> str: + """ + 功能: + 生成坐标提取报告 + 参数: + results: extract_all_files返回的结果字典 + 返回: + str, 格式化的报告文本 + """ + lines = [] + lines.append("=" * 80) + lines.append("程序文件运动坐标提取报告") + lines.append("=" * 80) + + total_count = 0 + + for filename, coords in results.items(): + lines.append(f"\n文件: {filename}") + lines.append("-" * 40) + + for coord in coords: + total_count += 1 + lines.append(f"\n 运动类型: {coord['motion_type']}") + lines.append(f" 点位名称: {coord.get('point_name', 'N/A')}") + lines.append(f" 坐标类型: {coord['coordinate_type']}") + lines.append(f" 备注: {coord.get('comment', '')}") + + if coord['coordinate_type'] == "variable": + lines.append(f" 变量名: {coord.get('variable_name', 'N/A')}") + else: + if coord.get('original_values') is not None: + original_str = ", ".join([f"{v:.6f}" for v in coord['original_values']]) + lines.append(f" 原始值: [{original_str}]") + + if coord.get('joint_values') is not None: + joint_str = ", ".join([f"{v:.6f}" for v in coord['joint_values']]) + lines.append(f" 关节坐标(rad): [{joint_str}]") + + if coord.get('pose_values') is not None: + pose_str = ", ".join([f"{v:.6f}" for v in coord['pose_values']]) + lines.append(f" 位姿坐标(mm,rad): [{pose_str}]") + + lines.append("\n" + "=" * 80) + lines.append(f"总计: {len(results)} 个文件, {total_count} 个运动坐标") + lines.append("=" * 80) + + return "\n".join(lines) + + def export_to_json(self, results: Dict[str, List[Dict[str, Any]]], + output_path: str = None) -> str: + """ + 功能: + 将提取结果导出为JSON文件, 坐标数组显示在一行 + 参数: + results: extract_all_files返回的结果字典 + output_path: 输出文件路径, 默认为program_file目录下的coordinates_export.json + 返回: + str, 输出文件路径 + """ + if output_path is None: + output_path = os.path.join(self.program_dir, "coordinates_export.json") + + # 先生成标准JSON字符串 + json_str = json.dumps(results, ensure_ascii=False, indent=2) + + # 将坐标数组压缩到一行: 匹配包含6个数字的数组 + import re + # 匹配形如 [\n 数字,\n 数字,\n ...] 的模式并压缩为一行 + json_str = re.sub( + r'\[\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*,\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*,\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*,\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*,\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*,\s*(-?\d+\.?\d*(?:e[+-]?\d+)?)\s*\]', + r'[\1, \2, \3, \4, \5, \6]', + json_str + ) + + with open(output_path, "w", encoding="utf-8") as f: + f.write(json_str) + + logger.info(f"坐标数据已导出到: {output_path}") + return output_path + + +def main(): + """ + 功能: + 主函数, 演示坐标提取功能 + """ + # 配置日志输出到控制台 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + print("=" * 60) + print("程序文件运动坐标提取工具") + print("=" * 60) + + # 创建提取器(不连接机械臂, 仅提取坐标) + extractor = ProgramCoordinateExtractor() + + # 提取所有文件的坐标 + print("\n正在提取坐标...") + results = extractor.extract_all_files() + + # 生成报告 + report = extractor.generate_report(results) + print(report) + + # 导出JSON + output_path = extractor.export_to_json(results) + print(f"\n坐标数据已导出到: {output_path}") + + # 如果需要进行坐标转换, 需要连接机械臂 + print("\n" + "=" * 60) + print("提示: 如需进行关节坐标到位姿坐标的转换, 请连接机械臂后运行") + print("示例代码:") + print(" from driver.arm_driver import ArmDriver") + print(" arm = ArmDriver()") + print(" arm.connect()") + print(" extractor = ProgramCoordinateExtractor(arm_driver=arm)") + print(" results = extractor.extract_all_files()") + print("=" * 60) + + +def main_with_arm(): + """ + 功能: + 连接机械臂并进行坐标转换的主函数 + """ + import sys + # 添加项目根目录到路径 + current_dir = os.path.dirname(os.path.abspath(__file__)) + project_root = os.path.dirname(current_dir) + if project_root not in sys.path: + sys.path.insert(0, project_root) + + from driver.arm_driver import ArmDriver + + # 配置日志输出到控制台 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + print("=" * 60) + print("程序文件运动坐标提取工具 (带坐标转换)") + print("=" * 60) + + # 初始化机械臂 + arm = ArmDriver() + + # 连接机械臂 + print("\n正在连接机械臂...") + if not arm.connect(): + print("连接失败, 将仅提取坐标不进行转换") + arm = None + + # 创建提取器 + extractor = ProgramCoordinateExtractor(arm_driver=arm) + + # 提取所有文件的坐标 + print("\n正在提取并转换坐标...") + results = extractor.extract_all_files() + + # 生成报告 + report = extractor.generate_report(results) + print(report) + + # 导出JSON + output_path = extractor.export_to_json(results) + print(f"\n坐标数据已导出到: {output_path}") + + # 断开连接 + if arm is not None: + arm.disconnect() + + +if __name__ == "__main__": + main_with_arm() diff --git a/unilabos/devices/eit_agv/utils/json_formatter.py b/unilabos/devices/eit_agv/utils/json_formatter.py new file mode 100644 index 00000000..37f2bf83 --- /dev/null +++ b/unilabos/devices/eit_agv/utils/json_formatter.py @@ -0,0 +1,259 @@ +""" +JSON格式化工具模块 + +功能: + 将压缩的JSON文件(如.jspf格式)转换为易读的格式化JSON +""" + +import json +import logging +from pathlib import Path +from typing import Union +import re + + +logger = logging.getLogger(__name__) + +def _inline_val_lists(json_text: str) -> str: + """ + 功能: + 将格式化结果中的val列表压缩为单行, 不影响其他字段 + 参数: + json_text: str, 已格式化的JSON文本 + 返回: + str, 已调整val列表展示方式的JSON文本 + """ + pattern = re.compile(r'(^\s*"val"\s*:\s*)\[\s*\n([\s\S]*?)\n\s*\](,?)', re.MULTILINE) + + def _repl(match: re.Match) -> str: + prefix = match.group(1) + body = match.group(2) + suffix = match.group(3) + items = [] + for line in body.splitlines(): + value = line.strip().rstrip(',') + if value: + items.append(value) + inline = ', '.join(items) + return f"{prefix}[{inline}]{suffix}" + + return pattern.sub(_repl, json_text) + +def format_json_file( + input_path: Union[str, Path], + output_path: Union[str, Path] = None, + indent: int = 2, + ensure_ascii: bool = False +) -> str: + """ + 功能: + 读取原始JSON或JSPF文件并格式化, 其中val字段的坐标列表保持单行 + 参数: + input_path: str | Path, 输入文件路径 + output_path: str | Path | None, 输出文件路径, None时自动生成 *_formatted.json + indent: int, 缩进空格数 + ensure_ascii: bool, 是否转义非ASCII字符 + 返回: + str, 格式化后的JSON字符串 + """ + input_path = Path(input_path) + + if not input_path.exists(): + error_msg = f"输入文件不存在: {input_path}" + logger.error(error_msg) + raise FileNotFoundError(error_msg) + + try: + # 读取原始文件内容 + with open(input_path, 'r', encoding='utf-8') as file_obj: + data = json.load(file_obj) + + logger.info(f"成功读取文件: {input_path}") + + # 按缩进格式化JSON文本 + formatted_json = json.dumps( + data, + indent=indent, + ensure_ascii=ensure_ascii, + sort_keys=False + ) + + # 保持val列表为单行便于坐标阅读 + formatted_json = _inline_val_lists(formatted_json) + + if output_path is None: + output_path = input_path.parent / f"{input_path.stem}_formatted.json" + else: + output_path = Path(output_path) + + # 写回格式化结果 + with open(output_path, 'w', encoding='utf-8') as file_obj: + file_obj.write(formatted_json) + + logger.info(f"格式化完成, 已保存: {output_path}") + + return formatted_json + + except json.JSONDecodeError as exc: + error_msg = f"JSON解析失败: {exc}" + logger.error(error_msg) + raise ValueError(error_msg) + + except Exception as exc: + error_msg = f"处理文件时出现异常: {exc}" + logger.error(error_msg) + raise + +def format_json_string( + json_str: str, + indent: int = 2, + ensure_ascii: bool = False +) -> str: + """ + 功能: + 将压缩的JSON字符串转换为易读的格式化JSON字符串 + + 参数: + json_str: 输入的JSON字符串 + indent: 缩进空格数, 默认2 + ensure_ascii: 是否将非ASCII字符转义, 默认False(保留中文等字符) + + 返回: + str, 格式化后的JSON字符串 + """ + try: + data = json.loads(json_str) + formatted_json = json.dumps( + data, + indent=indent, + ensure_ascii=ensure_ascii, + sort_keys=False + ) + return formatted_json + + except json.JSONDecodeError as e: + error_msg = f"JSON解析失败: {e}" + logger.error(error_msg) + raise ValueError(error_msg) + +def format_all_jspf_in_directory( + directory: Union[str, Path] = None, + indent: int = 2, + ensure_ascii: bool = False +) -> dict: + """ + 功能: + 批量格式化指定目录下的所有.jspf文件, 保留原文件并另存为.json + 参数: + directory: 目标目录路径, 如果为None则使用默认的program_file目录 + indent: 缩进空格数, 默认2 + ensure_ascii: 是否将非ASCII字符转义, 默认False(保留中文等字符) + 返回: + Dict[str, str], 键为文件名, 值为处理状态("成功"或错误信息) + """ + # 如果未指定目录, 使用默认的program_file目录 + if directory is None: + current_file = Path(__file__) + project_root = current_file.parent.parent + directory = project_root / "program_file" + else: + directory = Path(directory) + + if not directory.exists(): + error_msg = f"目录不存在: {directory}" + logger.error(error_msg) + raise FileNotFoundError(error_msg) + + if not directory.is_dir(): + error_msg = f"路径不是目录: {directory}" + logger.error(error_msg) + raise NotADirectoryError(error_msg) + + # 查找所有.jspf文件 + jspf_files = list(directory.glob("*.jspf")) + + if not jspf_files: + logger.warning(f"在目录 {directory} 中未找到.jspf文件") + return {} + + logger.info(f"找到 {len(jspf_files)} 个.jspf文件") + + results = {} + + for jspf_file in jspf_files: + try: + # 生成与原文件同名的.json输出, 保留原始.jspf + output_path = jspf_file.with_suffix(".json") + + format_json_file( + input_path=jspf_file, + output_path=output_path, + indent=indent, + ensure_ascii=ensure_ascii + ) + + results[jspf_file.name] = "成功" + logger.info(f"✓ {jspf_file.name} 已格式化并另存为 {output_path.name}") + + except Exception as e: + error_msg = f"失败: {str(e)}" + results[jspf_file.name] = error_msg + logger.error(f"✗ {jspf_file.name}: {error_msg}") + + return results + + +if __name__ == "__main__": + # 配置日志 + logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + ) + + import sys + + # 默认处理program_file目录下的所有jspf文件 + if len(sys.argv) == 1: + print("=" * 60) + print("批量格式化 program_file 目录下的所有 .jspf 文件") + print("=" * 60) + + try: + results = format_all_jspf_in_directory() + + # 统计结果 + success_count = sum(1 for status in results.values() if status == "成功") + fail_count = len(results) - success_count + + print("\n" + "=" * 60) + print(f"处理完成! 成功: {success_count}, 失败: {fail_count}") + print("=" * 60) + + if fail_count > 0: + print("\n失败的文件:") + for filename, status in results.items(): + if status != "成功": + print(f" - {filename}: {status}") + + except Exception as e: + print(f"错误: {e}") + + # 处理单个文件 + elif len(sys.argv) >= 2: + input_file = sys.argv[1] + output_file = sys.argv[2] if len(sys.argv) > 2 else None + + try: + result = format_json_file(input_file, output_file) + print("格式化成功!") + print(f"预览前100个字符:\n{result[:100]}...") + except Exception as e: + print(f"错误: {e}") + + else: + print("用法:") + print(" 1. 批量处理program_file目录: python json_formatter.py") + print(" 2. 处理单个文件: python json_formatter.py <输入文件路径> [输出文件路径]") + print("\n示例:") + print(" python json_formatter.py") + print(" python json_formatter.py agv_get_bg4.jspf") diff --git a/unilabos/devices/eit_agv/utils/position_manager.py b/unilabos/devices/eit_agv/utils/position_manager.py new file mode 100644 index 00000000..7ec07093 --- /dev/null +++ b/unilabos/devices/eit_agv/utils/position_manager.py @@ -0,0 +1,756 @@ +# coding:utf-8 +""" +功能: + 机械臂坐标管理模块, 负责加载和管理YAML配置文件中的坐标数据 + 提供统一的坐标访问接口, 支持位姿、关节角度、轨迹等多种类型 +""" + +import yaml +from ruamel.yaml import YAML +import logging +from pathlib import Path +from typing import Dict, List, Optional, Union + +logger = logging.getLogger(__name__) + + +class PositionData: + """ + 功能: + 单个位置点的数据封装类 + """ + + def __init__(self, name: str, data: dict): + """ + 功能: + 初始化位置数据 + 参数: + name: 位置点名称 + data: 位置点配置字典 + """ + self.name = name + self.pose = data.get('pose') # 位姿[x,y,z,rx,ry,rz] + self.joints = data.get('joints') # 关节角度[j1,j2,j3,j4,j5,j6] + self.speed = data.get('speed', 0.1) # 速度百分比 + self.acceleration = data.get('acceleration', 0.1) # 加速度百分比 + self.blend_radius = data.get('blend_radius', 0) # 融合半径 + self.tool = data.get('tool', '') # 工具坐标系 + self.wobj = data.get('wobj', '') # 工件坐标系 + self.description = data.get('description', '') # 描述信息 + self.descend_z = data.get('descend_z') # 下探距离, 单位mm + self.lift_z = data.get('lift_z') # 提升距离, 单位mm + + def has_pose(self) -> bool: + """ + 功能: + 判断是否包含位姿数据 + 返回: + bool, True表示包含位姿数据 + """ + return self.pose is not None + + def has_joints(self) -> bool: + """ + 功能: + 判断是否包含关节角度数据 + 返回: + bool, True表示包含关节角度数据 + """ + return self.joints is not None + + def __repr__(self): + return f"PositionData(name={self.name}, pose={self.pose}, joints={self.joints})" + + +class GripperData: + """ + 功能: + 夹爪配置数据封装类 + """ + + def __init__(self, name: str, data: dict): + """ + 功能: + 初始化夹爪数据 + 参数: + name: 夹爪名称 + data: 夹爪配置字典 + """ + self.name = name + self.slot = data.get('slot', 1) # 快换料位编号(1-3, 对应bg1-bg3) + self.description = data.get('description', '') + + def __repr__(self): + return f"GripperData(name={self.name}, slot={self.slot})" + + +class MaterialData: + """ + 功能: + 物料类型配置数据封装类 + """ + + def __init__(self, name: str, data: dict): + """ + 功能: + 初始化物料数据 + 参数: + name: 物料类型名称 + data: 物料配置字典 + """ + self.name = name + self.gripper = data.get('gripper', '') # 使用的夹爪名称 + self.descend_z_offset = data.get('descend_z_offset', 0) # 下探距离偏移(mm) + self.lift_z_offset = data.get('lift_z_offset', 0) # 提升距离偏移(mm) + self.description = data.get('description', '') + + def __repr__(self): + return f"MaterialData(name={self.name}, gripper={self.gripper})" + + +class TrajectoryData: + """ + 功能: + 轨迹数据封装类, 包含多个路径点 + """ + + def __init__(self, name: str, data: dict): + """ + 功能: + 初始化轨迹数据 + 参数: + name: 轨迹名称 + data: 轨迹配置字典 + """ + self.name = name + self.description = data.get('description', '') + self.waypoints = [] + + # 解析路径点 + for wp_data in data.get('waypoints', []): + wp_name = wp_data.get('name', f'waypoint_{len(self.waypoints)}') + self.waypoints.append(PositionData(wp_name, wp_data)) + + def get_waypoint(self, index: int) -> Optional[PositionData]: + """ + 功能: + 获取指定索引的路径点 + 参数: + index: 路径点索引 + 返回: + PositionData对象, 如果索引越界则返回None + """ + if 0 <= index < len(self.waypoints): + return self.waypoints[index] + return None + + def get_waypoint_by_name(self, name: str) -> Optional[PositionData]: + """ + 功能: + 根据名称获取路径点 + 参数: + name: 路径点名称 + 返回: + PositionData对象, 如果未找到则返回None + """ + for wp in self.waypoints: + if wp.name == name: + return wp + return None + + def __len__(self): + return len(self.waypoints) + + def __repr__(self): + return f"TrajectoryData(name={self.name}, waypoints={len(self.waypoints)})" + + +class ToolData: + """ + 功能: + 工具坐标系数据封装类 + """ + + def __init__(self, name: str, data: dict): + """ + 功能: + 初始化工具数据 + 参数: + name: 工具名称 + data: 工具配置字典 + """ + self.name = name + self.tcp_offset = data.get('tcp_offset', [0, 0, 0, 0, 0, 0]) + self.payload = data.get('payload', [0, 0, 0, 0]) + self.inertia = data.get('inertia', [0, 0, 0, 0, 0, 0]) + self.description = data.get('description', '') + + def __repr__(self): + return f"ToolData(name={self.name}, tcp_offset={self.tcp_offset})" + + +class WorkObjectData: + """ + 功能: + 工件坐标系数据封装类 + """ + + def __init__(self, name: str, data: dict): + """ + 功能: + 初始化工件坐标系数据 + 参数: + name: 工件坐标系名称 + data: 工件坐标系配置字典 + """ + self.name = name + self.offset = data.get('offset', [0, 0, 0, 0, 0, 0]) + self.description = data.get('description', '') + + def __repr__(self): + return f"WorkObjectData(name={self.name}, offset={self.offset})" + + +class PositionManager: + """ + 功能: + 机械臂坐标管理器, 负责加载和管理所有坐标配置 + """ + + def __init__(self, config_file: Optional[str] = None): + """ + 功能: + 初始化坐标管理器 + 参数: + config_file: YAML配置文件路径, 为None时使用默认路径 + """ + if config_file is None: + # 默认配置文件路径 + current_dir = Path(__file__).parent.parent + config_file = current_dir / 'config' / 'arm_positions.yaml' + + self.config_file = Path(config_file) + self.positions = {} # 所有位置点, 按类别组织 + self.trajectories = {} # 所有轨迹 + self.tools = {} # 所有工具坐标系 + self.workobjects = {} # 所有工件坐标系 + self.global_params = {} # 全局参数 + self.grippers = {} # 夹爪配置 + self.gripper_storage = {} # 夹爪存放位置 + self.materials = {} # 物料类型配置 + + self._load_config() + + def _load_config(self): + """ + 功能: + 从YAML文件加载配置 + """ + try: + with open(self.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is None: + logger.warning(f"配置文件为空: {self.config_file}") + return + + # 加载位置点(除了特殊类别) + special_categories = ['trajectories', 'tools', 'workobjects', 'global_params', + 'grippers', 'gripper_storage', 'materials', 'station_calibration'] + for category, positions in config.items(): + if category in special_categories: + continue + + if not isinstance(positions, dict): + continue + + self.positions[category] = {} + for pos_name, pos_data in positions.items(): + self.positions[category][pos_name] = PositionData(pos_name, pos_data) + + # 加载轨迹 + if 'trajectories' in config: + for traj_name, traj_data in config['trajectories'].items(): + self.trajectories[traj_name] = TrajectoryData(traj_name, traj_data) + + # 加载工具坐标系 + if 'tools' in config: + for tool_name, tool_data in config['tools'].items(): + self.tools[tool_name] = ToolData(tool_name, tool_data) + + # 加载工件坐标系 + if 'workobjects' in config: + for wobj_name, wobj_data in config['workobjects'].items(): + self.workobjects[wobj_name] = WorkObjectData(wobj_name, wobj_data) + + # 加载夹爪配置 + if 'grippers' in config: + for gripper_name, gripper_data in config['grippers'].items(): + self.grippers[gripper_name] = GripperData(gripper_name, gripper_data) + + # 加载夹爪存放位置 + if 'gripper_storage' in config: + for name, data in config['gripper_storage'].items(): + self.gripper_storage[name] = PositionData(name, data) + + # 加载物料类型配置 + if 'materials' in config: + for material_name, material_data in config['materials'].items(): + self.materials[material_name] = MaterialData(material_name, material_data) + + # 加载全局参数 + self.global_params = config.get('global_params', {}) + + logger.info(f"成功加载坐标配置: {self.config_file}") + logger.debug(f"位置类别数: {len(self.positions)}, 轨迹数: {len(self.trajectories)}, " + f"工具数: {len(self.tools)}, 工件坐标系数: {len(self.workobjects)}, " + f"夹爪数: {len(self.grippers)}, 物料类型数: {len(self.materials)}") + + except FileNotFoundError: + logger.error(f"配置文件不存在: {self.config_file}") + raise + except yaml.YAMLError as e: + logger.error(f"YAML解析错误: {e}") + raise + except Exception as e: + logger.error(f"加载配置文件失败: {e}") + raise + + def reload(self): + """ + 功能: + 重新加载配置文件 + """ + logger.info("重新加载坐标配置") + self.positions.clear() + self.trajectories.clear() + self.tools.clear() + self.workobjects.clear() + self.global_params.clear() + self.grippers.clear() + self.gripper_storage.clear() + self.materials.clear() + self._load_config() + + def get_category(self, category: str) -> Optional[Dict]: + """ + 功能: + 获取指定类别的所有位置点配置 + 参数: + category: 位置类别, 如'tray_position', 'safe_positions'等 + 返回: + Dict, 包含该类别下所有位置点的字典, 如果类别不存在则返回None + """ + if category not in self.positions: + logger.warning(f"位置类别不存在: {category}") + return None + + # 返回原始配置数据字典 + result = {} + for pos_name, pos_data in self.positions[category].items(): + result[pos_name] = { + 'pose': pos_data.pose, + 'joints': pos_data.joints, + 'speed': pos_data.speed, + 'acceleration': pos_data.acceleration, + 'descend_z': pos_data.descend_z, + 'lift_z': pos_data.lift_z, + 'description': pos_data.description, + 'blend_radius': pos_data.blend_radius, + 'tool': pos_data.tool, + 'wobj': pos_data.wobj + } + return result + + def get_position(self, category: str, name: str) -> Optional[PositionData]: + """ + 功能: + 获取指定类别和名称的位置点 + 参数: + category: 位置类别, 如'tray_pickup', 'material_place'等 + name: 位置名称, 如'tray_1', 'workbench_1'等 + 返回: + PositionData对象, 如果未找到则返回None + """ + if category not in self.positions: + logger.warning(f"位置类别不存在: {category}") + return None + + if name not in self.positions[category]: + logger.warning(f"位置点不存在: {category}.{name}") + return None + + return self.positions[category][name] + + def get_trajectory(self, name: str) -> Optional[TrajectoryData]: + """ + 功能: + 获取指定名称的轨迹 + 参数: + name: 轨迹名称 + 返回: + TrajectoryData对象, 如果未找到则返回None + """ + if name not in self.trajectories: + logger.warning(f"轨迹不存在: {name}") + return None + + return self.trajectories[name] + + def get_tool(self, name: str) -> Optional[ToolData]: + """ + 功能: + 获取指定名称的工具坐标系 + 参数: + name: 工具名称 + 返回: + ToolData对象, 如果未找到则返回None + """ + if name not in self.tools: + logger.warning(f"工具坐标系不存在: {name}") + return None + + return self.tools[name] + + def get_workobject(self, name: str) -> Optional[WorkObjectData]: + """ + 功能: + 获取指定名称的工件坐标系 + 参数: + name: 工件坐标系名称 + 返回: + WorkObjectData对象, 如果未找到则返回None + """ + if name not in self.workobjects: + logger.warning(f"工件坐标系不存在: {name}") + return None + + return self.workobjects[name] + + def list_categories(self) -> List[str]: + """ + 功能: + 列出所有位置类别 + 返回: + 位置类别名称列表 + """ + return list(self.positions.keys()) + + def list_positions(self, category: str) -> List[str]: + """ + 功能: + 列出指定类别下的所有位置点名称 + 参数: + category: 位置类别 + 返回: + 位置点名称列表 + """ + if category not in self.positions: + return [] + return list(self.positions[category].keys()) + + def list_trajectories(self) -> List[str]: + """ + 功能: + 列出所有轨迹名称 + 返回: + 轨迹名称列表 + """ + return list(self.trajectories.keys()) + + def list_tools(self) -> List[str]: + """ + 功能: + 列出所有工具坐标系名称 + 返回: + 工具坐标系名称列表 + """ + return list(self.tools.keys()) + + def list_workobjects(self) -> List[str]: + """ + 功能: + 列出所有工件坐标系名称 + 返回: + 工件坐标系名称列表 + """ + return list(self.workobjects.keys()) + + def get_gripper(self, name: str) -> Optional[GripperData]: + """ + 功能: + 获取指定名称的夹爪配置 + 参数: + name: 夹爪名称 + 返回: + GripperData对象, 如果未找到则返回None + """ + if name not in self.grippers: + logger.warning(f"夹爪配置不存在: {name}") + return None + return self.grippers[name] + + def get_gripper_storage_position(self, gripper_name: str) -> Optional[PositionData]: + """ + 功能: + 获取夹爪存放位置 + 参数: + gripper_name: 夹爪名称 + 返回: + PositionData对象, 如果未找到则返回None + """ + if gripper_name not in self.gripper_storage: + logger.warning(f"夹爪存放位置不存在: {gripper_name}") + return None + return self.gripper_storage[gripper_name] + + def get_material(self, name: str) -> Optional[MaterialData]: + """ + 功能: + 获取指定名称的物料类型配置 + 参数: + name: 物料类型名称 + 返回: + MaterialData对象, 如果未找到则返回None + """ + if name not in self.materials: + logger.warning(f"物料类型配置不存在: {name}") + return None + return self.materials[name] + + def list_grippers(self) -> List[str]: + """ + 功能: + 列出所有夹爪名称 + 返回: + 夹爪名称列表 + """ + return list(self.grippers.keys()) + + def list_materials(self) -> List[str]: + """ + 功能: + 列出所有物料类型名称 + 返回: + 物料类型名称列表 + """ + return list(self.materials.keys()) + + def get_global_param(self, key: str, default=None): + """ + 功能: + 获取全局参数 + 参数: + key: 参数名称 + default: 默认值 + 返回: + 参数值, 如果不存在则返回默认值 + """ + return self.global_params.get(key, default) + + def save_calibration_offset(self, station_name: str, offset: dict): + """ + 功能: + 保存工站校准偏移值到配置文件, 保留原有注释和格式 + 参数: + station_name: 工站名称, 如"shelf", "synthesis", "analysis" + offset: 偏移值字典, 包含x, y, z, dx, dy, dz + """ + try: + # 使用ruamel.yaml保留注释和格式 + yaml_handler = YAML() + yaml_handler.preserve_quotes = True + yaml_handler.default_flow_style = False + yaml_handler.width = 4096 # 设置足够大的行宽,避免自动换行 + + # 读取当前配置文件 + with open(self.config_file, 'r', encoding='utf-8') as f: + config = yaml_handler.load(f) + + if config is None: + config = {} + + # 确保存在station_calibration节点 + if 'station_calibration' not in config: + config['station_calibration'] = {} + + # 更新或创建该工站的校准偏移值 + if station_name not in config['station_calibration']: + config['station_calibration'][station_name] = {} + + # 只更新偏移值字段, 保留其他可能存在的字段 + config['station_calibration'][station_name]['x'] = offset['x'] + config['station_calibration'][station_name]['y'] = offset['y'] + config['station_calibration'][station_name]['z'] = offset['z'] + config['station_calibration'][station_name]['dx'] = offset['dx'] + config['station_calibration'][station_name]['dy'] = offset['dy'] + config['station_calibration'][station_name]['dz'] = offset['dz'] + + # 如果没有description字段, 则添加 + if 'description' not in config['station_calibration'][station_name]: + config['station_calibration'][station_name]['description'] = f"{station_name}工站校准偏移值" + + # 写回配置文件, 保留注释和格式 + with open(self.config_file, 'w', encoding='utf-8') as f: + yaml_handler.dump(config, f) + + logger.debug(f"成功保存工站 {station_name} 的校准偏移值到配置文件") + + except Exception as e: + logger.error(f"保存校准偏移值失败: {e}") + raise + + def get_calibration_offset(self, station_name: str) -> Optional[dict]: + """ + 功能: + 获取工站校准偏移值 + 参数: + station_name: 工站名称, 如"shelf", "synthesis", "analysis" + 返回: + dict或None, 包含x, y, z, dx, dy, dz的偏移值字典, 如果不存在则返回None + """ + try: + # 读取配置文件 + with open(self.config_file, 'r', encoding='utf-8') as f: + config = yaml.safe_load(f) + + if config is None or 'station_calibration' not in config: + logger.warning(f"配置文件中不存在station_calibration节点") + return None + + if station_name not in config['station_calibration']: + logger.warning(f"工站 {station_name} 的校准偏移值不存在") + return None + + offset = config['station_calibration'][station_name] + return { + 'x': offset.get('x', 0.0), + 'y': offset.get('y', 0.0), + 'z': offset.get('z', 0.0), + 'dx': offset.get('dx', 0.0), + 'dy': offset.get('dy', 0.0), + 'dz': offset.get('dz', 0.0) + } + + except Exception as e: + logger.error(f"读取校准偏移值失败: {e}") + return None + + def save_tray_position(self, tray_name: str, pose: list, description: str = None): + """ + 功能: + 保存托盘位置的TCP位姿到配置文件, 使用多行带注释格式 + 参数: + tray_name: 托盘位置名称, 如"agv_tray_1", "shelf_tray_1-1" + pose: TCP位姿列表[x, y, z, rx, ry, rz], 位置单位mm, 姿态单位rad + description: 位置描述, 为None时保留原有描述 + """ + try: + from ruamel.yaml.comments import CommentedSeq, CommentedMap + from ruamel.yaml.tokens import CommentToken + from ruamel.yaml.error import CommentMark + + # 使用ruamel.yaml保留注释和格式 + yaml_handler = YAML() + yaml_handler.preserve_quotes = True + yaml_handler.default_flow_style = False + yaml_handler.width = 4096 # 设置足够大的行宽, 避免自动换行 + + # 读取当前配置文件 + with open(self.config_file, 'r', encoding='utf-8') as f: + config = yaml_handler.load(f) + + if config is None: + config = {} + + # 确保存在tray_position节点 + if 'tray_position' not in config: + config['tray_position'] = {} + + # 创建带注释的pose序列, 使用纯Python列表 + pose_list = [float(v) for v in pose] + pose_seq = CommentedSeq(pose_list) + pose_seq.fa.set_flow_style() + pose_seq.fa.set_block_style() + # 使用安全的方式添加行尾注释 + pose_comments = ['x, 单位mm', 'y, 单位mm', 'z, 单位mm', 'rx, 单位rad', 'ry, 单位rad', 'rz, 单位rad'] + for i, comment in enumerate(pose_comments): + # 直接设置注释, 避免yaml_add_eol_comment的迭代问题 + pose_seq.ca.items[i] = [None, None, CommentToken(f' # {comment}\n', CommentMark(0), None), None] + + # 检查托盘位置是否存在 + if tray_name not in config['tray_position']: + # 新建托盘位置配置 + tray_config = CommentedMap() + tray_config['pose'] = pose_seq + tray_config['descend_z'] = -32 + tray_config['lift_z'] = 20 + tray_config['speed'] = 0.6 + tray_config['acceleration'] = 0.3 + tray_config['description'] = description if description else f"{tray_name}抓取/放置位置" + # 添加行尾注释 + tray_config.ca.items['pose'] = [None, None, CommentToken(' # 抓取点位姿, 位置单位mm, 姿态单位rad\n', CommentMark(0), None), None] + tray_config.ca.items['descend_z'] = [None, None, CommentToken(' # 过渡点到抓取点的下探距离, 沿工具坐标系Z轴, 负值表示下降, 单位mm\n', CommentMark(0), None), None] + tray_config.ca.items['lift_z'] = [None, None, CommentToken(' # 提升距离, 沿工具坐标系Z轴, 正值表示上升, 单位mm\n', CommentMark(0), None), None] + tray_config.ca.items['speed'] = [None, None, CommentToken(' # 运动速度百分比 (0-1)\n', CommentMark(0), None), None] + tray_config.ca.items['acceleration'] = [None, None, CommentToken(' # 加速度百分比 (0-1)\n', CommentMark(0), None), None] + config['tray_position'][tray_name] = tray_config + else: + # 更新现有托盘位置的pose + tray_config = config['tray_position'][tray_name] + tray_config['pose'] = pose_seq + # 更新pose键的注释 + tray_config.ca.items['pose'] = [None, None, CommentToken(' # 抓取点位姿, 位置单位mm, 姿态单位rad\n', CommentMark(0), None), None] + # 如果提供了新描述, 则更新 + if description is not None: + tray_config['description'] = description + + # 写回配置文件, 保留注释和格式 + with open(self.config_file, 'w', encoding='utf-8') as f: + yaml_handler.dump(config, f) + + logger.info(f"成功保存托盘位置 {tray_name} 的TCP位姿到配置文件") + + # 重新加载配置以更新内存中的数据 + self.reload() + + except Exception as e: + logger.error(f"保存托盘位置失败: {e}") + raise + + def print_summary(self): + """ + 功能: + 打印配置摘要信息 + """ + print(f"\n{'='*60}") + print(f"机械臂坐标配置摘要") + print(f"{'='*60}") + print(f"配置文件: {self.config_file}") + print(f"\n位置类别:") + for category in self.list_categories(): + positions = self.list_positions(category) + print(f" - {category}: {len(positions)}个位置点") + for pos_name in positions: + pos = self.get_position(category, pos_name) + print(f" * {pos_name}: {pos.description}") + + print(f"\n轨迹:") + for traj_name in self.list_trajectories(): + traj = self.get_trajectory(traj_name) + print(f" - {traj_name}: {len(traj)}个路径点 - {traj.description}") + + print(f"\n工具坐标系:") + for tool_name in self.list_tools(): + tool = self.get_tool(tool_name) + print(f" - {tool_name}: {tool.description}") + + print(f"\n工件坐标系:") + for wobj_name in self.list_workobjects(): + wobj = self.get_workobject(wobj_name) + print(f" - {wobj_name}: {wobj.description}") + + print(f"\n全局参数:") + for key, value in self.global_params.items(): + print(f" - {key}: {value}") + print(f"{'='*60}\n") diff --git a/unilabos/devices/eit_analysis_station/__init__.py b/unilabos/devices/eit_analysis_station/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_analysis_station/config/__init__.py b/unilabos/devices/eit_analysis_station/config/__init__.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/unilabos/devices/eit_analysis_station/config/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/unilabos/devices/eit_analysis_station/config/setting.py b/unilabos/devices/eit_analysis_station/config/setting.py new file mode 100644 index 00000000..ac1508c2 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/config/setting.py @@ -0,0 +1,483 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 分析站配置模块, 统一管理仪器连接参数, 数据目录和积分参数. +参数: + 无. +返回: + Settings 实例. +""" + +import logging +import os +from dataclasses import dataclass, field +from pathlib import Path +from typing import Optional + + +@dataclass +class Settings: + """ + 功能: + 存储分析站运行所需的全部配置项. + 参数: + 无. + 返回: + Settings. + """ + + # ---------- GC_MS 设备 ---------- + gc_ms_host: str = "10.40.6.101" # GC-MS 控制端地址, 修改后切换仪器目标主机. + gc_ms_port: int = 5792 # GC-MS 控制端端口, 修改后切换连接端口. + gc_ms_timeout: float = 10.0 # GC-MS 通信超时秒数, 调大可降低慢响应误判. + + # ---------- UPLC_QTOF 设备 ---------- + uplc_qtof_host: str = "10.40.8.69" # UPLC_QTOF 控制端地址, 修改后流程切换主机. + uplc_qtof_port: int = 5792 # UPLC_QTOF 控制端端口, 修改后流程切换端口. + uplc_qtof_timeout: float = 10.0 # UPLC_QTOF 通信超时秒数, 调大可降低超时告警. + uplc_qtof_append_wash_stop: bool = False # 是否追加 Wash stop 方法, 打开后序列会追加停机步骤. + + # ---------- HPLC 设备(预留) ---------- + hplc_host: str = "192.168.3.186" # HPLC 控制端地址, 预留流程切换主机. + hplc_port: int = 5792 # HPLC 控制端端口, 预留流程切换端口. + hplc_timeout: float = 10.0 # HPLC 通信超时秒数, 调大可降低超时告警. + + # ---------- 仪器侧数据目录 ---------- + gc_ms_data_dir: Path = field(default_factory=lambda: Path(r"\\10.37.2.2\Autolab_Database\Auto_GC_MS\data")) # GC-MS 仪器导出目录, 修改后切换采集源路径. + uplc_qtof_data_dir: Path = field(default_factory=lambda: Path("Z:/Auto_UPLC_QTOF/data")) # UPLC_QTOF 仪器导出目录, 修改后切换采集源路径. + hplc_data_dir: Path = field(default_factory=lambda: Path("Z:/Auto_HPLC/data")) # HPLC 仪器导出目录, 修改后切换采集源路径. + + # ---------- 合成任务目录 ---------- + synthesis_tasks_dir: Path = field( # 合成站任务目录, 修改后会改变任务同步目标. + default_factory=lambda: Path(__file__).parent.parent.parent + / "eit_synthesis_station" + / "data" + / "tasks" + ) + + # ---------- 分析站本地数据目录 ---------- + data_dir: Path = field(default_factory=lambda: Path(__file__).parent.parent / "data") # 分析站本地数据根目录, 修改后影响本地缓存与中间产物位置. + + # ---------- 日志 ---------- + log_level: str = "INFO" # 日志等级, 调为 DEBUG 可输出更详细排障信息. + + # ---------- NIST 配置 ---------- + nist_path: Path = field(default_factory=lambda: Path(r"D:\NIST23\MSSEARCH")) # NIST 安装目录, 修改后切换检索程序与库路径基准. + nist_max_hits: int = 3 # NIST 匹配化合物数量上限, 同时控制搜索命中数, 内存保留数和报表输出列数. + nist_search_timeout: float = 120.0 # NIST 单峰搜索超时秒数, 调大可降低超时中断. + nist_avg_scans: int = 3 # 质谱平均扫描数, 调大可提升信噪比但会平滑细节. + pim_enable: bool = True # 是否启用 PIM 预测, 关闭后不输出 PIM 列结果. + pim_ab_m: float = 1.5 # PIM 参数 ab_m, 调整后影响分子离子峰判定敏感度. 推荐范围:0.5 ~ 2.0. + pim_beta: float = 5.0 # PIM 参数 beta, 调整后影响高质量峰加权强度. 推荐范围:3.0 ~ 7.0 + pim_epsilon_f: float = 0.0 # PIM 参数 epsilon_f, 调整后影响峰筛选阈值. 可以尝试设为 0.01. + + # ---------- MSPepSearch 预测配置 ---------- + mspepsearch_enable: bool = True # 是否启用 MSPepSearch 预测链路, 关闭后不执行 SS-HM/iHS-HM. + process_gc_ms_enable_sshm_search: bool = True # 是否启用 SS-HM 预测, 关闭后报告不写 SS-HM 结果. + process_gc_ms_enable_ihshm_search: bool = False # 是否启用 iHS-HM 预测, 打开后增加 iHS-HM 计算耗时. + mspepsearch_exe: Path = field( # MSPepSearch 可执行文件路径, 修改后切换调用程序. + default_factory=lambda: Path( + r"D:\EIMS-mass-predictions\R_ShinyApplication\shiny\wrk" + r"\MSPepSearch\2017_05_15_MSPepSearch\x64\MSPepSearch64.exe" + ) + ) + mspepsearch_lib_path: Path = field( # MSPepSearch 库目录, 修改后切换检索库来源. + default_factory=lambda: Path(r"D:\NIST23\MSSEARCH\mainlib") + ) + mspepsearch_lib_type: str = "MAIN" # MSPepSearch 库类型, 改为 REPL/LIB 会改变命中空间. + nist_mainlib_msp: Path = field( # mainlib 导出 MSP 路径, 修改后影响离线库解析来源. + default_factory=lambda: Path(r"D:\NIST23\MSSEARCH\mainlib_export.msp") + ) + nist_structure_seed_msp: Path = field( # 结构映射 seed MSP 路径, 修改后影响 NIST# 到 CAS 映射. + default_factory=lambda: Path(r"D:\NIST23\MSSEARCH\mainlib_export.msp") + ) + nist_structure_seed_mol_dir: Path = field( # 结构映射 seed MOL 目录, 修改后影响本地结构图渲染命中率. + default_factory=lambda: Path(r"D:\NIST23\MSSEARCH\mainlib_export.MOL") + ) + nist_structure_runtime_cache_path: Path = field( # 运行时结构映射缓存路径, 修改后影响映射复用位置. + default_factory=lambda: Path(__file__).parent.parent / "data" / "nist_runtime_map.pkl" + ) + structure_offline_only: bool = False # 是否严格离线结构模式, 打开后禁用 PubChem 网络回退. + sshm_hits: int = 25 # SS-HM 搜索返回命中数, 调大可增加候选覆盖. 推荐范围50-100. + sshm_b_ss: int = 75 # SS-HM 概率加权参数 B_SS, 调整后影响置信度分布. 推荐范围50-100. + ihshm_hits: int = 25 # iHS-HM 搜索返回命中数, 调大可增加候选覆盖. 推荐范围25-50. + ihshm_mEMF: int = 700 # iHS-HM 最小匹配因子阈值, 调高会更严格过滤低质量命中. 推荐范围600 ~ 750. + mspepsearch_timeout: float = 120.0 # MSPepSearch 超时秒数, 调大可降低复杂谱图超时失败. + + # ---------- 峰检测与积分参数 ---------- + integration_mode: str = "robust_v3" # 处理模式, 可选 robust_v3 / legacy / gcpy, 切换后改变峰检测与边界算法路径. + peak_smoothing_window: int = 11 # TIC 平滑窗口点数, 调大可抑制噪声但可能吞并窄峰. + peak_prominence: float = 20000.0 # TIC 最小峰显著性阈值, 小幅调高以抑制平基线弱假峰. + peak_min_distance: int = 3 # TIC 相邻峰最小点距, 调大可减少近邻峰分裂. + peak_width_rel_height: float = 0.99 # 峰宽计算相对高度, 调整后影响峰边界与面积. + fid_peak_prominence: float = 0.5 # FID 最小峰显著性阈值, 调高会减少弱峰识别. + fid_peak_min_distance: int = 50 # FID 相邻峰最小点距, 调大可减少近邻峰分裂. + + # ---------- legacy 参数 ---------- + use_als_baseline: bool = True # 是否使用 ALS 基线, 关闭后使用替代基线策略. + als_lambda: float = 1e7 # ALS 平滑参数 lambda, 调大可使基线更平滑. + als_p: float = 0.01 # ALS 非对称参数 p, 调整后影响正负残差惩罚. + use_valley_boundary: bool = False # 是否使用谷底边界法, 打开后边界更贴近局部谷底. + + # ---------- robust_v3 参数 ---------- + baseline_method: str = "rolling_quantile" # robust_v3 基线方法, 修改后改变背景估计方式. + baseline_quantile: float = 20.0 # rolling quantile 分位数, 调低会提升基线灵敏度. + baseline_window_min: float = 0.9 # 基线窗口宽度(min), 调大可提升基线平稳性. + boundary_sigma_factor: float = 3.0 # 边界 sigma 系数, 调大通常会扩展积分边界. + boundary_edge_ratio: float = 0.005 # 边缘阈值比例, 调整后影响峰起止截断位置. + boundary_expand_factor: float = 6.0 # 边界扩展系数, 调大可覆盖更多拖尾区域. + boundary_min_span_min: float = 0.08 # 边界搜索最小跨度(min), 调大可避免边界收缩过窄. + boundary_max_span_min: float = 2.00 # 边界搜索最大跨度(min), 调小可限制异常拖尾扩展. + robust_v3_shoulder_filter_enable: bool = True # robust_v3 是否启用肩峰过滤, 关闭后行为退化为 robust_v2. + robust_v3_shoulder_width_max_min: float = 0.035 # 肩峰半高宽上限(min), 调大将更严格过滤窄肩峰. + robust_v3_shoulder_gap_max_min: float = 0.09 # 肩峰与强邻峰的最大间隔(min), 调大将扩大肩峰判定范围. + robust_v3_shoulder_relative_prominence_max: float = 0.15 # 肩峰相对显著性上限, 调大将过滤更显著的弱邻峰. + robust_v3_tail_artifact_filter_enable: bool = True # robust_v3 是否启用拖尾假峰过滤, 关闭后仅保留肩峰过滤. + robust_v3_tail_artifact_gap_max_min: float = 0.20 # 拖尾假峰与前峰最大间隔(min), 调大将扩大拖尾合并范围. + robust_v3_tail_artifact_relative_prominence_max: float = 0.15 # 拖尾假峰相对显著性上限, 调大将合并更强的尾部小峰. + robust_v3_tail_artifact_half_width_asymmetry_min: float = 2.0 # 拖尾假峰右/左半高宽不对称下限, 调大将减少误合并. + robust_v3_tail_monotonic_filter_enable: bool = True # robust_v3 是否启用平滑信号单调下降拖尾过滤, 关闭后仅使用三条件拖尾过滤. + robust_v3_tail_monotonic_ratio_max: float = 0.25 # 单调下降拖尾判定时上升步占比上限, 调大会放松判定. + robust_v3_max_peak_width_min: float = 1.0 # robust_v3 峰最大边界宽度(min), 配合自适应逻辑放宽基准值. 超过自适应上限视为基线抬升假峰. 设0关闭. + robust_v3_leading_edge_filter_enable: bool = True # robust_v3 是否启用前沿假峰过滤, 检测强峰上升沿上的假峰并丢弃. + robust_v3_leading_edge_relative_prominence_max: float = 0.25 # 前沿假峰相对后峰显著性上限, 调大将丢弃更强的前沿假峰. + robust_v3_leading_edge_monotonic_ratio_min: float = 0.65 # 前沿假峰判定时上升步占比下限, 调低会放松判定. + + # ---------- CWT 峰检测参数 ---------- + use_cwt_detection: bool = True # 是否使用 CWT 多尺度峰检测替代 find_peaks, 关闭后回退到传统 find_peaks. + cwt_min_width_min: float = 0.01 # CWT 最小小波宽度(min), 控制能检测的最窄峰宽度. 调小可分辨更近的双峰. + cwt_max_width_min: float = 0.40 # CWT 最大小波宽度(min), 控制能检测的最宽峰宽度. 调大可覆盖更宽的峰. + cwt_min_snr: float = 2.0 # CWT 脊线最小信噪比, 调高减少噪声假峰. + cwt_noise_perc: float = 10.0 # CWT 噪声估计分位数, 调低使噪声估计更保守. + + # ---------- gcpy 参数 ---------- + gcpy_whittaker_lmbd: float = 10.0 # gcpy 模式 Whittaker 平滑参数, 调大可使信号更平滑. + + # ---------- 峰过滤参数 ---------- + peak_rt_min: Optional[float] = 4.0 # 峰保留时间下限(min), 调大可忽略前段溶剂峰. + peak_rt_max: Optional[float] = 15.0 # 峰保留时间上限(min), 调小可限制后段噪声峰. + tic_area_min: Optional[float] = 10000.0 # TIC 峰面积下限, 调大可过滤小面积峰. + tic_area_max: Optional[float] = None # TIC 峰面积上限, 设置后可过滤过载峰. + fid_area_min: Optional[float] = 0.01 # FID 峰面积下限, 调大可过滤微小峰. + fid_area_max: Optional[float] = None # FID 峰面积上限, 设置后可过滤异常大峰. + + # ---------- TIC-FID 峰对齐参数 ---------- + alignment_tolerance: float = 0.2 # FID 与 TIC 峰保留时间对齐容差(min), 调大可提高配对成功率. + alignment_include_tic_only: bool = False # 是否输出仅 TIC 有峰行, 打开后对照表会增加 TIC-only 记录. + alignment_include_fid_only: bool = True # 是否输出仅 FID 有峰行, 关闭后对照表会隐藏 FID-only 记录. + + # ---------- 产率计算参数 ---------- + yield_rt_tolerance: float = 0.1 # 产率计算 RT 匹配容差(min), 调大可放宽目标峰匹配. + + # ---------- TIC 绘图参数 ---------- + tic_plot_show_compound: bool = True # TIC 色谱图是否标注化合物名称, 关闭后图面更简洁. + + # ---------- 图片导出参数 ---------- + chromatogram_plot_ppi: int = 300 # 色谱图导出 PPI, 调高可提升 TIC/FID 图清晰度并增大文件体积. + ms_spectrum_plot_ppi: int = 300 # 质谱图导出 PPI, 调高可提升棒图与标签清晰度并增大文件体积. + structure_image_ppi: int = 300 # 结构图导出 PPI, 调高可提升本地 MOL 与 PubChem SDF 渲染结构图清晰度并增大文件体积. + structure_image_size: int = 500 # 结构图渲染像素边长, 调大可提升清晰度并增大文件体积. + + # ---------- 报告目录 ---------- + report_dir: Path = field(default_factory=lambda: Path(__file__).parent.parent / "data") # 报告输出根目录, 修改后改变报告与图像落盘位置. + + # ---------- 实验归档目录 ---------- + archive_dir: Path = field( + default_factory=lambda: Path(r"\\10.37.2.2\Autolab_Database\experiment_records") + ) # 实验归档输出根目录, 修改后改变归档数据落盘位置. + archive_copy_raw_data: bool = True # 归档时是否复制 .D 原始数据目录, 打开后归档体积会显著增大. + + # ---------- 化学品库目录 ---------- + chemical_list_path: Path = field( # 化学品清单路径, 修改后切换产率计算的物性来源. + default_factory=lambda: Path(__file__).parent.parent.parent + / "eit_synthesis_station" + / "sheet" + / "chemical_list.xlsx" + ) + + # ---------- 结构图缓存目录 ---------- + structure_cache_dir: Path = field( # 全局结构图缓存目录, 修改后影响跨任务复用缓存位置. + default_factory=lambda: Path(__file__).parent.parent / "data" / "structure_cache" + ) + + @staticmethod + def from_env() -> "Settings": + """ + 功能: + 从环境变量读取配置, 未配置时使用默认值. + 参数: + 无. + 返回: + Settings. + 环境变量: + ANALYSIS_GC_MS_HOST, ANALYSIS_GC_MS_PORT, ANALYSIS_GC_MS_TIMEOUT, + ANALYSIS_UPLC_QTOF_HOST, ANALYSIS_UPLC_QTOF_PORT, ANALYSIS_UPLC_QTOF_TIMEOUT, + ANALYSIS_UPLC_QTOF_APPEND_WASH_STOP, + ANALYSIS_HPLC_HOST, ANALYSIS_HPLC_PORT, ANALYSIS_HPLC_TIMEOUT, + ANALYSIS_GC_MS_DATA_DIR, ANALYSIS_UPLC_QTOF_DATA_DIR, ANALYSIS_HPLC_DATA_DIR, + ANALYSIS_SYNTHESIS_TASKS_DIR, ANALYSIS_DATA_DIR, ANALYSIS_LOG_LEVEL, + ANALYSIS_PEAK_SMOOTHING_WINDOW, ANALYSIS_PEAK_PROMINENCE, + ANALYSIS_PEAK_MIN_DISTANCE, ANALYSIS_PEAK_WIDTH_REL_HEIGHT, + ANALYSIS_FID_PEAK_PROMINENCE, ANALYSIS_FID_PEAK_MIN_DISTANCE, + ANALYSIS_USE_ALS_BASELINE, ANALYSIS_ALS_LAMBDA, ANALYSIS_ALS_P, + ANALYSIS_USE_VALLEY_BOUNDARY, + ANALYSIS_INTEGRATION_MODE, ANALYSIS_BASELINE_METHOD, + ANALYSIS_BASELINE_QUANTILE, ANALYSIS_BASELINE_WINDOW_MIN, + ANALYSIS_BOUNDARY_SIGMA_FACTOR, ANALYSIS_BOUNDARY_EDGE_RATIO, + ANALYSIS_BOUNDARY_EXPAND_FACTOR, ANALYSIS_BOUNDARY_MIN_SPAN_MIN, + ANALYSIS_BOUNDARY_MAX_SPAN_MIN, + ANALYSIS_ROBUST_V3_SHOULDER_FILTER_ENABLE, + ANALYSIS_ROBUST_V3_SHOULDER_WIDTH_MAX_MIN, + ANALYSIS_ROBUST_V3_SHOULDER_GAP_MAX_MIN, + ANALYSIS_ROBUST_V3_SHOULDER_RELATIVE_PROMINENCE_MAX, + ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_FILTER_ENABLE, + ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_GAP_MAX_MIN, + ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_RELATIVE_PROMINENCE_MAX, + ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_HALF_WIDTH_ASYMMETRY_MIN, + ANALYSIS_ROBUST_V3_TAIL_MONOTONIC_FILTER_ENABLE, + ANALYSIS_ROBUST_V3_TAIL_MONOTONIC_RATIO_MAX, + ANALYSIS_ROBUST_V3_MAX_PEAK_WIDTH_MIN, + ANALYSIS_ROBUST_V3_LEADING_EDGE_FILTER_ENABLE, + ANALYSIS_ROBUST_V3_LEADING_EDGE_RELATIVE_PROMINENCE_MAX, + ANALYSIS_ROBUST_V3_LEADING_EDGE_MONOTONIC_RATIO_MIN, + ANALYSIS_PEAK_RT_MIN, ANALYSIS_PEAK_RT_MAX, + ANALYSIS_TIC_AREA_MIN, ANALYSIS_TIC_AREA_MAX, + ANALYSIS_FID_AREA_MIN, ANALYSIS_FID_AREA_MAX, + ANALYSIS_REPORT_DIR, ANALYSIS_STRUCTURE_CACHE_DIR, + ANALYSIS_NIST_PATH, ANALYSIS_NIST_MAX_HITS, + ANALYSIS_NIST_SEARCH_TIMEOUT, ANALYSIS_NIST_AVG_SCANS, + ANALYSIS_PIM_ENABLE, ANALYSIS_PIM_AB_M, + ANALYSIS_PIM_BETA, ANALYSIS_PIM_EPSILON_F, + ANALYSIS_ALIGNMENT_INCLUDE_TIC_ONLY, + ANALYSIS_ALIGNMENT_INCLUDE_FID_ONLY, + ANALYSIS_TIC_PLOT_SHOW_COMPOUND, + ANALYSIS_CHROMATOGRAM_PLOT_PPI, ANALYSIS_MS_SPECTRUM_PLOT_PPI, + ANALYSIS_STRUCTURE_IMAGE_PPI, + ANALYSIS_MSPEPSEARCH_ENABLE, ANALYSIS_MSPEPSEARCH_EXE, + ANALYSIS_PROCESS_GC_MS_ENABLE_SSHM_SEARCH, + ANALYSIS_PROCESS_GC_MS_ENABLE_IHSHM_SEARCH, + ANALYSIS_MSPEPSEARCH_LIB_PATH, ANALYSIS_MSPEPSEARCH_LIB_TYPE, + ANALYSIS_NIST_MAINLIB_MSP, ANALYSIS_SSHM_HITS, + ANALYSIS_SSHM_B_SS, ANALYSIS_IHSHM_HITS, + ANALYSIS_IHSHM_MEMF, ANALYSIS_MSPEPSEARCH_TIMEOUT, + ANALYSIS_NIST_STRUCTURE_SEED_MSP, ANALYSIS_NIST_STRUCTURE_SEED_MOL_DIR, + ANALYSIS_NIST_STRUCTURE_RUNTIME_CACHE_PATH, ANALYSIS_STRUCTURE_OFFLINE_ONLY, + ANALYSIS_ARCHIVE_DIR, ANALYSIS_ARCHIVE_COPY_RAW_DATA. + """ + defaults = Settings() + + def _str(key: str, default: str) -> str: + return os.getenv(key, default) + + def _int(key: str, default: int) -> int: + try: + return int(os.getenv(key, str(default))) + except ValueError: + return default + + def _float(key: str, default: float) -> float: + try: + return float(os.getenv(key, str(default))) + except ValueError: + return default + + def _path(key: str, default: Path) -> Path: + value = os.getenv(key) + if value is None or value.strip() == "": + return default + return Path(value) + + def _opt_float(key: str, default: Optional[float] = None) -> Optional[float]: + value = os.getenv(key) + if value is None or value.strip() == "": + return default + try: + return float(value) + except ValueError: + return default + + def _bool(key: str, default: bool) -> bool: + value = os.getenv(key) + if value is None or value.strip() == "": + return default + return value.strip().lower() in ("true", "1", "yes") + + return Settings( + gc_ms_host=_str("ANALYSIS_GC_MS_HOST", defaults.gc_ms_host), + gc_ms_port=_int("ANALYSIS_GC_MS_PORT", defaults.gc_ms_port), + gc_ms_timeout=_float("ANALYSIS_GC_MS_TIMEOUT", defaults.gc_ms_timeout), + uplc_qtof_host=_str("ANALYSIS_UPLC_QTOF_HOST", defaults.uplc_qtof_host), + uplc_qtof_port=_int("ANALYSIS_UPLC_QTOF_PORT", defaults.uplc_qtof_port), + uplc_qtof_timeout=_float("ANALYSIS_UPLC_QTOF_TIMEOUT", defaults.uplc_qtof_timeout), + uplc_qtof_append_wash_stop=_bool( + "ANALYSIS_UPLC_QTOF_APPEND_WASH_STOP", defaults.uplc_qtof_append_wash_stop + ), + hplc_host=_str("ANALYSIS_HPLC_HOST", defaults.hplc_host), + hplc_port=_int("ANALYSIS_HPLC_PORT", defaults.hplc_port), + hplc_timeout=_float("ANALYSIS_HPLC_TIMEOUT", defaults.hplc_timeout), + gc_ms_data_dir=_path("ANALYSIS_GC_MS_DATA_DIR", defaults.gc_ms_data_dir), + uplc_qtof_data_dir=_path("ANALYSIS_UPLC_QTOF_DATA_DIR", defaults.uplc_qtof_data_dir), + hplc_data_dir=_path("ANALYSIS_HPLC_DATA_DIR", defaults.hplc_data_dir), + synthesis_tasks_dir=_path("ANALYSIS_SYNTHESIS_TASKS_DIR", defaults.synthesis_tasks_dir), + data_dir=_path("ANALYSIS_DATA_DIR", defaults.data_dir), + log_level=_str("ANALYSIS_LOG_LEVEL", defaults.log_level), + peak_smoothing_window=_int("ANALYSIS_PEAK_SMOOTHING_WINDOW", defaults.peak_smoothing_window), + peak_prominence=_float("ANALYSIS_PEAK_PROMINENCE", defaults.peak_prominence), + peak_min_distance=_int("ANALYSIS_PEAK_MIN_DISTANCE", defaults.peak_min_distance), + peak_width_rel_height=_float("ANALYSIS_PEAK_WIDTH_REL_HEIGHT", defaults.peak_width_rel_height), + fid_peak_prominence=_float("ANALYSIS_FID_PEAK_PROMINENCE", defaults.fid_peak_prominence), + fid_peak_min_distance=_int("ANALYSIS_FID_PEAK_MIN_DISTANCE", defaults.fid_peak_min_distance), + use_als_baseline=_bool("ANALYSIS_USE_ALS_BASELINE", defaults.use_als_baseline), + als_lambda=_float("ANALYSIS_ALS_LAMBDA", defaults.als_lambda), + als_p=_float("ANALYSIS_ALS_P", defaults.als_p), + use_valley_boundary=_bool("ANALYSIS_USE_VALLEY_BOUNDARY", defaults.use_valley_boundary), + integration_mode=_str("ANALYSIS_INTEGRATION_MODE", defaults.integration_mode), + baseline_method=_str("ANALYSIS_BASELINE_METHOD", defaults.baseline_method), + baseline_quantile=_float("ANALYSIS_BASELINE_QUANTILE", defaults.baseline_quantile), + baseline_window_min=_float("ANALYSIS_BASELINE_WINDOW_MIN", defaults.baseline_window_min), + boundary_sigma_factor=_float("ANALYSIS_BOUNDARY_SIGMA_FACTOR", defaults.boundary_sigma_factor), + boundary_edge_ratio=_float("ANALYSIS_BOUNDARY_EDGE_RATIO", defaults.boundary_edge_ratio), + boundary_expand_factor=_float("ANALYSIS_BOUNDARY_EXPAND_FACTOR", defaults.boundary_expand_factor), + boundary_min_span_min=_float("ANALYSIS_BOUNDARY_MIN_SPAN_MIN", defaults.boundary_min_span_min), + boundary_max_span_min=_float("ANALYSIS_BOUNDARY_MAX_SPAN_MIN", defaults.boundary_max_span_min), + robust_v3_shoulder_filter_enable=_bool( + "ANALYSIS_ROBUST_V3_SHOULDER_FILTER_ENABLE", + defaults.robust_v3_shoulder_filter_enable, + ), + robust_v3_shoulder_width_max_min=_float( + "ANALYSIS_ROBUST_V3_SHOULDER_WIDTH_MAX_MIN", + defaults.robust_v3_shoulder_width_max_min, + ), + robust_v3_shoulder_gap_max_min=_float( + "ANALYSIS_ROBUST_V3_SHOULDER_GAP_MAX_MIN", + defaults.robust_v3_shoulder_gap_max_min, + ), + robust_v3_shoulder_relative_prominence_max=_float( + "ANALYSIS_ROBUST_V3_SHOULDER_RELATIVE_PROMINENCE_MAX", + defaults.robust_v3_shoulder_relative_prominence_max, + ), + robust_v3_tail_artifact_filter_enable=_bool( + "ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_FILTER_ENABLE", + defaults.robust_v3_tail_artifact_filter_enable, + ), + robust_v3_tail_artifact_gap_max_min=_float( + "ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_GAP_MAX_MIN", + defaults.robust_v3_tail_artifact_gap_max_min, + ), + robust_v3_tail_artifact_relative_prominence_max=_float( + "ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_RELATIVE_PROMINENCE_MAX", + defaults.robust_v3_tail_artifact_relative_prominence_max, + ), + robust_v3_tail_artifact_half_width_asymmetry_min=_float( + "ANALYSIS_ROBUST_V3_TAIL_ARTIFACT_HALF_WIDTH_ASYMMETRY_MIN", + defaults.robust_v3_tail_artifact_half_width_asymmetry_min, + ), + robust_v3_tail_monotonic_filter_enable=_bool( + "ANALYSIS_ROBUST_V3_TAIL_MONOTONIC_FILTER_ENABLE", + defaults.robust_v3_tail_monotonic_filter_enable, + ), + robust_v3_tail_monotonic_ratio_max=_float( + "ANALYSIS_ROBUST_V3_TAIL_MONOTONIC_RATIO_MAX", + defaults.robust_v3_tail_monotonic_ratio_max, + ), + robust_v3_max_peak_width_min=_float( + "ANALYSIS_ROBUST_V3_MAX_PEAK_WIDTH_MIN", + defaults.robust_v3_max_peak_width_min, + ), + robust_v3_leading_edge_filter_enable=_bool( + "ANALYSIS_ROBUST_V3_LEADING_EDGE_FILTER_ENABLE", + defaults.robust_v3_leading_edge_filter_enable, + ), + robust_v3_leading_edge_relative_prominence_max=_float( + "ANALYSIS_ROBUST_V3_LEADING_EDGE_RELATIVE_PROMINENCE_MAX", + defaults.robust_v3_leading_edge_relative_prominence_max, + ), + robust_v3_leading_edge_monotonic_ratio_min=_float( + "ANALYSIS_ROBUST_V3_LEADING_EDGE_MONOTONIC_RATIO_MIN", + defaults.robust_v3_leading_edge_monotonic_ratio_min, + ), + report_dir=_path("ANALYSIS_REPORT_DIR", defaults.report_dir), + structure_cache_dir=_path("ANALYSIS_STRUCTURE_CACHE_DIR", defaults.structure_cache_dir), + chemical_list_path=_path("ANALYSIS_CHEMICAL_LIST_PATH", defaults.chemical_list_path), + nist_path=_path("ANALYSIS_NIST_PATH", defaults.nist_path), + nist_max_hits=_int("ANALYSIS_NIST_MAX_HITS", defaults.nist_max_hits), + nist_search_timeout=_float("ANALYSIS_NIST_SEARCH_TIMEOUT", defaults.nist_search_timeout), + nist_avg_scans=_int("ANALYSIS_NIST_AVG_SCANS", defaults.nist_avg_scans), + pim_enable=_bool("ANALYSIS_PIM_ENABLE", defaults.pim_enable), + pim_ab_m=_float("ANALYSIS_PIM_AB_M", defaults.pim_ab_m), + pim_beta=_float("ANALYSIS_PIM_BETA", defaults.pim_beta), + pim_epsilon_f=_float("ANALYSIS_PIM_EPSILON_F", defaults.pim_epsilon_f), + mspepsearch_enable=_bool("ANALYSIS_MSPEPSEARCH_ENABLE", defaults.mspepsearch_enable), + process_gc_ms_enable_sshm_search=_bool( + "ANALYSIS_PROCESS_GC_MS_ENABLE_SSHM_SEARCH", + defaults.process_gc_ms_enable_sshm_search + ), + process_gc_ms_enable_ihshm_search=_bool( + "ANALYSIS_PROCESS_GC_MS_ENABLE_IHSHM_SEARCH", + defaults.process_gc_ms_enable_ihshm_search + ), + mspepsearch_exe=_path("ANALYSIS_MSPEPSEARCH_EXE", defaults.mspepsearch_exe), + mspepsearch_lib_path=_path("ANALYSIS_MSPEPSEARCH_LIB_PATH", defaults.mspepsearch_lib_path), + mspepsearch_lib_type=_str("ANALYSIS_MSPEPSEARCH_LIB_TYPE", defaults.mspepsearch_lib_type), + nist_mainlib_msp=_path("ANALYSIS_NIST_MAINLIB_MSP", defaults.nist_mainlib_msp), + nist_structure_seed_msp=_path( + "ANALYSIS_NIST_STRUCTURE_SEED_MSP", defaults.nist_structure_seed_msp + ), + nist_structure_seed_mol_dir=_path( + "ANALYSIS_NIST_STRUCTURE_SEED_MOL_DIR", defaults.nist_structure_seed_mol_dir + ), + nist_structure_runtime_cache_path=_path( + "ANALYSIS_NIST_STRUCTURE_RUNTIME_CACHE_PATH", + defaults.nist_structure_runtime_cache_path, + ), + structure_offline_only=_bool( + "ANALYSIS_STRUCTURE_OFFLINE_ONLY", defaults.structure_offline_only + ), + sshm_hits=_int("ANALYSIS_SSHM_HITS", defaults.sshm_hits), + sshm_b_ss=_int("ANALYSIS_SSHM_B_SS", defaults.sshm_b_ss), + ihshm_hits=_int("ANALYSIS_IHSHM_HITS", defaults.ihshm_hits), + ihshm_mEMF=_int("ANALYSIS_IHSHM_MEMF", defaults.ihshm_mEMF), + mspepsearch_timeout=_float("ANALYSIS_MSPEPSEARCH_TIMEOUT", defaults.mspepsearch_timeout), + peak_rt_min=_opt_float("ANALYSIS_PEAK_RT_MIN", defaults.peak_rt_min), + peak_rt_max=_opt_float("ANALYSIS_PEAK_RT_MAX", defaults.peak_rt_max), + tic_area_min=_opt_float("ANALYSIS_TIC_AREA_MIN", defaults.tic_area_min), + tic_area_max=_opt_float("ANALYSIS_TIC_AREA_MAX", defaults.tic_area_max), + fid_area_min=_opt_float("ANALYSIS_FID_AREA_MIN", defaults.fid_area_min), + fid_area_max=_opt_float("ANALYSIS_FID_AREA_MAX", defaults.fid_area_max), + alignment_tolerance=_float("ANALYSIS_ALIGNMENT_TOLERANCE", defaults.alignment_tolerance), + alignment_include_tic_only=_bool("ANALYSIS_ALIGNMENT_INCLUDE_TIC_ONLY", defaults.alignment_include_tic_only), + alignment_include_fid_only=_bool("ANALYSIS_ALIGNMENT_INCLUDE_FID_ONLY", defaults.alignment_include_fid_only), + tic_plot_show_compound=_bool("ANALYSIS_TIC_PLOT_SHOW_COMPOUND", defaults.tic_plot_show_compound), + chromatogram_plot_ppi=_int("ANALYSIS_CHROMATOGRAM_PLOT_PPI", defaults.chromatogram_plot_ppi), + ms_spectrum_plot_ppi=_int("ANALYSIS_MS_SPECTRUM_PLOT_PPI", defaults.ms_spectrum_plot_ppi), + structure_image_ppi=_int("ANALYSIS_STRUCTURE_IMAGE_PPI", defaults.structure_image_ppi), + structure_image_size=_int("ANALYSIS_STRUCTURE_IMAGE_SIZE", defaults.structure_image_size), + yield_rt_tolerance=_float("ANALYSIS_YIELD_RT_TOLERANCE", defaults.yield_rt_tolerance), + archive_dir=_path("ANALYSIS_ARCHIVE_DIR", defaults.archive_dir), + archive_copy_raw_data=_bool("ANALYSIS_ARCHIVE_COPY_RAW_DATA", defaults.archive_copy_raw_data), + ) + + +def configure_logging(level: str = "INFO") -> None: + """ + 功能: + 配置全局 logging. + 参数: + level: 日志等级字符串. + 返回: + 无. + """ + numeric_level = getattr(logging, level.upper(), logging.INFO) + + root_logger = logging.getLogger() + root_logger.setLevel(numeric_level) + + if not root_logger.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_logger.addHandler(handler) diff --git a/unilabos/devices/eit_analysis_station/controller/__init__.py b/unilabos/devices/eit_analysis_station/controller/__init__.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/unilabos/devices/eit_analysis_station/controller/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/unilabos/devices/eit_analysis_station/controller/analysis_controller.py b/unilabos/devices/eit_analysis_station/controller/analysis_controller.py new file mode 100644 index 00000000..ba3a828f --- /dev/null +++ b/unilabos/devices/eit_analysis_station/controller/analysis_controller.py @@ -0,0 +1,3052 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 分析站上层控制器, 读取合成任务 xlsx 中的实验信息和仪器方法配置, + 自动生成分析任务 CSV 并通过 ZhidaClient 提交至对应仪器. + 当前已实现 GC_MS 和 UPLC_QTOF 提交, HPLC 自动提交流程预留占位. +参数: + 无(通过 Settings 传入配置). +返回: + 无. +""" + +import csv +import json +import logging +import io +import re +import shutil +import time +import xml.etree.ElementTree as ET +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Optional, Tuple + +import openpyxl + +from ..config.setting import Settings, configure_logging +from ..driver.zhida_driver import ZhidaClient +from ..processor.chromatogram_plotter import ChromatogramPlotter +from ..processor.data_reader import GCMSDataReader +from ..processor.molecular_mass_predictor import PIMPredictor +from ..processor.mspepsearch_predictor import MSPepSearchPredictor +from ..processor.nist_library_reader import NistLibraryReader +from ..processor.peak_integrator import PeakIntegrator, PeakResult +from ..processor.nist_matcher import NISTMatcher +from ..processor.report_generator import ( + PIMPrediction, SSHMPrediction, iHSHMPrediction, + ReportGenerator, SampleResult, +) +from ..processor.structure_fetcher import NistLocalStructureFetcher +from ..processor.yield_calculator import YieldCalculator, YIELD_CONFIG_SHEET_NAME + + +def _natural_sort_key(path: Path) -> list: + """ + 功能: + 自然排序键函数, 将路径名中的连续数字段转换为 int 排序, + 非数字段按小写字符串排序, 实现 725-1 < 725-2 < 725-10 的效果. + 参数: + path: 文件或目录路径. + 返回: + list: 混合类型排序键列表. + """ + parts = re.split(r'(\d+)', path.stem) + return [int(p) if p.isdigit() else p.lower() for p in parts] + + +class AnalysisStationController: + """ + 功能: + 检测站上层控制器, 提供以下核心流程: + 1. 定位合成任务目录(按 task_id 或取最新任务). + 2. 检查任务状态(非 COMPLETED 时发出 warning 但继续). + 3. 解析任务 xlsx, 提取实验数量及各仪器方法名称. + 4. 生成分析任务 CSV, 双路保存. + 5. 通过 ZhidaClient 提交 CSV 给 GC_MS 和 UPLC_QTOF 仪器. + 参数: + settings: Settings 实例, 为 None 时从环境变量读取. + 返回: + 无. + """ + + # CSV 列头(按仪器协议固定顺序) + _CSV_HEADERS: List[str] = [ + "SampleName", "AcqMethod", "RackCode", "VialPos", "SmplInjVol", "OutputFile" + ] + + # 默认 Rack 编号 + _DEFAULT_RACK_CODE: str = "Rack 6" + + # 默认进样量 + _DEFAULT_INJ_VOL: int = 1 + + def __init__(self, settings: Optional[Settings] = None) -> None: + self._settings = settings or Settings.from_env() + configure_logging(self._settings.log_level) + self._logger = logging.getLogger(self.__class__.__name__) + self._logger.info("分析站控制器初始化完成, 合成任务目录: %s", self._settings.synthesis_tasks_dir) + + # ------------------------------------------------------------------ + # 任务定位与状态检查 + # ------------------------------------------------------------------ + + def _build_task_file_name_map(self, task_id: str) -> Dict[str, str]: + """ + 功能: + 构建任务目录中的旧文件名到新文件名映射, 用于历史文件自动迁移. + 参数: + task_id: 任务 ID. + 返回: + Dict[str, str], 键为旧文件名, 值为新文件名. + """ + return { + f"{task_id}.xlsx": f"{task_id}_experiment_plan.xlsx", + f"integration_report_{task_id}.xlsx": f"{task_id}_integration_report.xlsx", + f"yield_report_{task_id}.xlsx": f"{task_id}_yield_report.xlsx", + f"task_report_{task_id}.xlsx": f"{task_id}_task_report.xlsx", + f"task_report_{task_id}.csv": f"{task_id}_task_report.csv", + f"task_report_{task_id}.pdf": f"{task_id}_task_report.pdf", + } + + def _migrate_task_file_names(self, task_dir: Path, task_id: str) -> None: + """ + 功能: + 在任务目录中执行旧命名到新命名的自动迁移. + 若新文件已存在, 保留新文件并记录 warning. + 参数: + task_dir: 任务目录. + task_id: 任务 ID. + 返回: + 无. + """ + rename_map = self._build_task_file_name_map(task_id) + for old_name, new_name in rename_map.items(): + old_path = task_dir / old_name + new_path = task_dir / new_name + + if old_path.exists() is False: + continue + + if new_path.exists() is True: + self._logger.warning("新命名文件已存在, 跳过迁移: %s -> %s", old_path, new_path) + continue + + try: + old_path.rename(new_path) + self._logger.info("历史文件重命名完成: %s -> %s", old_path.name, new_path.name) + except Exception as exc: + self._logger.warning("历史文件重命名失败: %s -> %s, 错误: %s", old_path, new_path, exc) + + def _find_task_dir(self, task_id: Optional[str] = None) -> Tuple[Path, str]: + """ + 功能: + 定位合成任务目录. + 若指定 task_id 则直接定位, 否则取编号最大(最新)的任务目录. + 参数: + task_id: 任务编号字符串, None 表示自动选取最新任务. + 返回: + Tuple[Path, str]: (任务目录 Path, 任务 ID 字符串). + """ + tasks_root = self._settings.synthesis_tasks_dir + + if not tasks_root.exists(): + raise FileNotFoundError(f"合成任务根目录不存在: {tasks_root}") + + if task_id is not None: + # 按指定 ID 定位 + task_dir = tasks_root / str(task_id) + if not task_dir.is_dir(): + raise FileNotFoundError(f"指定的任务目录不存在: {task_dir}") + self._logger.info("使用指定任务目录: %s", task_dir) + self._migrate_task_file_names(task_dir, str(task_id)) + return task_dir, str(task_id) + + # 自动选取编号最大的子目录 + sub_dirs = [d for d in tasks_root.iterdir() if d.is_dir()] + if not sub_dirs: + raise FileNotFoundError(f"合成任务根目录下没有任务: {tasks_root}") + + # 尝试将目录名解析为整数排序, 取最大值 + def _dir_key(d: Path) -> int: + try: + return int(d.name) + except ValueError: + return -1 + + latest_dir = max(sub_dirs, key=_dir_key) + self._migrate_task_file_names(latest_dir, latest_dir.name) + self._logger.info("自动选取最新任务目录: %s", latest_dir) + return latest_dir, latest_dir.name + + def _check_task_status(self, task_dir: Path) -> str: + """ + 功能: + 读取 task_info.json 中的任务状态, 非 COMPLETED 时发出 warning. + 参数: + task_dir: 任务目录 Path. + 返回: + str: 任务状态字符串(如 "COMPLETED"). + """ + info_path = task_dir / "task_info.json" + if not info_path.exists(): + self._logger.warning("未找到 task_info.json: %s, 跳过状态检查", info_path) + return "UNKNOWN" + + with info_path.open("r", encoding="utf-8") as f: + info = json.load(f) + + status = info.get("status", "UNKNOWN") + + if status != "COMPLETED": + self._logger.warning( + "任务 %s 状态为 [%s], 并非 COMPLETED, 将继续生成分析CSV.", + info.get("task_id", "?"), status + ) + else: + self._logger.info("任务状态: %s", status) + + return status + + # ------------------------------------------------------------------ + # xlsx 解析 + # ------------------------------------------------------------------ + + @staticmethod + def _normalize_sheet_text(value: Any) -> str: + """ + 功能: + 规范化工作表文本, 用于表头和关键字匹配. + 参数: + value: 任意类型单元格值. + 返回: + str, 去空白并转小写后的文本. + """ + text = "" if value is None else str(value) + return ( + text.replace(" ", "") + .replace("\n", "") + .replace("\r", "") + .replace("\t", "") + .strip() + .lower() + ) + + def _find_header_in_sheet( + self, + worksheet: Any, + header_keyword: str, + *, + max_scan_rows: int = 80, + max_scan_cols: int = 40, + ) -> Tuple[Optional[int], Optional[int]]: + """ + 功能: + 在单个工作表中查找目标表头位置. + 参数: + worksheet: openpyxl 工作表对象. + header_keyword: 目标表头关键字, 例如 "实验编号". + max_scan_rows: 最大扫描行数. + max_scan_cols: 最大扫描列数. + 返回: + Tuple[Optional[int], Optional[int]], 命中时返回(行号, 列号), 未命中返回(None, None). + """ + normalized_keyword = self._normalize_sheet_text(header_keyword) + scan_rows = min(worksheet.max_row, max_scan_rows) + scan_cols = min(worksheet.max_column, max_scan_cols) + + for row_index in range(1, scan_rows + 1): + for col_index in range(1, scan_cols + 1): + cell_text = self._normalize_sheet_text(worksheet.cell(row_index, col_index).value) + if normalized_keyword in cell_text and cell_text != "": + return row_index, col_index + + return None, None + + def _sheet_contains_instrument_anchor(self, worksheet: Any) -> bool: + """ + 功能: + 判断工作表是否包含分析仪器方法锚点字段. + 参数: + worksheet: openpyxl 工作表对象. + 返回: + bool, 是否命中 GC_MS/UPLC_QTOF/HPLC 任一锚点. + """ + anchors = {"GC_MS", "UPLC_QTOF", "HPLC"} + scan_rows = min(worksheet.max_row, 300) + + for row_index in range(1, scan_rows + 1): + cell_value = worksheet.cell(row_index, 1).value + if cell_value is None: + continue + if str(cell_value).strip() in anchors: + return True + + return False + + def _select_task_parse_sheet(self, workbook: Any) -> Tuple[Any, int, int]: + """ + 功能: + 为任务解析选择最合适的工作表, 并返回实验编号表头位置. + 选择顺序: 实验方案设定 > 当前 active > 其它工作表. + 命中优先级: 同时命中实验编号表头和仪器锚点 > 仅命中实验编号表头. + 参数: + workbook: openpyxl Workbook 对象. + 返回: + Tuple[Any, int, int], (工作表对象, 实验编号表头行号, 实验编号列号). + """ + candidate_sheet_names: List[str] = [] + preferred_sheet_name = "实验方案设定" + if preferred_sheet_name in workbook.sheetnames: + candidate_sheet_names.append(preferred_sheet_name) + + active_sheet_name = workbook.active.title + if active_sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(active_sheet_name) + + for sheet_name in workbook.sheetnames: + if sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(sheet_name) + + anchor_candidates: List[Tuple[Any, int, int]] = [] + header_only_candidates: List[Tuple[Any, int, int]] = [] + for sheet_name in candidate_sheet_names: + worksheet = workbook[sheet_name] + header_row, exp_no_col = self._find_header_in_sheet(worksheet, "实验编号") + if header_row is None or exp_no_col is None: + continue + + has_anchor = self._sheet_contains_instrument_anchor(worksheet) + if has_anchor: + anchor_candidates.append((worksheet, header_row, exp_no_col)) + continue + + header_only_candidates.append((worksheet, header_row, exp_no_col)) + + if len(anchor_candidates) > 0: + worksheet, header_row, exp_no_col = anchor_candidates[0] + if len(anchor_candidates) > 1: + candidate_names = [item[0].title for item in anchor_candidates] + self._logger.warning( + "任务解析命中多个候选工作表, 将按优先顺序使用 [%s], 其余候选: %s", + worksheet.title, + candidate_names[1:], + ) + return worksheet, header_row, exp_no_col + + if len(header_only_candidates) > 0: + worksheet, header_row, exp_no_col = header_only_candidates[0] + if len(header_only_candidates) > 1: + candidate_names = [item[0].title for item in header_only_candidates] + self._logger.warning( + "任务解析命中多个仅含实验编号表头的工作表, 将按优先顺序使用 [%s], 其余候选: %s", + worksheet.title, + candidate_names[1:], + ) + self._logger.warning( + "任务解析工作表 [%s] 命中实验编号表头, 但未检测到仪器锚点, 将按兼容模式继续解析.", + worksheet.title, + ) + return worksheet, header_row, exp_no_col + + raise ValueError( + f"未找到包含实验编号表头的工作表, 可用工作表: {workbook.sheetnames}" + ) + + def _parse_task_xlsx_legacy(self, task_dir: Path, task_id: str) -> Dict: + """ + 功能: + 解析合成任务 xlsx 文件, 提取实验数量及各仪器方法名称. + 扫描 col A 定位 GC_MS/UPLC_QTOF/HPLC 字段(ASCII 可靠锚点), + 扫描 col C 统计实验数量(连续整数字符串). + 参数: + task_dir: 任务目录 Path. + task_id: 任务 ID 字符串. + 返回: + Dict, 包含以下键: + task_id (str): 任务 ID. + exp_count (int): 实验数量. + gc_ms_method (str|None): GC_MS 方法名, None 表示不使用. + gc_ms_exp_nums (List[int]|None): GC_MS 实验编号过滤列表. + uplc_qtof_method (str|None): UPLC_QTOF 方法名. + uplc_qtof_exp_nums (List[int]|None): UPLC_QTOF 实验编号过滤列表. + hplc_method (str|None): HPLC 方法名. + hplc_exp_nums (List[int]|None): HPLC 实验编号过滤列表. + """ + # 优先查找新命名 xlsx, 兼容旧命名和 .csv + xlsx_path = task_dir / f"{task_id}_experiment_plan.xlsx" + legacy_xlsx_path = task_dir / f"{task_id}.xlsx" + csv_path = task_dir / f"{task_id}.csv" + + if xlsx_path.exists(): + file_path = xlsx_path + elif legacy_xlsx_path.exists(): + file_path = legacy_xlsx_path + elif csv_path.exists(): + file_path = csv_path + else: + raise FileNotFoundError( + f"未找到任务文件 {task_id}_experiment_plan.xlsx, {task_id}.xlsx 或 {task_id}.csv 于: {task_dir}" + ) + + self._logger.info("解析任务文件: %s", file_path) + + wb = openpyxl.load_workbook(file_path, data_only=True) + ws = wb.active + + exp_count = 0 + gc_ms_method_raw: Optional[str] = None + uplc_qtof_method_raw: Optional[str] = None + hplc_method_raw: Optional[str] = None + + for row in ws.iter_rows(values_only=True): + col_a = row[0] if len(row) > 0 else None + col_b = row[1] if len(row) > 1 else None + col_c = row[2] if len(row) > 2 else None + + # 统计 col C 中连续整数形式的实验编号 + if col_c is not None: + try: + exp_num = int(str(col_c).strip()) + if exp_num > exp_count: + exp_count = exp_num # 取最大值即为总数 + except (ValueError, TypeError): + pass + + # 定位仪器方法行(col A 为 ASCII 关键字) + if col_a is None: + continue + col_a_str = str(col_a).strip() + + if col_a_str == "GC_MS": + # col B 为方法名, 空值则跳过该仪器 + gc_ms_method_raw = str(col_b).strip() if col_b is not None else None + elif col_a_str == "UPLC_QTOF": + uplc_qtof_method_raw = str(col_b).strip() if col_b is not None else None + elif col_a_str == "HPLC": + hplc_method_raw = str(col_b).strip() if col_b is not None else None + + if exp_count == 0: + raise ValueError(f"未能从 {file_path} 中读取到有效实验编号, 请检查 col C 数据.") + + # 支持 Method(1-8,9) 写法, 提取方法名与实验编号过滤列表. + gc_ms_method, gc_ms_exp_nums = self._parse_method_and_exp_filter( + gc_ms_method_raw, exp_count, "GC_MS" + ) + uplc_qtof_method, uplc_qtof_exp_nums = self._parse_method_and_exp_filter( + uplc_qtof_method_raw, exp_count, "UPLC_QTOF" + ) + hplc_method, hplc_exp_nums = self._parse_method_and_exp_filter( + hplc_method_raw, exp_count, "HPLC" + ) + + self._logger.info( + "任务解析完成: 实验数=%d, GC_MS方法=%s, GC_MS实验=%s, UPLC_QTOF方法=%s, " + "UPLC_QTOF实验=%s, HPLC方法=%s, HPLC实验=%s", + exp_count, + gc_ms_method, + gc_ms_exp_nums if gc_ms_exp_nums is not None else "ALL", + uplc_qtof_method, + uplc_qtof_exp_nums if uplc_qtof_exp_nums is not None else "ALL", + hplc_method, + hplc_exp_nums if hplc_exp_nums is not None else "ALL", + ) + + return { + "task_id": task_id, + "exp_count": exp_count, + "gc_ms_method": gc_ms_method, + "gc_ms_exp_nums": gc_ms_exp_nums, + "uplc_qtof_method": uplc_qtof_method, + "uplc_qtof_exp_nums": uplc_qtof_exp_nums, + "hplc_method": hplc_method, + "hplc_exp_nums": hplc_exp_nums, + } + + def _parse_task_xlsx(self, task_dir: Path, task_id: str) -> Dict: + """ + 功能: + 解析合成任务 xlsx 文件, 提取实验数量及各仪器方法名称. + 工作表选择优先级: 实验方案设定 > 当前 active > 其它工作表. + 实验数量统计基于“实验编号”表头定位到的列, 避免误读其它表格数值列. + 参数: + task_dir: 任务目录 Path. + task_id: 任务 ID 字符串. + 返回: + Dict, 包含以下键: + task_id (str): 任务 ID. + exp_count (int): 实验数量. + gc_ms_method (str|None): GC_MS 方法名, None 表示不使用. + gc_ms_exp_nums (List[int]|None): GC_MS 实验编号过滤列表. + uplc_qtof_method (str|None): UPLC_QTOF 方法名. + uplc_qtof_exp_nums (List[int]|None): UPLC_QTOF 实验编号过滤列表. + hplc_method (str|None): HPLC 方法名. + hplc_exp_nums (List[int]|None): HPLC 实验编号过滤列表. + """ + # 优先查找新命名 xlsx, 兼容旧命名和 .csv. + xlsx_path = task_dir / f"{task_id}_experiment_plan.xlsx" + legacy_xlsx_path = task_dir / f"{task_id}.xlsx" + csv_path = task_dir / f"{task_id}.csv" + + if xlsx_path.exists(): + file_path = xlsx_path + elif legacy_xlsx_path.exists(): + file_path = legacy_xlsx_path + elif csv_path.exists(): + file_path = csv_path + else: + raise FileNotFoundError( + f"未找到任务文件 {task_id}_experiment_plan.xlsx, {task_id}.xlsx 或 {task_id}.csv 于: {task_dir}" + ) + + self._logger.info("解析任务文件: %s", file_path) + + wb = openpyxl.load_workbook(file_path, data_only=True) + try: + ws, header_row, exp_no_col = self._select_task_parse_sheet(wb) + if ws.title != wb.active.title: + self._logger.info( + "任务解析使用工作表: %s, 当前活动工作表: %s", + ws.title, + wb.active.title, + ) + + exp_count = 0 + gc_ms_method_raw: Optional[str] = None + uplc_qtof_method_raw: Optional[str] = None + hplc_method_raw: Optional[str] = None + + for row_index, row in enumerate(ws.iter_rows(values_only=True), start=1): + col_a = row[0] if len(row) > 0 else None + col_b = row[1] if len(row) > 1 else None + col_exp = row[exp_no_col - 1] if len(row) >= exp_no_col else None + + # 按实验编号表头列统计实验总数, 避免误读其它表格中的数值列. + if row_index > header_row and col_exp is not None: + try: + exp_num = int(str(col_exp).strip()) + if exp_num > exp_count: + exp_count = exp_num + except (ValueError, TypeError): + pass + + # 定位仪器方法行: col A 中 ASCII 关键字. + if col_a is None: + continue + col_a_str = str(col_a).strip() + + if col_a_str == "GC_MS": + # col B 为方法名, 空值则跳过该仪器. + gc_ms_method_raw = str(col_b).strip() if col_b is not None else None + elif col_a_str == "UPLC_QTOF": + uplc_qtof_method_raw = str(col_b).strip() if col_b is not None else None + elif col_a_str == "HPLC": + hplc_method_raw = str(col_b).strip() if col_b is not None else None + + if exp_count == 0: + raise ValueError( + f"未能从工作表 [{ws.title}] 的实验编号列读取有效实验编号, " + f"表头位置: row={header_row}, col={exp_no_col}, 文件: {file_path}" + ) + + # 支持 Method(1-8,9) 写法, 提取方法名与实验编号过滤列表. + gc_ms_method, gc_ms_exp_nums = self._parse_method_and_exp_filter( + gc_ms_method_raw, exp_count, "GC_MS" + ) + uplc_qtof_method, uplc_qtof_exp_nums = self._parse_method_and_exp_filter( + uplc_qtof_method_raw, exp_count, "UPLC_QTOF" + ) + hplc_method, hplc_exp_nums = self._parse_method_and_exp_filter( + hplc_method_raw, exp_count, "HPLC" + ) + + self._logger.info( + "任务解析完成: 实验数=%d, GC_MS方法=%s, GC_MS实验=%s, UPLC_QTOF方法=%s, " + "UPLC_QTOF实验=%s, HPLC方法=%s, HPLC实验=%s", + exp_count, + gc_ms_method, + gc_ms_exp_nums if gc_ms_exp_nums is not None else "ALL", + uplc_qtof_method, + uplc_qtof_exp_nums if uplc_qtof_exp_nums is not None else "ALL", + hplc_method, + hplc_exp_nums if hplc_exp_nums is not None else "ALL", + ) + + return { + "task_id": task_id, + "exp_count": exp_count, + "gc_ms_method": gc_ms_method, + "gc_ms_exp_nums": gc_ms_exp_nums, + "uplc_qtof_method": uplc_qtof_method, + "uplc_qtof_exp_nums": uplc_qtof_exp_nums, + "hplc_method": hplc_method, + "hplc_exp_nums": hplc_exp_nums, + } + finally: + wb.close() + + def _parse_method_and_exp_filter( + self, + raw_method: Optional[str], + exp_count: int, + instrument: str, + ) -> Tuple[Optional[str], Optional[List[int]]]: + """ + 功能: + 解析方法字符串中的实验编号过滤表达式. + 支持 `Generic_15min(1-8,9)` 和 `Generic_15min(1-8,9)` 等写法. + 仅当括号内容为数字范围表达式时启用过滤, 否则保持原方法名不变. + 参数: + raw_method: 原始方法字符串, None 或空字符串表示未配置. + exp_count: 实验总数, 用于校验编号范围. + instrument: 仪器名称, 用于日志和错误信息. + 返回: + Tuple[Optional[str], Optional[List[int]]]: + 第一个值为去掉过滤后的方法名, 第二个值为过滤实验编号列表. + 若未配置过滤, 第二个值为 None. + """ + if raw_method is None: + return None, None + + method_text = raw_method.strip() + if method_text == "": + return None, None + + # 仅匹配结尾一对括号, 避免干扰方法名中间内容. + match = re.match(r"^(.*?)[\((]\s*(.*?)\s*[\))]\s*$", method_text) + if match is None: + return method_text, None + + method_name = match.group(1).strip() + selector_text = match.group(2).strip() + + if selector_text == "": + return method_text, None + + # 仅当括号中是编号表达式时才按过滤处理, 其余情况保持原方法名. + if re.fullmatch(r"[0-90-9\s,,、;;\--—–~~到至]+", selector_text) is None: + return method_text, None + + if method_name == "": + raise ValueError(f"{instrument} 方法配置无效, 括号前方法名不能为空: {raw_method}") + + exp_nums = self._parse_exp_selector(selector_text, exp_count, instrument) + return method_name, exp_nums + + def _parse_exp_selector(self, selector_text: str, exp_count: int, instrument: str) -> List[int]: + """ + 功能: + 将实验编号过滤表达式解析为有序去重的实验编号列表. + 支持中英文括号内的中文标点, 例如 `1-8,9` `1~8,9` `1到8、9`. + 参数: + selector_text: 括号内原始表达式文本. + exp_count: 实验总数, 用于范围校验. + instrument: 仪器名称, 用于错误提示. + 返回: + List[int], 有序去重后的实验编号列表. + """ + full_width_digits = str.maketrans("0123456789", "0123456789") + normalized = selector_text.translate(full_width_digits) + + # 统一分隔符与范围连接符, 简化后续解析. + normalized = normalized.replace(",", ",").replace("、", ",").replace(";", ",").replace(";", ",") + for range_sep in ("-", "—", "–", "~", "~", "到", "至"): + normalized = normalized.replace(range_sep, "-") + normalized = normalized.replace(" ", "") + + if normalized == "": + raise ValueError(f"{instrument} 方法实验编号为空, 请检查括号内容: {selector_text}") + + exp_num_set = set() + for token in normalized.split(","): + if token == "": + continue + + if "-" in token: + parts = token.split("-") + if len(parts) != 2 or parts[0] == "" or parts[1] == "": + raise ValueError(f"{instrument} 方法实验编号格式无效: {selector_text}") + + try: + start_num = int(parts[0]) + end_num = int(parts[1]) + except ValueError as exc: + raise ValueError(f"{instrument} 方法实验编号格式无效: {selector_text}") from exc + + if start_num > end_num: + raise ValueError(f"{instrument} 方法实验编号范围无效: {selector_text}") + + for exp_num in range(start_num, end_num + 1): + exp_num_set.add(exp_num) + else: + try: + exp_num_set.add(int(token)) + except ValueError as exc: + raise ValueError(f"{instrument} 方法实验编号格式无效: {selector_text}") from exc + + if len(exp_num_set) == 0: + raise ValueError(f"{instrument} 方法实验编号为空, 请检查括号内容: {selector_text}") + + exp_nums = sorted(exp_num_set) + invalid_nums = [num for num in exp_nums if num < 1 or num > exp_count] + if len(invalid_nums) > 0: + raise ValueError( + f"{instrument} 方法实验编号超出范围(1-{exp_count}): {invalid_nums}, 原始表达式: {selector_text}" + ) + + return exp_nums + + # ------------------------------------------------------------------ + # VialPos 计算 + # ------------------------------------------------------------------ + + def _calc_vial_pos(self, exp_num: int) -> int: + """ + 功能: + 根据实验编号计算 GC-MS 进样位置 VialPos. + + 样品托盘(闪滤瓶外瓶托盘) 规格: 6行(A-F) × 8列(1-8). + 装样遵循蛇形规则: + 偶数行(A/C/E): 从左到右 col 1→8. + 奇数行(B/D/F): 从右到左 col 8→1. + GC-MS 进样位置从 F8=1 向上递增, 直至 A1=48. + 参数: + exp_num: 实验编号, 范围 1-48. + 返回: + int, 对应的 VialPos(1-48). + """ + exp_0 = exp_num - 1 # 转为 0-indexed + row_0 = exp_0 // 8 # 行索引: 0=A, 1=B, ..., 5=F + col_within = exp_0 % 8 # 该行内第几个样品(0-indexed) + + # 蛇形: 奇数行(B/D/F)列方向翻转 + col_0 = col_within if row_0 % 2 == 0 else 7 - col_within + + row = row_0 + 1 # 1=A, ..., 6=F + col = col_0 + 1 # 1-8 + + # VialPos: F8=1, F7=2, ..., A1=48 + vial_pos = (6 - row) * 8 + (9 - col) + return vial_pos + + # ------------------------------------------------------------------ + # CSV 生成 + # ------------------------------------------------------------------ + + def _generate_gc_ms_csv( + self, + task_id: str, + exp_count: int, + method: str, + exp_nums: Optional[List[int]] = None, + ) -> str: + """ + 功能: + 生成 GC_MS 分析任务 CSV 字符串. + 每一行对应一个实验样品, VialPos 由蛇形映射公式计算. + 当传入 exp_nums 时, 仅为指定实验编号生成提交行. + 参数: + task_id: 任务 ID, 用于拼接 SampleName/OutputFile. + exp_count: 实验数量. + method: GC_MS 方法名称. + exp_nums: 可选实验编号列表, None 表示 1..exp_count 全量提交. + 返回: + str: CSV 文本内容(含列头). + """ + output = io.StringIO() + writer = csv.writer(output, lineterminator="\n") + + # 写入列头 + writer.writerow(self._CSV_HEADERS) + + target_exp_nums = exp_nums if exp_nums is not None else list(range(1, exp_count + 1)) + + for exp_num in target_exp_nums: + sample_name = f"{task_id}-{exp_num}" # 如 "719-1" + vial_pos = self._calc_vial_pos(exp_num) # VialPos 蛇形映射 + writer.writerow([ + sample_name, # SampleName + method, # AcqMethod + self._DEFAULT_RACK_CODE, # RackCode + vial_pos, # VialPos + self._DEFAULT_INJ_VOL, # SmplInjVol + sample_name, # OutputFile(与 SampleName 相同) + ]) + + return output.getvalue() + + # ------------------------------------------------------------------ + # CSV 保存 + # ------------------------------------------------------------------ + + def _save_csv(self, content: str, task_id: str, instrument: str) -> List[Path]: + """ + 功能: + 将 CSV 内容双路保存: + 1. 分析站本地数据目录: eit_analysis_station/data//.csv. + 2. 合成任务目录: eit_synthesis_station/data/tasks//.csv. + 参数: + content: CSV 文本内容. + task_id: 任务 ID. + instrument: 仪器名称(如 "gc_ms", "hplc", "uplc_qtof"). + 返回: + List[Path]: 实际保存的文件路径列表. + """ + filename = f"{instrument}.csv" + saved_paths: List[Path] = [] + + # 路径 1: 分析站本地 data// + local_dir = self._settings.data_dir / task_id + local_dir.mkdir(parents=True, exist_ok=True) + local_path = local_dir / filename + local_path.write_text(content, encoding="utf-8") + self._logger.info("CSV 已保存至本地: %s", local_path) + saved_paths.append(local_path) + + # 路径 2: 合成任务目录 synthesis_tasks// + syn_dir = self._settings.synthesis_tasks_dir / task_id + if syn_dir.is_dir(): + syn_path = syn_dir / filename + syn_path.write_text(content, encoding="utf-8") + self._logger.info("CSV 已同步至合成任务目录: %s", syn_path) + saved_paths.append(syn_path) + else: + self._logger.warning("合成任务目录不存在, 跳过同步: %s", syn_dir) + + return saved_paths + + # ------------------------------------------------------------------ + # GC_MS 提交流程 + # ------------------------------------------------------------------ + + def _do_submit_gc_ms(self, resolved_id: str, task_info: Dict) -> Dict: + """ + 功能: + GC_MS 提交核心逻辑(内部方法), 接收已解析的任务信息直接执行, + 避免 run_analysis 统一调度时重复解析 xlsx. + 参数: + resolved_id: 任务 ID 字符串. + task_info: _parse_task_xlsx 返回的任务信息字典. + 返回: + Dict: {"success": bool, "return_info": str}. + """ + gc_ms_method = task_info["gc_ms_method"] + + if gc_ms_method is None: + msg = f"任务 {resolved_id} 未配置 GC_MS 方法, 跳过 GC_MS 提交." + self._logger.info(msg) + return {"success": True, "return_info": msg} + + gc_ms_exp_nums = task_info.get("gc_ms_exp_nums") + + # 步骤1: 生成并保存 CSV + csv_content = self._generate_gc_ms_csv( + resolved_id, task_info["exp_count"], gc_ms_method, gc_ms_exp_nums + ) + saved_paths = self._save_csv(csv_content, resolved_id, "gc_ms") + + # 步骤2: 通过 ZhidaClient 提交至 GC_MS 仪器 + client = ZhidaClient( + host=self._settings.gc_ms_host, + port=self._settings.gc_ms_port, + timeout=self._settings.gc_ms_timeout, + ) + self._logger.info( + "连接 GC_MS: %s:%d", self._settings.gc_ms_host, self._settings.gc_ms_port + ) + if gc_ms_exp_nums is not None: + self._logger.info("GC_MS 按实验编号过滤提交: %s", gc_ms_exp_nums) + + try: + client.connect() + # 使用本地保存的第一份 CSV 文件提交 + submit_path = str(saved_paths[0]) + result = client.start_with_csv_file(string=submit_path) + finally: + client.close() # 确保连接关闭 + + self._logger.info("GC_MS 提交结果: %s", result) + return result + + def submit_gc_ms(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + GC_MS 分析任务完整提交流程(公开入口, 独立调用时使用): + 1. 定位任务目录(task_id 或最新). + 2. 检查 task_info.json 状态(非 COMPLETED 则 warning 后继续). + 3. 解析 xlsx, 若无 gc_ms_method 则跳过提交. + 4. 生成分析 CSV 并双路保存. + 5. ZhidaClient 连接 GC_MS 并调用 start_with_csv_file 提交. + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + 返回: + Dict: {"success": bool, "return_info": str}. + """ + try: + # 定位任务目录 + task_dir, resolved_id = self._find_task_dir(task_id) + # 检查任务状态(仅 warning, 不阻断) + self._check_task_status(task_dir) + # 解析 xlsx + task_info = self._parse_task_xlsx(task_dir, resolved_id) + # 调用核心提交逻辑 + return self._do_submit_gc_ms(resolved_id, task_info) + + except Exception as exc: + msg = f"GC_MS 提交失败: {exc}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + + # ------------------------------------------------------------------ + # UPLC_QTOF 提交流程 + # ------------------------------------------------------------------ + + def _do_submit_uplc_qtof(self, resolved_id: str, task_info: Dict) -> Dict: + """ + 功能: + UPLC_QTOF 提交核心逻辑(内部方法), 接收已解析的任务信息直接执行. + 通过复用统一 CSV 协议格式, 避免 run_analysis 统一调度时重复解析 xlsx. + 参数: + resolved_id: 任务 ID 字符串. + task_info: _parse_task_xlsx 返回的任务信息字典. + 返回: + Dict: {"success": bool, "return_info": str}. + """ + uplc_qtof_method = task_info["uplc_qtof_method"] + + if uplc_qtof_method is None: + msg = f"任务 {resolved_id} 未配置 UPLC_QTOF 方法, 跳过 UPLC_QTOF 提交." + self._logger.info(msg) + return {"success": True, "return_info": msg} + + uplc_qtof_exp_nums = task_info.get("uplc_qtof_exp_nums") + + # UPLC_QTOF 与 GC_MS 使用一致的 CSV 协议格式. + csv_content = self._generate_gc_ms_csv( + resolved_id, task_info["exp_count"], uplc_qtof_method, uplc_qtof_exp_nums + ) + + if self._settings.uplc_qtof_append_wash_stop is True: + # 末尾追加 wash_stop 行, 仅写方法名, 不填进样信息. + wash_buf = io.StringIO() + wash_writer = csv.writer(wash_buf, lineterminator="\n") + wash_writer.writerow(["", "wash_stop", "", "", "", ""]) + csv_content += wash_buf.getvalue() + self._logger.info("UPLC_QTOF 已追加停止方法行: wash_stop") + else: + self._logger.info("UPLC_QTOF 已关闭追加停止方法") + + saved_paths = self._save_csv(csv_content, resolved_id, "uplc_qtof") + + client = ZhidaClient( + host=self._settings.uplc_qtof_host, + port=self._settings.uplc_qtof_port, + timeout=self._settings.uplc_qtof_timeout, + ) + self._logger.info( + "连接 UPLC_QTOF: %s:%d", + self._settings.uplc_qtof_host, + self._settings.uplc_qtof_port, + ) + if uplc_qtof_exp_nums is not None: + self._logger.info("UPLC_QTOF 按实验编号过滤提交: %s", uplc_qtof_exp_nums) + + try: + client.connect() + submit_path = str(saved_paths[0]) + result = client.start_with_csv_file(string=submit_path) + finally: + client.close() + + self._logger.info("UPLC_QTOF 提交结果: %s", result) + return result + + def submit_uplc_qtof(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + UPLC_QTOF 分析任务完整提交流程(公开入口, 独立调用时使用): + 1. 定位任务目录(task_id 或最新). + 2. 检查 task_info.json 状态, 非 COMPLETED 时 warning 后继续. + 3. 解析 xlsx, 若无 uplc_qtof_method 则跳过提交. + 4. 生成分析 CSV 并双路保存. + 5. ZhidaClient 连接 UPLC_QTOF 并调用 start_with_csv_file 提交. + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + 返回: + Dict: {"success": bool, "return_info": str}. + """ + try: + task_dir, resolved_id = self._find_task_dir(task_id) + self._check_task_status(task_dir) + task_info = self._parse_task_xlsx(task_dir, resolved_id) + return self._do_submit_uplc_qtof(resolved_id, task_info) + except Exception as exc: + msg = f"UPLC_QTOF 提交失败: {exc}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + + def submit_by_csv_path(self, instrument: str, csv_file_path: str) -> Dict: + """ + 功能: + 按指定仪器和 CSV 文件路径直接提交分析任务. + 支持 gc_ms, uplc_qtof, hplc 三种仪器. + 参数: + instrument: 仪器标识, 可选值为 gc_ms/uplc_qtof/hplc. + csv_file_path: CSV 文件路径. + 返回: + Dict: {"success": bool, "return_info": str}. + """ + instrument_key = instrument.strip().lower() + instrument_configs = { + "gc_ms": ("GC_MS", self._settings.gc_ms_host, self._settings.gc_ms_port, self._settings.gc_ms_timeout), + "uplc_qtof": ( + "UPLC_QTOF", + self._settings.uplc_qtof_host, + self._settings.uplc_qtof_port, + self._settings.uplc_qtof_timeout, + ), + "hplc": ("HPLC", self._settings.hplc_host, self._settings.hplc_port, self._settings.hplc_timeout), + } + + if instrument_key not in instrument_configs: + msg = "仪器参数无效, 可选值: gc_ms, uplc_qtof, hplc." + self._logger.error(msg) + return {"success": False, "return_info": msg} + + csv_path = Path(csv_file_path) + if csv_path.exists() is False or csv_path.is_file() is False: + msg = f"CSV 文件不存在或不是文件: {csv_file_path}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + + instrument_name, host, port, timeout = instrument_configs[instrument_key] + client = ZhidaClient(host=host, port=port, timeout=timeout) + self._logger.info( + "按路径提交 CSV, 仪器=%s, 地址=%s:%d, 文件=%s", + instrument_name, host, port, csv_path, + ) + + try: + client.connect() + result = client.start_with_csv_file(string=str(csv_path)) + self._logger.info("%s 手工 CSV 提交结果: %s", instrument_name, result) + return result + except Exception as exc: + msg = f"{instrument_name} CSV 提交失败: {exc}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + finally: + client.close() + + # ------------------------------------------------------------------ + # 统一分析入口 + # ------------------------------------------------------------------ + + def run_analysis(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + 统一分析入口, 依据 xlsx 中各仪器方法配置依次处理. + xlsx 仅解析一次, 各仪器提交直接调用内部核心方法避免重复解析. + 支持方法写法 `Method(1-8,9)`, 仅提交括号中实验编号对应样品. + 当前已实现: GC_MS, UPLC_QTOF. + 预留未实现: HPLC 自动提交流程(方法存在时发出 warning). + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + 返回: + Dict: {"gc_ms": result_dict, "uplc_qtof": result_dict, "hplc": result_dict}. + """ + results: Dict = {} + + try: + # 定位任务目录并解析 xlsx(仅执行一次) + task_dir, resolved_id = self._find_task_dir(task_id) + self._check_task_status(task_dir) + task_info = self._parse_task_xlsx(task_dir, resolved_id) + except Exception as exc: + msg = f"任务初始化失败: {exc}" + self._logger.error(msg) + return {"error": msg} + + # ---------- GC_MS ---------- + if task_info["gc_ms_method"] is not None: + self._logger.info("开始提交 GC_MS 分析任务...") + try: + # 直接调用核心方法,跳过重复的定位+解析步骤 + results["gc_ms"] = self._do_submit_gc_ms(resolved_id, task_info) + except Exception as exc: + results["gc_ms"] = {"success": False, "return_info": f"GC_MS 提交失败: {exc}"} + else: + results["gc_ms"] = {"success": True, "return_info": "未配置 GC_MS 方法, 已跳过."} + + # ---------- UPLC_QTOF ---------- + if task_info["uplc_qtof_method"] is not None: + self._logger.info("开始提交 UPLC_QTOF 分析任务...") + try: + results["uplc_qtof"] = self._do_submit_uplc_qtof(resolved_id, task_info) + except Exception as exc: + results["uplc_qtof"] = {"success": False, "return_info": f"UPLC_QTOF 提交失败: {exc}"} + else: + results["uplc_qtof"] = {"success": True, "return_info": "未配置 UPLC_QTOF 方法, 已跳过."} + + # ---------- HPLC(预留) ---------- + if task_info["hplc_method"] is not None: + self._logger.warning( + "任务 %s 配置了 HPLC 方法 [%s], 但 HPLC 接入尚未实现, 已跳过.", + resolved_id, task_info["hplc_method"] + ) + results["hplc"] = {"success": False, "return_info": "HPLC 接入尚未实现."} + else: + results["hplc"] = {"success": True, "return_info": "未配置 HPLC 方法, 已跳过."} + + self._logger.info("分析任务提交完毕, 结果: %s", results) + return results + + # ------------------------------------------------------------------ + # GC_MS 结果处理(积分 + 定性 + 报告) + # ------------------------------------------------------------------ + + def _enumerate_d_dirs(self, task_id: str) -> List[Path]: + """ + 功能: + 枚举指定任务下所有 .D 结果目录. + 先在仪器侧网络目录查找, 再在本地 data 目录查找. + 参数: + task_id: 任务 ID. + 返回: + List[Path]: 找到的 .D 目录列表, 按样品编号排序. + """ + d_dirs: List[Path] = [] + + # 优先查找仪器侧网络目录 + remote_data_dir = self._settings.gc_ms_data_dir + if remote_data_dir.exists(): + # 匹配 -.D 格式 + for d_dir in sorted(remote_data_dir.glob(f"{task_id}-*.D"), key=_natural_sort_key): + if d_dir.is_dir(): + d_dirs.append(d_dir) + + if d_dirs: + self._logger.info("从仪器侧目录找到 %d 个 .D 文件: %s", len(d_dirs), remote_data_dir) + return d_dirs + + # 备选: 在本地 data// 目录下查找 + local_data_dir = self._settings.data_dir / task_id + if local_data_dir.exists(): + for d_dir in sorted(local_data_dir.glob("*.D"), key=_natural_sort_key): + if d_dir.is_dir(): + d_dirs.append(d_dir) + + if d_dirs: + self._logger.info("从本地目录找到 %d 个 .D 文件: %s", len(d_dirs), local_data_dir) + else: + self._logger.warning("未找到任务 %s 的 .D 结果目录", task_id) + + return d_dirs + + def _load_expected_samples(self, task_id: str) -> List[str]: + """ + 功能: + 从本地数据目录的 gc_ms.csv 读取预期样品列表, 按 CSV 行顺序返回. + CSV 由 _save_csv 生成, 格式为: + SampleName,AcqMethod,RackCode,VialPos,SmplInjVol,OutputFile. + 参数: + task_id: 任务 ID 字符串. + 返回: + List[str]: 样品名称列表, 如 ["725-1", "725-2", ..., "725-12"]. + """ + csv_path = self._settings.data_dir / task_id / "gc_ms.csv" + if not csv_path.exists(): + raise FileNotFoundError(f"未找到样品列表文件: {csv_path}") + + with csv_path.open("r", encoding="utf-8") as f: + reader = csv.DictReader(f) + samples = [row["SampleName"] for row in reader] + + if not samples: + raise ValueError(f"样品列表为空: {csv_path}") + + self._logger.info("从 gc_ms.csv 加载 %d 个预期样品", len(samples)) + return samples + + def _read_run_completed_flag(self, d_dir: Path) -> Optional[bool]: + """ + 功能: + 读取 .D 目录下 AcqData/sample_info.xml 中的 RunCompletedFlag 字段. + 解析 XML 中所有 元素, 找到 Name 为 "RunCompletedFlag" 的条目, + 返回其 Value 的布尔解析结果. + 参数: + d_dir: .D 目录路径. + 返回: + Optional[bool]: True 表示采集完成, False 表示采集中, + None 表示文件不存在或解析失败. + """ + info_path = d_dir / "AcqData" / "sample_info.xml" + if not info_path.exists(): + return None + + try: + tree = ET.parse(str(info_path)) + root = tree.getroot() + for field_elem in root.findall("Field"): + name = field_elem.findtext("Name", "") + if name == "RunCompletedFlag": + value = field_elem.findtext("Value", "").strip() + return value.lower() == "true" + # RunCompletedFlag 字段不存在, 视为未完成 + return False + except (ET.ParseError, OSError) as exc: + # 文件可能正在被仪器写入, 视为采集中 + self._logger.debug("解析 %s 失败: %s, 视为采集中", info_path, exc) + return None + + def _filter_peaks( + self, + peaks: List[PeakResult], + area_min: Optional[float] = None, + area_max: Optional[float] = None, + ) -> List[PeakResult]: + """ + 功能: + 按保留时间范围和面积阈值过滤峰列表, 过滤后重新计算面积百分比. + 参数: + peaks: 积分后的峰列表. + area_min: 峰面积下限, None 表示不过滤. + area_max: 峰面积上限, None 表示不过滤. + 返回: + List[PeakResult]: 过滤后的峰列表. + """ + filtered = peaks + + # 保留时间范围过滤 (TIC/FID 共用) + if self._settings.peak_rt_min is not None: + filtered = [p for p in filtered if p.retention_time >= self._settings.peak_rt_min] + if self._settings.peak_rt_max is not None: + filtered = [p for p in filtered if p.retention_time <= self._settings.peak_rt_max] + + # 面积范围过滤 (TIC/FID 分别传入不同阈值) + if area_min is not None: + filtered = [p for p in filtered if p.area >= area_min] + if area_max is not None: + filtered = [p for p in filtered if p.area <= area_max] + + # 重新计算面积百分比 + if len(filtered) < len(peaks): + total_area = sum(p.area for p in filtered) + if total_area > 0: + for p in filtered: + p.area_percent = (p.area / total_area) * 100.0 + self._logger.info("峰过滤: %d -> %d 个峰", len(peaks), len(filtered)) + + return filtered + + def _process_single_sample( + self, d_dir: Path, nist: NISTMatcher, report_dir: Optional[Path] = None, + mspepsearch_predictor: Optional[MSPepSearchPredictor] = None, + ) -> SampleResult: + """ + 功能: + 处理单个 .D 目录: 读取 TIC/FID, 积分, NIST 匹配, PIM/SS-HM/iHS-HM 预测, 生成色谱图. + 参数: + d_dir: .D 目录路径. + nist: NISTMatcher 实例 (由外部传入, 保持状态复用). + report_dir: 报告输出目录, 用于保存色谱图. None 则不生成图. + mspepsearch_predictor: MSPepSearchPredictor 实例, None 则跳过 SS-HM/iHS-HM 预测. + 返回: + SampleResult: 该样品的完整积分结果. + """ + reader = GCMSDataReader() + + # 读取样品元数据 + sample_info = reader.read_sample_info(d_dir) + sample_name = sample_info.get("sample_name") or d_dir.stem # 空值回退到目录名 + acq_time = sample_info.get("acq_time", "") + + result = SampleResult( + sample_name=sample_name, + d_dir=d_dir, + acq_time=acq_time, + ) + + # 缓存色谱数据, 供后续绘图复用 + tic_times = tic_intensities = None + fid_times = fid_intensities = None + tic_baseline = fid_baseline = None # 积分基线, 供绘图使用 + peak_ms_cache: Dict[int, Tuple] = {} + + pim_predictor: Optional[PIMPredictor] = None + if self._settings.pim_enable is True: + try: + pim_predictor = PIMPredictor( + ab_m=self._settings.pim_ab_m, + beta=self._settings.pim_beta, + epsilon_f=self._settings.pim_epsilon_f, + ) + except Exception as e: + self._logger.error("样品 %s PIM 预测器初始化失败: %s", sample_name, e) + + # TIC 积分 + try: + tic_times, tic_intensities = reader.read_tic(d_dir) + tic_integrator = PeakIntegrator( + smoothing_window=self._settings.peak_smoothing_window, + prominence=self._settings.peak_prominence, + min_distance=self._settings.peak_min_distance, + width_rel_height=self._settings.peak_width_rel_height, + use_als_baseline=self._settings.use_als_baseline, + als_lambda=self._settings.als_lambda, + als_p=self._settings.als_p, + use_valley_boundary=self._settings.use_valley_boundary, + integration_mode=self._settings.integration_mode, + baseline_method=self._settings.baseline_method, + baseline_quantile=self._settings.baseline_quantile, + baseline_window_min=self._settings.baseline_window_min, + boundary_sigma_factor=self._settings.boundary_sigma_factor, + boundary_edge_ratio=self._settings.boundary_edge_ratio, + boundary_expand_factor=self._settings.boundary_expand_factor, + boundary_min_span_min=self._settings.boundary_min_span_min, + boundary_max_span_min=self._settings.boundary_max_span_min, + shoulder_filter_enable=self._settings.robust_v3_shoulder_filter_enable, + shoulder_filter_width_max_min=self._settings.robust_v3_shoulder_width_max_min, + shoulder_filter_gap_max_min=self._settings.robust_v3_shoulder_gap_max_min, + shoulder_filter_relative_prominence_max=self._settings.robust_v3_shoulder_relative_prominence_max, + tail_artifact_filter_enable=self._settings.robust_v3_tail_artifact_filter_enable, + tail_artifact_gap_max_min=self._settings.robust_v3_tail_artifact_gap_max_min, + tail_artifact_relative_prominence_max=self._settings.robust_v3_tail_artifact_relative_prominence_max, + tail_artifact_half_width_asymmetry_min=self._settings.robust_v3_tail_artifact_half_width_asymmetry_min, + tail_monotonic_filter_enable=self._settings.robust_v3_tail_monotonic_filter_enable, + tail_monotonic_ratio_max=self._settings.robust_v3_tail_monotonic_ratio_max, + max_peak_width_min=self._settings.robust_v3_max_peak_width_min, + leading_edge_filter_enable=self._settings.robust_v3_leading_edge_filter_enable, + leading_edge_relative_prominence_max=self._settings.robust_v3_leading_edge_relative_prominence_max, + leading_edge_monotonic_ratio_min=self._settings.robust_v3_leading_edge_monotonic_ratio_min, + gcpy_whittaker_lmbd=self._settings.gcpy_whittaker_lmbd, + use_cwt_detection=self._settings.use_cwt_detection, + cwt_min_width_min=self._settings.cwt_min_width_min, + cwt_max_width_min=self._settings.cwt_max_width_min, + cwt_min_snr=self._settings.cwt_min_snr, + cwt_noise_perc=self._settings.cwt_noise_perc, + ) + result.tic_peaks = tic_integrator.integrate(tic_times, tic_intensities) + tic_baseline = tic_integrator.last_baseline + result.tic_peaks = self._filter_peaks( + result.tic_peaks, + area_min=self._settings.tic_area_min, + area_max=self._settings.tic_area_max, + ) + self._logger.info("样品 %s TIC 积分: %d 个峰", sample_name, len(result.tic_peaks)) + except Exception as e: + self._logger.error("样品 %s TIC 积分失败: %s", sample_name, e) + + # FID 积分 + try: + fid_times, fid_intensities = reader.read_fid(d_dir) + fid_integrator = PeakIntegrator( + smoothing_window=self._settings.peak_smoothing_window, + prominence=self._settings.fid_peak_prominence, + min_distance=self._settings.fid_peak_min_distance, + width_rel_height=self._settings.peak_width_rel_height, + use_als_baseline=self._settings.use_als_baseline, + als_lambda=self._settings.als_lambda, + als_p=self._settings.als_p, + use_valley_boundary=self._settings.use_valley_boundary, + integration_mode=self._settings.integration_mode, + baseline_method=self._settings.baseline_method, + baseline_quantile=self._settings.baseline_quantile, + baseline_window_min=self._settings.baseline_window_min, + boundary_sigma_factor=self._settings.boundary_sigma_factor, + boundary_edge_ratio=self._settings.boundary_edge_ratio, + boundary_expand_factor=self._settings.boundary_expand_factor, + boundary_min_span_min=self._settings.boundary_min_span_min, + boundary_max_span_min=self._settings.boundary_max_span_min, + shoulder_filter_enable=self._settings.robust_v3_shoulder_filter_enable, + shoulder_filter_width_max_min=self._settings.robust_v3_shoulder_width_max_min, + shoulder_filter_gap_max_min=self._settings.robust_v3_shoulder_gap_max_min, + shoulder_filter_relative_prominence_max=self._settings.robust_v3_shoulder_relative_prominence_max, + tail_artifact_filter_enable=self._settings.robust_v3_tail_artifact_filter_enable, + tail_artifact_gap_max_min=self._settings.robust_v3_tail_artifact_gap_max_min, + tail_artifact_relative_prominence_max=self._settings.robust_v3_tail_artifact_relative_prominence_max, + tail_artifact_half_width_asymmetry_min=self._settings.robust_v3_tail_artifact_half_width_asymmetry_min, + tail_monotonic_filter_enable=self._settings.robust_v3_tail_monotonic_filter_enable, + tail_monotonic_ratio_max=self._settings.robust_v3_tail_monotonic_ratio_max, + max_peak_width_min=self._settings.robust_v3_max_peak_width_min, + leading_edge_filter_enable=self._settings.robust_v3_leading_edge_filter_enable, + leading_edge_relative_prominence_max=self._settings.robust_v3_leading_edge_relative_prominence_max, + leading_edge_monotonic_ratio_min=self._settings.robust_v3_leading_edge_monotonic_ratio_min, + gcpy_whittaker_lmbd=self._settings.gcpy_whittaker_lmbd, + use_cwt_detection=self._settings.use_cwt_detection, + cwt_min_width_min=self._settings.cwt_min_width_min, + cwt_max_width_min=self._settings.cwt_max_width_min, + cwt_min_snr=self._settings.cwt_min_snr, + cwt_noise_perc=self._settings.cwt_noise_perc, + ) + result.fid_peaks = fid_integrator.integrate(fid_times, fid_intensities) + fid_baseline = fid_integrator.last_baseline + result.fid_peaks = self._filter_peaks( + result.fid_peaks, + area_min=self._settings.fid_area_min, + area_max=self._settings.fid_area_max, + ) + self._logger.info("样品 %s FID 积分: %d 个峰", sample_name, len(result.fid_peaks)) + except Exception as e: + self._logger.error("样品 %s FID 积分失败: %s", sample_name, e) + + # NIST 化合物匹配 (优先使用 NIST MS Search 自动化) + try: + if nist.nist_available and result.tic_peaks: + # 提取各 TIC 峰的质谱并通过 NIST 搜索匹配 + result.compound_matches = nist.match_peaks_with_nist( + d_dir, result.tic_peaks, reader, + avg_scans=self._settings.nist_avg_scans, + ) + if result.compound_matches: + self._logger.info( + "样品 %s NIST 自动匹配: %d 个峰有匹配结果", + sample_name, len(result.compound_matches) + ) + # 记录 NIST 结果文件副本路径 + if hasattr(nist, '_saved_srcreslt') and nist._saved_srcreslt is not None: + result.nist_result_path = nist._saved_srcreslt + else: + # 降级: 从 MassHunter 已有报告中提取 + result.compound_matches = nist.match_from_qual_results(d_dir) + + if not result.compound_matches: + self._logger.info("样品 %s 无可用的 NIST 定性结果", sample_name) + except Exception as e: + self._logger.error("样品 %s NIST 匹配失败: %s", sample_name, e) + + # 提取 TIC 峰质谱并执行 PIM 预测 + if len(result.tic_peaks) > 0: + for peak_num, peak in enumerate(result.tic_peaks, start=1): + try: + mz_values, ms_intensities = reader.read_ms_spectra_at_peak( + d_dir, peak.start_time, peak.end_time, + avg_scans=self._settings.nist_avg_scans, + ) + peak_ms_cache[peak_num] = (mz_values, ms_intensities) + except Exception as e: + self._logger.error("样品 %s 峰%d 质谱提取失败: %s", sample_name, peak_num, e) + if pim_predictor is not None: + result.pim_predictions[peak.retention_time] = PIMPrediction( + status="error", + message=f"质谱提取失败: {e}", + ) + continue + + if pim_predictor is None: + continue + + try: + prediction = pim_predictor.predict(mz_values, ms_intensities) + result.pim_predictions[peak.retention_time] = prediction + except Exception as e: + self._logger.error("样品 %s 峰%d PIM 预测失败: %s", sample_name, peak_num, e) + result.pim_predictions[peak.retention_time] = PIMPrediction( + status="error", + message=f"PIM 预测失败: {e}", + ) + + # SS-HM / iHS-HM 预测 (复用已缓存的峰质谱) + if mspepsearch_predictor is not None and mspepsearch_predictor.available: + enable_sshm_search = self._settings.process_gc_ms_enable_sshm_search + enable_ihshm_search = self._settings.process_gc_ms_enable_ihshm_search + for peak_num, peak in enumerate(result.tic_peaks, start=1): + cached_spectrum = peak_ms_cache.get(peak_num) + if cached_spectrum is None: + continue + + mz_vals, ms_ints = cached_spectrum + + # SS-HM + if enable_sshm_search is True: + try: + sshm_pred = mspepsearch_predictor.predict_sshm(mz_vals, ms_ints) + result.sshm_predictions[peak.retention_time] = sshm_pred + except Exception as e: + self._logger.error("样品 %s 峰%d SS-HM 预测失败: %s", sample_name, peak_num, e) + result.sshm_predictions[peak.retention_time] = SSHMPrediction( + status="error", message=f"SS-HM 预测失败: {e}", + ) + + # iHS-HM + if enable_ihshm_search is True: + try: + ihshm_pred = mspepsearch_predictor.predict_ihshm(mz_vals, ms_ints) + result.ihshm_predictions[peak.retention_time] = ihshm_pred + except Exception as e: + self._logger.error("样品 %s 峰%d iHS-HM 预测失败: %s", sample_name, peak_num, e) + result.ihshm_predictions[peak.retention_time] = iHSHMPrediction( + status="error", message=f"iHS-HM 预测失败: {e}", + ) + + # 生成色谱图 (TIC + FID) + if report_dir is not None: + plotter = ChromatogramPlotter( + chromatogram_ppi=self._settings.chromatogram_plot_ppi, + ms_spectrum_ppi=self._settings.ms_spectrum_plot_ppi, + ) + plot_dir = report_dir / "plots" + + # 根据积分模式决定填充基线方式, 使绘图区域与实际积分一致 + mode = self._settings.integration_mode.strip().lower() + if mode == "gcpy" or mode == "robust_v3": + fill_mode = "global" + elif mode == "legacy" and self._settings.use_als_baseline is True: + fill_mode = "global" + else: + # legacy 无 ALS 时, 积分使用局部端点连线. + fill_mode = "local" + + # TIC 色谱图 + if tic_times is not None and result.tic_peaks: + try: + tic_plot = plot_dir / f"{sample_name}_tic.png" + result.tic_plot_path = plotter.plot_chromatogram( + tic_times, tic_intensities, result.tic_peaks, + compound_matches=(result.compound_matches or None) if self._settings.tic_plot_show_compound else None, + title=f"TIC Chromatogram - {sample_name}", + ylabel="TIC Intensity", + output_path=tic_plot, + rt_min=self._settings.peak_rt_min, + rt_max=self._settings.peak_rt_max, + baseline=tic_baseline, + fill_baseline_mode=fill_mode, + ) + except Exception as e: + self._logger.error("样品 %s TIC 色谱图生成失败: %s", sample_name, e) + + # FID 色谱图 + if fid_times is not None and result.fid_peaks: + try: + fid_plot = plot_dir / f"{sample_name}_fid.png" + result.fid_plot_path = plotter.plot_chromatogram( + fid_times, fid_intensities, result.fid_peaks, + compound_matches=None, # FID 不标注化合物 + title=f"FID Chromatogram - {sample_name}", + ylabel="FID Signal", + output_path=fid_plot, + rt_min=self._settings.peak_rt_min, + rt_max=self._settings.peak_rt_max, + y_range_min=100, # FID 信号较小, 确保 Y 轴最小范围 + baseline=fid_baseline, + fill_baseline_mode=fill_mode, + ) + except Exception as e: + self._logger.error("样品 %s FID 色谱图生成失败: %s", sample_name, e) + + # 各 TIC 峰的质谱图 + if result.tic_peaks: + ms_plot_dir = report_dir / "ms_plots" + for peak_num, peak in enumerate(result.tic_peaks, start=1): + try: + cached_spectrum = peak_ms_cache.get(peak_num) + if cached_spectrum is None: + mz, ms_intensities = reader.read_ms_spectra_at_peak( + d_dir, peak.start_time, peak.end_time, + avg_scans=self._settings.nist_avg_scans, + ) + else: + mz, ms_intensities = cached_spectrum + if len(mz) > 0: + ms_plot_path = ms_plot_dir / f"{sample_name}_peak{peak_num}_ms.png" + plotter.plot_ms_spectrum( + mz, ms_intensities, + title=f"Mass Spectrum - {sample_name} Peak {peak_num} (RT {peak.retention_time:.2f} min)", + output_path=ms_plot_path, + ) + result.ms_plot_paths[peak_num] = ms_plot_path + except Exception as e: + self._logger.error( + "样品 %s 峰 %d 质谱图生成失败: %s", sample_name, peak_num, e + ) + + return result + + def process_gc_ms_results(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + GC-MS 运行完成后的结果处理入口: + 1. 定位任务目录, 找到所有 .D 结果文件. + 2. 逐个读取 TIC/FID 数据并积分. + 3. 读取/匹配 NIST 定性结果. + 4. 按任务汇总生成 Excel 报告. + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + 返回: + Dict: {"success": bool, "return_info": str, "report_path": str}. + """ + try: + # 定位任务(取 resolved_id, task_dir 不直接使用) + _, resolved_id = self._find_task_dir(task_id) + self._logger.info("开始处理任务 %s 的 GC-MS 结果", resolved_id) + + # 枚举 .D 目录 + d_dirs = self._enumerate_d_dirs(resolved_id) + if not d_dirs: + return { + "success": False, + "return_info": f"任务 {resolved_id} 未找到 .D 结果目录", + } + + # 初始化 NIST 匹配器 (复用同一实例) + nist = NISTMatcher( + nist_path=self._settings.nist_path, + max_hits=self._settings.nist_max_hits, + search_timeout=self._settings.nist_search_timeout, + ) + + # 本地报告目录 (色谱图也保存在此) + local_report_dir = self._settings.report_dir / resolved_id + + # 初始化 MSPepSearch 预测器 (SS-HM / iHS-HM) + mspepsearch_predictor: Optional[MSPepSearchPredictor] = None + if ( + self._settings.mspepsearch_enable is True + and ( + self._settings.process_gc_ms_enable_sshm_search is True + or self._settings.process_gc_ms_enable_ihshm_search is True + ) + ): + try: + # PIMPredictor 供 MSPepSearchPredictor 内部复用 + pim_for_mspep = PIMPredictor( + ab_m=self._settings.pim_ab_m, + beta=self._settings.pim_beta, + epsilon_f=self._settings.pim_epsilon_f, + ) + # 库谱读取器 (SS-HM 完整版需要, 不可用时自动降级) + nist_lib_reader = NistLibraryReader(self._settings.nist_mainlib_msp) + + mspepsearch_predictor = MSPepSearchPredictor( + mspepsearch_exe=self._settings.mspepsearch_exe, + lib_path=self._settings.mspepsearch_lib_path, + lib_type=self._settings.mspepsearch_lib_type, + pim_predictor=pim_for_mspep, + nist_lib_reader=nist_lib_reader, + work_dir=local_report_dir, + sshm_hits=self._settings.sshm_hits, + sshm_b_ss=self._settings.sshm_b_ss, + ihshm_hits=self._settings.ihshm_hits, + ihshm_mEMF=self._settings.ihshm_mEMF, + timeout=self._settings.mspepsearch_timeout, + ) + if mspepsearch_predictor.available: + self._logger.info("MSPepSearch 预测器初始化成功") + else: + self._logger.warning("MSPepSearch 预测器不可用, 将跳过 SS-HM/iHS-HM 预测") + except Exception as exc: + self._logger.warning("MSPepSearch 预测器初始化失败, 将跳过 SS-HM/iHS-HM: %s", exc) + mspepsearch_predictor = None + else: + self._logger.info( + "已跳过 SS-HM/iHS-HM 预测, mspepsearch_enable=%s, process_gc_ms_enable_sshm_search=%s, process_gc_ms_enable_ihshm_search=%s", + self._settings.mspepsearch_enable, + self._settings.process_gc_ms_enable_sshm_search, + self._settings.process_gc_ms_enable_ihshm_search, + ) + + # 逐样品处理 + sample_results: List[SampleResult] = [] + for d_dir in d_dirs: + self._logger.info("处理样品: %s", d_dir.name) + sr = self._process_single_sample( + d_dir, nist, report_dir=local_report_dir, + mspepsearch_predictor=mspepsearch_predictor, + ) + sample_results.append(sr) + + # 收集所有命中项并按本地索引批量获取结构图. + structure_images = {} + all_matches = [] + for sr in sample_results: + for match_list in sr.compound_matches.values(): + for m in match_list: + all_matches.append(m) + + if all_matches: + try: + fetcher = NistLocalStructureFetcher( + task_cache_dir=local_report_dir / "structures", + seed_msp_path=self._settings.nist_structure_seed_msp, + seed_mol_dir=self._settings.nist_structure_seed_mol_dir, + runtime_cache_path=self._settings.nist_structure_runtime_cache_path, + offline_only=self._settings.structure_offline_only, + global_cache_dir=self._settings.structure_cache_dir, + image_ppi=self._settings.structure_image_ppi, + image_size=self._settings.structure_image_size, + ) + structure_images = fetcher.fetch_batch_from_matches(all_matches) + except Exception as e: + self._logger.warning("化合物结构图获取失败, 报告将不含结构图: %s", e) + + # 生成 Excel 报告 + generator = ReportGenerator() + + # 确定各预测方法是否启用, 控制报告中对应列的显示 + pim_enabled = self._settings.pim_enable + sshm_enabled = ( + self._settings.mspepsearch_enable is True + and self._settings.process_gc_ms_enable_sshm_search is True + ) + ihshm_enabled = ( + self._settings.mspepsearch_enable is True + and self._settings.process_gc_ms_enable_ihshm_search is True + ) + + # 保存到本地数据目录 + report_path = generator.generate_task_report( + resolved_id, sample_results, local_report_dir, + structure_images=structure_images, + nist_max_hits=self._settings.nist_max_hits, + alignment_tolerance=self._settings.alignment_tolerance, + include_tic_only=self._settings.alignment_include_tic_only, + include_fid_only=self._settings.alignment_include_fid_only, + pim_enabled=pim_enabled, + sshm_enabled=sshm_enabled, + ihshm_enabled=ihshm_enabled, + ) + + # 同步到合成任务目录 + syn_dir = self._settings.synthesis_tasks_dir / resolved_id + if syn_dir.is_dir(): + syn_report = generator.generate_task_report( + resolved_id, sample_results, syn_dir, + structure_images=structure_images, + nist_max_hits=self._settings.nist_max_hits, + alignment_tolerance=self._settings.alignment_tolerance, + include_tic_only=self._settings.alignment_include_tic_only, + include_fid_only=self._settings.alignment_include_fid_only, + pim_enabled=pim_enabled, + sshm_enabled=sshm_enabled, + ihshm_enabled=ihshm_enabled, + ) + self._logger.info("报告已同步至合成任务目录: %s", syn_report) + + # 统计摘要 + total_tic_peaks = sum(len(sr.tic_peaks) for sr in sample_results) + total_fid_peaks = sum(len(sr.fid_peaks) for sr in sample_results) + msg = ( + f"任务 {resolved_id} 结果处理完成: " + f"{len(sample_results)} 个样品, " + f"TIC 共 {total_tic_peaks} 个峰, " + f"FID 共 {total_fid_peaks} 个峰, " + f"报告: {report_path}" + ) + self._logger.info(msg) + + # 尝试自动触发产率计算 + self._try_auto_yield_calculation(resolved_id) + + return { + "success": True, + "return_info": msg, + "report_path": str(report_path), + } + + except Exception as exc: + msg = f"结果处理失败: {exc}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + + def calculate_yields(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + 产率计算入口, 定位实验方案/积分报告/化学品清单后调用 YieldCalculator. + 实验方案中需包含 "GC产率计算" Sheet, 否则跳过. + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + 返回: + Dict: {"success": bool, "return_info": str, "report_path": str}. + """ + try: + _, resolved_id = self._find_task_dir(task_id) + self._logger.info("开始产率计算, 任务: %s", resolved_id) + + # 定位文件 + syn_dir = self._settings.synthesis_tasks_dir / resolved_id + plan_path = syn_dir / f"{resolved_id}_experiment_plan.xlsx" + report_path = syn_dir / f"{resolved_id}_integration_report.xlsx" + chemical_list_path = self._settings.chemical_list_path + + if not plan_path.exists(): + return {"success": False, "return_info": f"未找到实验方案: {plan_path}"} + if not report_path.exists(): + # 尝试本地报告目录 + local_report = self._settings.report_dir / resolved_id / f"{resolved_id}_integration_report.xlsx" + legacy_local_report = self._settings.report_dir / resolved_id / f"integration_report_{resolved_id}.xlsx" + if local_report.exists(): + report_path = local_report + elif legacy_local_report.exists(): + report_path = legacy_local_report + else: + return {"success": False, "return_info": f"未找到积分报告: {report_path}"} + if not chemical_list_path.exists(): + return {"success": False, "return_info": f"未找到化学品清单: {chemical_list_path}"} + + # 检查实验方案是否包含 "GC产率计算" Sheet + wb_check = openpyxl.load_workbook(str(plan_path), data_only=True) + has_yield_sheet = YIELD_CONFIG_SHEET_NAME in wb_check.sheetnames + wb_check.close() + if not has_yield_sheet: + return { + "success": False, + "return_info": f"实验方案中未包含 '{YIELD_CONFIG_SHEET_NAME}' Sheet, 跳过产率计算", + } + + # 执行产率计算 + sshm_enabled = ( + self._settings.mspepsearch_enable is True + and self._settings.process_gc_ms_enable_sshm_search is True + ) + ihshm_enabled = ( + self._settings.mspepsearch_enable is True + and self._settings.process_gc_ms_enable_ihshm_search is True + ) + calc = YieldCalculator( + rt_tolerance=self._settings.yield_rt_tolerance, + nist_mainlib_msp_path=self._settings.nist_mainlib_msp, + pim_enabled=self._settings.pim_enable, + sshm_enabled=sshm_enabled, + ihshm_enabled=ihshm_enabled, + ) + config, results = calc.process_task(plan_path, report_path, chemical_list_path) + + # 按自然顺序排序 (729-1, 729-2, ..., 729-10 而非字典序 729-1, 729-10, 729-2) + results.sort(key=lambda r: [ + int(p) if p.isdigit() else p.lower() + for p in re.split(r'(\d+)', r.sample_name) + ]) + + # 保存到合成任务目录 + yield_path = calc.generate_yield_report(resolved_id, config, results, syn_dir) + + # 同步到本地分析数据目录 + local_report_dir = self._settings.report_dir / resolved_id + if local_report_dir != syn_dir: + calc.generate_yield_report(resolved_id, config, results, local_report_dir) + + # 统计 + valid_count = sum(1 for r in results if r.yield_percent is not None) + msg = f"任务 {resolved_id} 产率计算完成: {len(results)} 条结果, 其中 {valid_count} 条有效产率" + self._logger.info(msg) + + return { + "success": True, + "return_info": msg, + "report_path": str(yield_path), + } + + except Exception as exc: + msg = f"产率计算失败: {exc}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + + def _try_auto_yield_calculation(self, resolved_id: str) -> None: + """ + 功能: + 在积分报告生成后尝试自动触发产率计算. + 仅当实验方案包含 "GC产率计算" Sheet 时执行, 失败不影响积分报告. + 参数: + resolved_id: 任务 ID 字符串. + 返回: + 无. + """ + try: + result = self.calculate_yields(resolved_id) + if result["success"]: + self._logger.info("自动产率计算成功: %s", result["return_info"]) + else: + self._logger.info("自动产率计算跳过: %s", result["return_info"]) + except Exception as exc: + self._logger.warning("自动产率计算失败(不影响积分报告): %s", exc) + + def poll_analysis_run( + self, task_id: Optional[str] = None, poll_interval: float = 30.0 + ) -> Dict: + """ + 功能: + 基于文件监控的 GC-MS 分析任务轮询. + 1. 从 gc_ms.csv 读取预期样品列表, 确定总样品数和采集顺序. + 2. 循环检查数据目录中 .D 目录的出现情况. + 3. 对每个 .D 目录, 解析 AcqData/sample_info.xml 中的 + RunCompletedFlag 判断采集状态: + - .D 目录不存在 -> "等待进样" + - .D 存在但 RunCompletedFlag 非 True -> "采集中" + - RunCompletedFlag 为 True -> "采集结束" + 4. 实时反馈每个样品的状态变更和整体进度. + 5. 当所有样品均为 "采集结束" 时, 调用 process_gc_ms_results 处理结果. + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + poll_interval: 轮询间隔(秒), 默认 30 秒. + 返回: + Dict: process_gc_ms_results 的返回值, 包含 success/return_info/report_path. + """ + # 解析任务 ID + _, resolved_id = self._find_task_dir(task_id) + + # 从 gc_ms.csv 加载预期样品列表(按表格顺序) + try: + expected_samples = self._load_expected_samples(resolved_id) + except (FileNotFoundError, ValueError) as exc: + msg = f"加载预期样品列表失败: {exc}" + self._logger.error(msg) + return {"success": False, "return_info": msg} + + total = len(expected_samples) + + # 确定 .D 目录搜索路径: 优先远程仪器目录, 回退到本地数据目录 + remote_data_dir = self._settings.gc_ms_data_dir + local_data_dir = self._settings.data_dir / resolved_id + if remote_data_dir.exists(): + search_dir = remote_data_dir + else: + self._logger.warning( + "远程数据目录不可达: %s, 回退到本地目录: %s", + remote_data_dir, local_data_dir + ) + search_dir = local_data_dir + + self._logger.info( + "开始文件监控轮询, 任务 %s, 共 %d 个样品, 间隔 %.0f 秒, 监控目录: %s", + resolved_id, total, poll_interval, search_dir + ) + + # 记录每个样品的上次状态, 用于检测状态变更 + prev_status: Dict[str, str] = {name: "" for name in expected_samples} + prev_completed = -1 # -1 保证第一轮必然输出进度 + + try: + while True: + completed_count = 0 + + for idx, sample_name in enumerate(expected_samples, start=1): + d_dir = search_dir / f"{sample_name}.D" + + # 判断当前样品采集状态 + if not d_dir.exists(): + status = "等待进样" + else: + flag = self._read_run_completed_flag(d_dir) + if flag is True: + status = "采集结束" + else: + status = "采集中" + + # 状态变更时输出日志 + if status != prev_status[sample_name]: + self._logger.info( + "[%d/%d] 样品 %s: %s -> %s", + idx, total, sample_name, + prev_status[sample_name] or "(初始)", status + ) + prev_status[sample_name] = status + + if status == "采集结束": + completed_count += 1 + + # 进度有变化时才输出, 避免无变化时重复刷屏 + if completed_count != prev_completed: + self._logger.info( + "轮询进度: %d/%d 样品已完成采集", completed_count, total + ) + prev_completed = completed_count + + # 全部采集完成, 进入结果处理 + if completed_count == total: + self._logger.info( + "任务 %s 全部 %d 个样品采集完成, 开始处理结果...", + resolved_id, total + ) + return self.process_gc_ms_results(resolved_id) + + time.sleep(poll_interval) + + except KeyboardInterrupt: + self._logger.info("轮询被用户中断") + return {"success": False, "return_info": "轮询被用户中断"} + + # ------------------------------------------------------------------ + # 实验数据归档 + # ------------------------------------------------------------------ + + @staticmethod + def _safe_copy( + src: Path, dest: Path, logger: logging.Logger + ) -> bool: + """ + 功能: + 安全复制单个文件, 自动创建目标父目录. + 失败时记录 warning 而非抛出异常, 保证归档流程不因单个文件中断. + 参数: + src: 源文件路径. + dest: 目标文件路径. + logger: 日志记录器. + 返回: + bool: 复制是否成功. + """ + try: + dest.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src, dest) + return True + except (OSError, shutil.Error) as exc: + logger.warning("复制文件失败: %s -> %s, 原因: %s", src, dest, exc) + return False + + @staticmethod + def _select_plan_sheet(wb: openpyxl.Workbook): + """ + 功能: + 从实验方案工作簿中选取包含 '实验编号' 表头的工作表. + 优先尝试名为 '实验方案设定' 的工作表, 其次激活表, 最后遍历其余表. + 参数: + wb: openpyxl Workbook 对象. + 返回: + 命中的 Worksheet; 未命中则回退到激活表. + """ + preferred = "实验方案设定" + candidates = [] + if preferred in wb.sheetnames: + candidates.append(preferred) + active_name = wb.active.title + if active_name not in candidates: + candidates.append(active_name) + for name in wb.sheetnames: + if name not in candidates: + candidates.append(name) + + for name in candidates: + ws = wb[name] + # 在前 5 行中查找 '实验编号' 关键词 + for row in range(1, min(ws.max_row + 1, 6)): + for col in range(1, min(ws.max_column + 1, 20)): + val = ws.cell(row, col).value + if val is not None and "实验编号" in str(val): + return ws + # 未命中则回退到激活表 + return wb.active + + def _collect_experiment_plan( + self, + task_id: str, + syn_task_dir: Path, + dest_dir: Path, + ) -> Tuple[Dict[str, str], List[Dict[str, str]]]: + """ + 功能: + 从合成站任务目录复制实验方案 Excel 到归档目录的 experiment_plan/ 子目录. + 参数: + task_id: 任务 ID. + syn_task_dir: 合成站任务目录路径. + dest_dir: 归档目标根目录 ({task_id}/). + 返回: + Tuple[Dict, List]: + 第一个值为成功复制的文件清单 {"experiment_plan": "relative/path"}. + 第二个值为缺失文件记录列表. + """ + copied: Dict[str, str] = {} + missing: List[Dict[str, str]] = [] + plan_dir = dest_dir / "experiment_plan" + plan_dir.mkdir(parents=True, exist_ok=True) + + # 实验方案 Excel: 优先 {task_id}_experiment_plan.xlsx, 后备 {task_id}.xlsx + plan_name = f"{task_id}_experiment_plan.xlsx" + plan_src = syn_task_dir / plan_name + if not plan_src.exists(): + plan_name = f"{task_id}.xlsx" + plan_src = syn_task_dir / plan_name + if plan_src.exists(): + rel = f"experiment_plan/{plan_name}" + if self._safe_copy(plan_src, plan_dir / plan_name, self._logger): + copied["experiment_plan"] = rel + # 从实验方案 sheet 中提取实验名称 + try: + wb = openpyxl.load_workbook(plan_src, data_only=True) + ws = self._select_plan_sheet(wb) + # 扫描 A 列, 找到 "实验名称" 标签对应的 B 列值 + exp_name = None + for r in range(1, min(ws.max_row + 1, 10)): + label = ws.cell(r, 1).value + if label is not None and "实验名称" in str(label): + exp_name = ws.cell(r, 2).value + break + wb.close() + if exp_name is not None and str(exp_name).strip() != "": + copied["experiment_name"] = str(exp_name).strip() + except Exception: + self._logger.debug("无法从实验方案中提取实验名称") + else: + missing.append({ + "expected": f"{task_id}_experiment_plan.xlsx", + "reason": "合成站任务目录中未找到实验方案 Excel", + }) + + return copied, missing + + def _detect_instruments( + self, + analysis_data_dir: Path, + ) -> List[str]: + """ + 功能: + 检测分析站数据目录中存在哪些仪器的数据, + 通过检查对应 CSV 文件是否存在来判断. + 参数: + analysis_data_dir: 分析站本地数据目录 (data/{task_id}/). + 返回: + List[str]: 有数据的仪器标识列表, 如 ["gc_ms", "uplc_qtof"]. + """ + instruments: List[str] = [] + # 按 CSV 文件名判断仪器是否有数据 + instrument_csv_map = { + "gc_ms": "gc_ms.csv", + "uplc_qtof": "uplc_qtof.csv", + "hplc": "hplc.csv", + } + for instrument, csv_name in instrument_csv_map.items(): + if (analysis_data_dir / csv_name).exists(): + instruments.append(instrument) + self._logger.info("检测到 %s 数据: %s", instrument, csv_name) + + if not instruments: + # 若无 CSV, 通过 plots/ 目录推断是否有 GC-MS 数据 + plots_dir = analysis_data_dir / "plots" + if plots_dir.exists() and list(plots_dir.glob("*_tic.png")): + instruments.append("gc_ms") + self._logger.info("通过 plots/ 目录推断存在 GC-MS 数据") + + return instruments + + def _collect_analysis_data( + self, + task_id: str, + analysis_data_dir: Path, + dest_dir: Path, + instruments: List[str], + ) -> Tuple[Dict[str, Any], List[Dict[str, str]]]: + """ + 功能: + 按仪器收集分析数据到 analysis_data/{instrument}/ 子目录, + 包括 CSV 清单, 积分报告, 色谱图, 质谱图和结构图. + 参数: + task_id: 任务 ID. + analysis_data_dir: 分析站本地数据目录. + dest_dir: 归档目标根目录 ({task_id}/). + instruments: 有数据的仪器标识列表. + 返回: + Tuple[Dict, List]: + 第一个值为按仪器组织的归档信息, 如 {"gc_ms": {"csv": ..., "samples": [...], ...}}. + 第二个值为缺失文件记录列表. + """ + result: Dict[str, Any] = {} + missing: List[Dict[str, str]] = [] + + for instrument in instruments: + inst_dir = dest_dir / "analysis_data" / instrument + inst_dir.mkdir(parents=True, exist_ok=True) + inst_info: Dict[str, Any] = {} + + if instrument == "gc_ms": + inst_info, inst_missing = self._collect_gc_ms_analysis( + task_id, analysis_data_dir, inst_dir + ) + missing.extend(inst_missing) + elif instrument == "uplc_qtof": + inst_info, inst_missing = self._collect_uplc_qtof_analysis( + task_id, analysis_data_dir, inst_dir + ) + missing.extend(inst_missing) + elif instrument == "hplc": + inst_info, inst_missing = self._collect_hplc_analysis( + task_id, analysis_data_dir, inst_dir + ) + missing.extend(inst_missing) + + result[instrument] = inst_info + + return result, missing + + def _collect_gc_ms_analysis( + self, + task_id: str, + analysis_data_dir: Path, + inst_dir: Path, + ) -> Tuple[Dict[str, Any], List[Dict[str, str]]]: + """ + 功能: + 收集 GC-MS 仪器的分析数据: CSV 清单, 积分报告, 色谱图, 质谱图, 结构图. + 参数: + task_id: 任务 ID. + analysis_data_dir: 分析站本地数据目录. + inst_dir: 目标仪器子目录 (analysis_data/gc_ms/). + 返回: + Tuple[Dict, List]: + 第一个值为 GC-MS 归档信息字典. + 第二个值为缺失文件记录列表. + """ + info: Dict[str, Any] = {} + missing: List[Dict[str, str]] = [] + + # gc_ms.csv + csv_src = analysis_data_dir / "gc_ms.csv" + if csv_src.exists(): + if self._safe_copy(csv_src, inst_dir / "gc_ms.csv", self._logger): + info["csv"] = "analysis_data/gc_ms/gc_ms.csv" + else: + missing.append({"expected": "gc_ms.csv", "reason": "GC-MS 样品清单缺失"}) + + # 积分报告 + report_name = f"{task_id}_integration_report.xlsx" + report_src = analysis_data_dir / report_name + if report_src.exists(): + if self._safe_copy(report_src, inst_dir / report_name, self._logger): + info["integration_report"] = f"analysis_data/gc_ms/{report_name}" + else: + missing.append({"expected": report_name, "reason": "GC-MS 积分报告缺失"}) + + # 解析样品列表 + sample_names: List[str] = [] + if csv_src.exists(): + with csv_src.open("r", encoding="utf-8") as f: + reader = csv.DictReader(f) + sample_names = [row["SampleName"] for row in reader] + if not sample_names: + # 从 plots/ 目录推断 + plots_dir = analysis_data_dir / "plots" + if plots_dir.exists(): + for png in sorted(plots_dir.glob("*_tic.png"), key=_natural_sort_key): + name = png.stem.replace("_tic", "") + if name not in sample_names: + sample_names.append(name) + info["sample_names"] = sample_names + + # 色谱图 + chrom_dir = inst_dir / "chromatograms" + chrom_dir.mkdir(parents=True, exist_ok=True) + tic_count = 0 + fid_count = 0 + for sample_name in sample_names: + # TIC + tic_name = f"{sample_name}_tic.png" + tic_src = analysis_data_dir / "plots" / tic_name + if tic_src.exists(): + if self._safe_copy(tic_src, chrom_dir / tic_name, self._logger): + tic_count += 1 + else: + missing.append({ + "expected": tic_name, + "reason": f"样品 {sample_name} 的 TIC 色谱图缺失", + }) + # FID + fid_name = f"{sample_name}_fid.png" + fid_src = analysis_data_dir / "plots" / fid_name + if fid_src.exists(): + if self._safe_copy(fid_src, chrom_dir / fid_name, self._logger): + fid_count += 1 + else: + missing.append({ + "expected": fid_name, + "reason": f"样品 {sample_name} 的 FID 色谱图缺失", + }) + info["tic_count"] = tic_count + info["fid_count"] = fid_count + + # 质谱图 + ms_dir = inst_dir / "ms_spectra" + ms_dir.mkdir(parents=True, exist_ok=True) + ms_count = 0 + ms_plots_dir = analysis_data_dir / "ms_plots" + if ms_plots_dir.exists(): + for sample_name in sample_names: + ms_pattern = f"{sample_name}_peak*_ms.png" + for ms_src in sorted(ms_plots_dir.glob(ms_pattern), key=_natural_sort_key): + if self._safe_copy(ms_src, ms_dir / ms_src.name, self._logger): + ms_count += 1 + info["ms_count"] = ms_count + + # 结构图 + struct_dir = inst_dir / "structures" + struct_dir.mkdir(parents=True, exist_ok=True) + struct_count = 0 + src_struct_dir = analysis_data_dir / "structures" + if src_struct_dir.exists(): + for struct_src in sorted(src_struct_dir.glob("*.png")): + if self._safe_copy(struct_src, struct_dir / struct_src.name, self._logger): + struct_count += 1 + info["struct_count"] = struct_count + + return info, missing + + def _collect_uplc_qtof_analysis( + self, + task_id: str, + analysis_data_dir: Path, + inst_dir: Path, + ) -> Tuple[Dict[str, Any], List[Dict[str, str]]]: + """ + 功能: + 收集 UPLC-QTOF 仪器的分析数据. + 当前仅收集 CSV 清单, 图片相关功能预留. + 参数: + task_id: 任务 ID. + analysis_data_dir: 分析站本地数据目录. + inst_dir: 目标仪器子目录 (analysis_data/uplc_qtof/). + 返回: + Tuple[Dict, List]: + 第一个值为 UPLC-QTOF 归档信息字典. + 第二个值为缺失文件记录列表. + """ + info: Dict[str, Any] = {} + missing: List[Dict[str, str]] = [] + + # uplc_qtof.csv + csv_src = analysis_data_dir / "uplc_qtof.csv" + if csv_src.exists(): + if self._safe_copy(csv_src, inst_dir / "uplc_qtof.csv", self._logger): + info["csv"] = "analysis_data/uplc_qtof/uplc_qtof.csv" + else: + missing.append({"expected": "uplc_qtof.csv", "reason": "UPLC-QTOF 样品清单缺失"}) + + # 解析样品列表 + sample_names: List[str] = [] + if csv_src.exists(): + with csv_src.open("r", encoding="utf-8") as f: + reader = csv.DictReader(f) + sample_names = [row["SampleName"] for row in reader] + info["sample_names"] = sample_names + + # 预留: 色谱图, 质谱图, 结构图子目录 + for sub in ("chromatograms", "ms_spectra", "structures"): + (inst_dir / sub).mkdir(parents=True, exist_ok=True) + + return info, missing + + def _collect_hplc_analysis( + self, + task_id: str, + analysis_data_dir: Path, + inst_dir: Path, + ) -> Tuple[Dict[str, Any], List[Dict[str, str]]]: + """ + 功能: + 收集 HPLC 仪器的分析数据. + 当前仅收集 CSV 清单, 图片相关功能预留. + 参数: + task_id: 任务 ID. + analysis_data_dir: 分析站本地数据目录. + inst_dir: 目标仪器子目录 (analysis_data/hplc/). + 返回: + Tuple[Dict, List]: + 第一个值为 HPLC 归档信息字典. + 第二个值为缺失文件记录列表. + """ + info: Dict[str, Any] = {} + missing: List[Dict[str, str]] = [] + + # hplc.csv + csv_src = analysis_data_dir / "hplc.csv" + if csv_src.exists(): + if self._safe_copy(csv_src, inst_dir / "hplc.csv", self._logger): + info["csv"] = "analysis_data/hplc/hplc.csv" + else: + missing.append({"expected": "hplc.csv", "reason": "HPLC 样品清单缺失"}) + + # 解析样品列表 + sample_names: List[str] = [] + if csv_src.exists(): + with csv_src.open("r", encoding="utf-8") as f: + reader = csv.DictReader(f) + sample_names = [row["SampleName"] for row in reader] + info["sample_names"] = sample_names + + return info, missing + + def _collect_results( + self, + task_id: str, + syn_task_dir: Path, + analysis_data_dir: Path, + dest_dir: Path, + ) -> Tuple[Dict[str, str], List[Dict[str, str]]]: + """ + 功能: + 收集结果报告到 results/ 子目录, + 包括任务报告 (合成站) 和产率报告 (分析站). + 参数: + task_id: 任务 ID. + syn_task_dir: 合成站任务目录路径. + analysis_data_dir: 分析站本地数据目录. + dest_dir: 归档目标根目录 ({task_id}/). + 返回: + Tuple[Dict, List]: + 第一个值为成功复制的文件清单. + 第二个值为缺失文件记录列表. + """ + copied: Dict[str, str] = {} + missing: List[Dict[str, str]] = [] + results_dir = dest_dir / "results" + results_dir.mkdir(parents=True, exist_ok=True) + + # 任务报告 (来自合成站) + report_name = f"{task_id}_task_report.xlsx" + report_src = syn_task_dir / report_name + if report_src.exists(): + if self._safe_copy(report_src, results_dir / report_name, self._logger): + copied["task_report"] = f"results/{report_name}" + else: + missing.append({ + "expected": report_name, + "reason": "合成站任务目录中未找到任务报告", + }) + + # 产率报告 (来自分析站) + yield_name = f"{task_id}_yield_report.xlsx" + yield_src = analysis_data_dir / yield_name + if yield_src.exists(): + if self._safe_copy(yield_src, results_dir / yield_name, self._logger): + copied["yield_report"] = f"results/{yield_name}" + else: + missing.append({ + "expected": yield_name, + "reason": "分析站数据目录中未找到产率报告", + }) + + return copied, missing + + def _collect_raw_data( + self, + task_id: str, + dest_dir: Path, + instrument_samples: Dict[str, List[str]], + ) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]: + """ + 功能: + 从各仪器数据目录复制 .D 原始数据到 raw_data/{instrument}/ 子目录. + 参数: + task_id: 任务 ID. + dest_dir: 归档目标根目录 ({task_id}/). + instrument_samples: 按仪器分组的样品名列表, + 如 {"gc_ms": ["771-1", "771-2"], "uplc_qtof": ["771-3"]}. + 返回: + Tuple[List, List]: + 第一个值为成功复制的原始数据记录列表. + 第二个值为缺失文件记录列表. + """ + copied: List[Dict[str, str]] = [] + missing: List[Dict[str, str]] = [] + + # 仪器标识 -> 仪器数据目录的映射 + instrument_data_dirs = { + "gc_ms": self._settings.gc_ms_data_dir, + "uplc_qtof": self._settings.uplc_qtof_data_dir, + "hplc": self._settings.hplc_data_dir, + } + + for instrument, sample_names in instrument_samples.items(): + data_dir = instrument_data_dirs.get(instrument) + if data_dir is None: + self._logger.warning("未知仪器类型: %s, 跳过原始数据收集", instrument) + continue + + raw_dir = dest_dir / "raw_data" / instrument + raw_dir.mkdir(parents=True, exist_ok=True) + + for sample_name in sample_names: + d_dir_name = f"{sample_name}.D" + d_src = data_dir / d_dir_name + d_dest = raw_dir / d_dir_name + if d_src.exists() and d_src.is_dir(): + try: + if d_dest.exists(): + shutil.rmtree(d_dest) + shutil.copytree(d_src, d_dest) + copied.append({ + "instrument": instrument, + "sample_name": sample_name, + "path": f"raw_data/{instrument}/{d_dir_name}", + }) + self._logger.info("已复制原始数据: %s/%s", instrument, d_dir_name) + except (OSError, shutil.Error) as exc: + self._logger.warning( + "复制原始数据失败: %s -> %s, 原因: %s", d_src, d_dest, exc + ) + missing.append({ + "expected": d_dir_name, + "reason": f"{instrument} 原始数据复制失败: {exc}", + }) + else: + missing.append({ + "expected": d_dir_name, + "reason": f"{instrument} 仪器目录 {data_dir} 中未找到", + }) + + return copied, missing + + def _build_raw_data_references( + self, + instrument_samples: Dict[str, List[str]], + ) -> Dict[str, List[Dict[str, str]]]: + """ + 功能: + 构建各仪器原始数据路径引用, 记录各样品 .D 目录的原始位置. + 不论是否复制原始数据, 均记录以便溯源. + 参数: + instrument_samples: 按仪器分组的样品名列表. + 返回: + Dict[str, List]: 按仪器分组的引用列表. + """ + instrument_data_dirs = { + "gc_ms": self._settings.gc_ms_data_dir, + "uplc_qtof": self._settings.uplc_qtof_data_dir, + "hplc": self._settings.hplc_data_dir, + } + + refs: Dict[str, List[Dict[str, str]]] = {} + for instrument, sample_names in instrument_samples.items(): + data_dir = instrument_data_dirs.get(instrument) + if data_dir is None: + continue + inst_refs: List[Dict[str, str]] = [] + for sample_name in sample_names: + d_dir_name = f"{sample_name}.D" + inst_refs.append({ + "sample_name": sample_name, + "d_dir": str(data_dir / d_dir_name), + }) + refs[instrument] = inst_refs + + return refs + + def _write_archive_summary_txt( + self, + task_id: str, + dest_dir: Path, + experiment_plan: Dict[str, str], + analysis_info: Dict[str, Any], + results_files: Dict[str, str], + raw_refs: Dict[str, List[Dict[str, str]]], + missing_files: List[Dict[str, str]], + raw_copied: Optional[List[Dict[str, str]]] = None, + ) -> Path: + """ + 功能: + 在任务归档目录内生成 archive_summary.txt, + 按四大类 (实验计划/分析数据/结果报告/原始数据) 汇总. + 参数: + task_id: 任务 ID. + dest_dir: 归档目标目录 ({task_id}/). + experiment_plan: 实验计划已归档文件清单. + analysis_info: 按仪器组织的分析数据信息. + results_files: 结果报告已归档文件清单. + raw_refs: 按仪器分组的原始数据引用. + missing_files: 缺失文件记录列表. + raw_copied: 已复制的原始数据记录列表, None 表示未执行复制. + 返回: + Path: 生成的 TXT 文件路径. + """ + txt_path = dest_dir / "archive_summary.txt" + now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + instrument_labels = { + "gc_ms": "GC-MS", + "uplc_qtof": "UPLC-QTOF", + "hplc": "HPLC", + } + + lines: List[str] = [] + lines.append(f"实验数据归档清单 - 任务 {task_id}") + lines.append(f"归档时间: {now_str}") + lines.append(f"归档目录: {dest_dir}") + # 实验名称 (从实验方案 Excel B1 单元格提取) + exp_name = experiment_plan.get("experiment_name") + if exp_name: + lines.append(f"实验名称: {exp_name}") + lines.append("") + + # 一. 实验计划 + lines.append("=" * 40) + lines.append("实验计划") + lines.append("=" * 40) + if "experiment_plan" in experiment_plan: + lines.append(f"[实验方案] {experiment_plan['experiment_plan']}") + else: + lines.append("(无)") + lines.append("") + + # 二. 分析数据 + lines.append("=" * 40) + lines.append("分析数据") + lines.append("=" * 40) + if analysis_info: + for instrument, inst_data in analysis_info.items(): + label = instrument_labels.get(instrument, instrument) + lines.append(f"--- {label} ---") + if "csv" in inst_data: + lines.append(f"[样品清单] {inst_data['csv']}") + if "integration_report" in inst_data: + lines.append(f"[积分报告] {inst_data['integration_report']}") + tic_count = inst_data.get("tic_count", 0) + fid_count = inst_data.get("fid_count", 0) + ms_count = inst_data.get("ms_count", 0) + struct_count = inst_data.get("struct_count", 0) + if tic_count > 0: + lines.append( + f"[TIC色谱图] analysis_data/{instrument}/chromatograms/ " + f"... 共 {tic_count} 个" + ) + if fid_count > 0: + lines.append( + f"[FID色谱图] analysis_data/{instrument}/chromatograms/ " + f"... 共 {fid_count} 个" + ) + if ms_count > 0: + lines.append( + f"[质谱图] analysis_data/{instrument}/ms_spectra/ " + f"... 共 {ms_count} 个" + ) + if struct_count > 0: + lines.append( + f"[结构图] analysis_data/{instrument}/structures/ " + f"... 共 {struct_count} 个" + ) + # 仅有 CSV 无图片数据的仪器, 显示样品数 + sample_count = len(inst_data.get("sample_names", [])) + if sample_count > 0 and tic_count == 0 and ms_count == 0: + lines.append(f"[样品数] {sample_count} 个") + lines.append("") + else: + lines.append("(无)") + lines.append("") + + # 三. 结果报告 + lines.append("=" * 40) + lines.append("结果报告") + lines.append("=" * 40) + if "task_report" in results_files: + lines.append(f"[任务报告] {results_files['task_report']}") + if "yield_report" in results_files: + lines.append(f"[产率报告] {results_files['yield_report']}") + if not results_files: + lines.append("(无)") + lines.append("") + + # 四. 原始数据 + lines.append("=" * 40) + lines.append("原始数据") + lines.append("=" * 40) + has_raw_info = False + # 已复制的原始数据 + if raw_copied is not None and len(raw_copied) > 0: + # 按仪器分组统计 + inst_counts: Dict[str, int] = {} + for r in raw_copied: + inst = r["instrument"] + inst_counts[inst] = inst_counts.get(inst, 0) + 1 + for inst, count in inst_counts.items(): + label = instrument_labels.get(inst, inst) + lines.append( + f"[{label}] raw_data/{inst}/ ... 共 {count} 个 .D 目录 (已复制)" + ) + has_raw_info = True + # 原始数据引用 + if raw_refs: + for instrument, inst_refs in raw_refs.items(): + label = instrument_labels.get(instrument, instrument) + if inst_refs: + first_ref = inst_refs[0]["d_dir"] + lines.append(f"[{label}] {first_ref} ... 共 {len(inst_refs)} 个") + has_raw_info = True + if not has_raw_info: + lines.append("(无)") + lines.append("") + + # 缺失文件 + lines.append("=" * 40) + lines.append("缺失文件") + lines.append("=" * 40) + if missing_files: + for item in missing_files: + lines.append(f" {item['expected']} - {item['reason']}") + else: + lines.append("(无)") + lines.append("") + + txt_path.parent.mkdir(parents=True, exist_ok=True) + txt_path.write_text("\n".join(lines), encoding="utf-8") + self._logger.info("归档清单已写入: %s", txt_path) + return txt_path + + def aggregate_task_data( + self, + task_id: Optional[str] = None, + archive_dir: Optional[Path] = None, + copy_raw_data: Optional[bool] = None, + ) -> Dict: + """ + 功能: + 将指定任务的全部实验数据从合成站和分析站汇聚到统一归档目录. + 按四大类组织: 实验计划, 分析数据 (按仪器划分), 结果报告, 原始数据. + 参数: + task_id: 任务 ID 字符串, None 表示自动选取最新任务. + archive_dir: 归档输出根目录, None 则使用 settings.archive_dir. + copy_raw_data: 是否将 .D 原始数据目录复制到归档, + None 则使用 settings.archive_copy_raw_data. + 返回: + Dict: {"success": bool, "return_info": str, "archive_path": str}. + """ + try: + # 1. 定位任务目录 + syn_task_dir, resolved_id = self._find_task_dir(task_id) + self._logger.info("开始归档任务 %s ...", resolved_id) + + # 2. 确定源目录 + analysis_data_dir = self._settings.data_dir / resolved_id + + # 3. 确定归档目标目录 + target_archive_dir = archive_dir if archive_dir is not None else self._settings.archive_dir + dest_dir = target_archive_dir / resolved_id + dest_dir.mkdir(parents=True, exist_ok=True) + self._logger.info("归档目标目录: %s", dest_dir) + + all_missing: List[Dict[str, str]] = [] + + # 4. 验证源目录 + if not syn_task_dir.exists(): + self._logger.warning("合成站任务目录不存在: %s", syn_task_dir) + if not analysis_data_dir.exists(): + self._logger.warning("分析站数据目录不存在: %s", analysis_data_dir) + + # 5. 检测有数据的仪器 + instruments: List[str] = [] + if analysis_data_dir.exists(): + instruments = self._detect_instruments(analysis_data_dir) + self._logger.info("检测到仪器: %s", instruments) + + # 6. 收集实验计划 + experiment_plan: Dict[str, str] = {} + if syn_task_dir.exists(): + experiment_plan, plan_missing = self._collect_experiment_plan( + resolved_id, syn_task_dir, dest_dir + ) + all_missing.extend(plan_missing) + + # 7. 收集分析数据 (按仪器划分) + analysis_info: Dict[str, Any] = {} + if analysis_data_dir.exists() and instruments: + analysis_info, ana_missing = self._collect_analysis_data( + resolved_id, analysis_data_dir, dest_dir, instruments + ) + all_missing.extend(ana_missing) + + # 8. 收集结果报告 + results_files: Dict[str, str] = {} + if syn_task_dir.exists() or analysis_data_dir.exists(): + results_files, res_missing = self._collect_results( + resolved_id, syn_task_dir, analysis_data_dir, dest_dir + ) + all_missing.extend(res_missing) + + # 9. 构建按仪器分组的样品名映射 + instrument_samples: Dict[str, List[str]] = {} + for instrument, inst_data in analysis_info.items(): + sample_names = inst_data.get("sample_names", []) + if sample_names: + instrument_samples[instrument] = sample_names + + # 10. 解析是否复制原始数据: 参数优先, 否则取配置 + should_copy_raw = copy_raw_data if copy_raw_data is not None else self._settings.archive_copy_raw_data + + # 11. 可选复制原始数据 + raw_copied: Optional[List[Dict[str, str]]] = None + if should_copy_raw is True and instrument_samples: + raw_copied, raw_missing = self._collect_raw_data( + resolved_id, dest_dir, instrument_samples + ) + all_missing.extend(raw_missing) + + # 12. 构建原始数据引用 + raw_refs = self._build_raw_data_references(instrument_samples) + + # 13. 写入归档清单 TXT (位于 {task_id}/ 目录内) + txt_path = self._write_archive_summary_txt( + task_id=resolved_id, + dest_dir=dest_dir, + experiment_plan=experiment_plan, + analysis_info=analysis_info, + results_files=results_files, + raw_refs=raw_refs, + missing_files=all_missing, + raw_copied=raw_copied, + ) + + # 14. 统计归档文件数量 + file_count = len(experiment_plan) + len(results_files) + for inst_data in analysis_info.values(): + file_count += (1 if "csv" in inst_data else 0) + file_count += (1 if "integration_report" in inst_data else 0) + file_count += inst_data.get("tic_count", 0) + file_count += inst_data.get("fid_count", 0) + file_count += inst_data.get("ms_count", 0) + file_count += inst_data.get("struct_count", 0) + if raw_copied is not None: + file_count += len(raw_copied) + + info = ( + f"任务 {resolved_id} 归档完成: " + f"已归档 {file_count} 项, " + f"缺失 {len(all_missing)} 项. " + f"归档目录: {dest_dir}, " + f"清单文件: {txt_path}" + ) + self._logger.info(info) + return { + "success": True, + "return_info": info, + "archive_path": str(dest_dir), + } + + except Exception as exc: + self._logger.error("归档失败: %s", exc, exc_info=True) + return {"success": False, "return_info": f"归档失败: {exc}"} + + +# ------------------------------------------------------------------ +# 交互式测试入口 +# ------------------------------------------------------------------ + +def _print_result(result: Dict) -> None: + """ + 功能: + 格式化打印函数返回结果. + 参数: + result: 函数返回的字典. + 返回: + 无. + """ + print("\n========== 执行结果 ==========") + for key, value in result.items(): + print(f" {key}: {value}") + print("==============================\n") + + +def main() -> None: + """ + 功能: + 交互式菜单, 用于手动测试 run_analysis / process_gc_ms_results / + poll_analysis_run / get_status / get_methods / calculate_yields / + submit_by_csv_path / aggregate_task_data. + 用户可选择功能并输入 task_id, 输入 q 退出. + 参数: + 无. + 返回: + 无. + """ + configure_logging("DEBUG") + logger = logging.getLogger("main") + logger.info("初始化分析站控制器...") + + controller = AnalysisStationController() + + menu = ( + "\n===== 分析站交互式测试菜单 =====\n" + " 1. run_analysis - 统一分析入口(生成CSV并提交至仪器)\n" + " 2. process_gc_ms_results - GC-MS结果处理(积分+定性+报告)\n" + " 3. poll_analysis_run - 轮询GC-MS分析任务状态并自动处理结果\n" + " 4. get_status - 获取GC-MS设备当前状态\n" + " 5. get_methods - 获取当前Project的方法列表\n" + " 6. calculate_yields - 产率计算\n" + " 7. submit_by_csv_path - 选择仪器并按CSV路径直接提交任务\n" + " 8. aggregate_task_data - 实验数据归档汇总\n" + " 9. transfer_to_shelf - 分析完成样品→货架转运(等待空闲后自动执行)\n" + " 0. 退出\n" + "================================" + ) + + while True: + print(menu) + choice = input("请选择功能编号: ").strip() + + if choice == "0": + print("已退出测试.") + break + + if choice not in ("1", "2", "3", "4", "5", "6", "7", "8", "9"): + print("无效选择, 请输入 0/1/2/3/4/5/6/7/8/9.") + continue + + # 选项 4/5 直接操作设备驱动, 不需要 task_id + if choice in ("4", "5"): + settings = controller._settings + client = ZhidaClient( + host=settings.gc_ms_host, + port=settings.gc_ms_port, + timeout=settings.gc_ms_timeout, + ) + try: + client.connect() + if choice == "4": + print("\n>>> 调用 ZhidaClient.get_status_detail()") + status_detail = client.get_status_detail() + raw_status = status_detail["raw_status"] or "(空)" + sub_status = status_detail["sub_status"] or "(无)" + print( + "\n" + f" 原始状态: {raw_status}\n" + f" 主状态: {status_detail['base_status']}\n" + f" 子状态: {sub_status}\n" + ) + else: + print("\n>>> 调用 ZhidaClient.get_methods()") + methods = client.get_methods() + _print_result(methods) + except Exception as exc: + logger.error("设备操作失败: %s", exc) + print(f"\n 操作失败: {exc}\n") + finally: + client.close() + continue + + if choice == "7": + instrument_options = { + "1": ("gc_ms", "GC-MS"), + "2": ("uplc_qtof", "UPLC_QTOF"), + "3": ("hplc", "HPLC"), + } + print("\n请选择仪器:") + for option, (_, instrument_name) in instrument_options.items(): + print(f" {option}. {instrument_name}") + + instrument_choice = input("请输入仪器编号(1/2/3): ").strip() + if instrument_choice not in instrument_options: + print("无效选择, 请输入 1/2/3.") + continue + + instrument = instrument_options[instrument_choice][0] + csv_file_path = input("请输入CSV文件路径: ").strip() + print( + f"\n>>> 调用 submit_by_csv_path(" + f"instrument={instrument!r}, csv_file_path={csv_file_path!r})" + ) + result = controller.submit_by_csv_path( + instrument=instrument, csv_file_path=csv_file_path + ) + _print_result(result) + continue + + # 获取 task_id, 空字符串视为 None(自动选取最新任务) + task_id_input = input("请输入 task_id (留空则自动选取最新任务): ").strip() + task_id = task_id_input if task_id_input else None + + if choice == "1": + print(f"\n>>> 调用 run_analysis(task_id={task_id!r})") + result = controller.run_analysis(task_id=task_id) + _print_result(result) + + elif choice == "2": + print(f"\n>>> 调用 process_gc_ms_results(task_id={task_id!r})") + result = controller.process_gc_ms_results(task_id=task_id) + _print_result(result) + + elif choice == "3": + # poll_analysis_run 额外支持配置轮询间隔 + interval_input = input("请输入轮询间隔秒数 (留空默认30): ").strip() + try: + interval = float(interval_input) if interval_input else 30.0 + except ValueError: + print("无效数值, 使用默认30秒.") + interval = 30.0 + + print( + f"\n>>> 调用 poll_analysis_run(task_id={task_id!r}, " + f"poll_interval={interval})" + ) + result = controller.poll_analysis_run( + task_id=task_id, poll_interval=interval + ) + _print_result(result) + + elif choice == "6": + print(f"\n>>> 调用 calculate_yields(task_id={task_id!r})") + result = controller.calculate_yields(task_id=task_id) + _print_result(result) + + elif choice == "8": + default_copy = controller._settings.archive_copy_raw_data + hint = "Y/n" if default_copy is True else "y/N" + copy_raw_input = input( + f"是否复制原始数据(.D目录)? ({hint}, 留空使用配置默认值): " + ).strip().lower() + if copy_raw_input == "": + copy_raw: Optional[bool] = None # 使用配置默认值 + else: + copy_raw = copy_raw_input in ("y", "yes") + print( + f"\n>>> 调用 aggregate_task_data(" + f"task_id={task_id!r}, copy_raw_data={copy_raw})" + ) + result = controller.aggregate_task_data( + task_id=task_id, copy_raw_data=copy_raw + ) + _print_result(result) + + elif choice == "9": + # 分析完成样品→货架转运 + print("\n>>> 分析完成样品→货架转运") + print("说明: 轮询智达进样设备状态, 等待空闲后将样品从分析站转运到货架空位\n") + + try: + from unilabos.devices.eit_agv.controller.agv_controller import AGVController + + agv = AGVController(timeout=180000) + + # 显示当前货架状态 + agv.shelf_manager.print_status() + + # 询问源托盘 + print("默认源托盘: analysis_station_tray_1-2") + source_input = input( + "请输入源托盘(多个用逗号分隔, 直接回车使用默认): " + ).strip() + + if source_input == "": + source_trays = ["analysis_station_tray_1-2"] + else: + source_trays = [ + s.strip() for s in source_input.split(",") if s.strip() != "" + ] + + # 询问轮询间隔 + interval_input = input("请输入轮询间隔秒数 (留空默认30): ").strip() + try: + interval = float(interval_input) if interval_input else 30.0 + except ValueError: + print("无效数值, 使用默认30秒.") + interval = 30.0 + + print(f"\n源托盘: {source_trays}") + print(f"轮询间隔: {interval} 秒") + + # 执行转运 + print("\n开始执行分析站→货架样品转运...") + success = agv.transfer_analysis_to_shelf( + source_trays=source_trays, + poll_interval=interval, + ) + + _print_result({ + "success": success, + "source_trays": source_trays, + "poll_interval": interval, + }) + + if success: + agv.shelf_manager.print_status() + + except Exception as exc: + logger.error("分析站→货架转运失败: %s", exc) + print(f"\n 操作失败: {exc}\n") + + +if __name__ == "__main__": + main() + diff --git a/unilabos/devices/eit_analysis_station/driver/__init__.py b/unilabos/devices/eit_analysis_station/driver/__init__.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/unilabos/devices/eit_analysis_station/driver/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/unilabos/devices/eit_analysis_station/driver/zhida_driver.py b/unilabos/devices/eit_analysis_station/driver/zhida_driver.py new file mode 100644 index 00000000..2f477b38 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/driver/zhida_driver.py @@ -0,0 +1,435 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +智达GCMS设备驱动 + +支持智达GCMS设备的TCP通信协议,包括状态查询、方法获取、样品分析等功能。 +通信协议版本:1.0.1 +""" + +import base64 +import json +import socket +import time +import os +from pathlib import Path +from typing import Dict + + +class ZhidaClient: + def __init__(self, host='10.40.6.101', port=5792, timeout=10.0): + # 如果部署在智达GCMS上位机本地,可使用localhost: host='127.0.0.1' + """ + 初始化智达GCMS客户端 + + Args: + host (str): 设备IP地址,本地部署时可使用'127.0.0.1' + port (int): 通信端口,默认5792 + timeout (float): 超时时间,单位秒 + """ + self.host = host + self.port = port + self.timeout = timeout + self.sock = None + self._ros_node = None # ROS节点引用,由框架设置 + + def post_init(self, ros_node): + """ + ROS节点初始化后的回调方法,用于建立设备连接 + + Args: + ros_node: ROS节点实例 + """ + self._ros_node = ros_node + try: + self.connect() + ros_node.lab_logger().info(f"智达GCMS设备连接成功: {self.host}:{self.port}") + except Exception as e: + ros_node.lab_logger().error(f"智达GCMS设备连接失败: {e}") + # 不抛出异常,允许节点继续运行,后续可以重试连接 + + def connect(self): + """ + 建立TCP连接到智达GCMS设备 + + Raises: + ConnectionError: 连接失败时抛出 + """ + try: + self.sock = socket.create_connection((self.host, self.port), timeout=self.timeout) + # 确保后续 recv/send 都会在 timeout 秒后抛 socket.timeout + self.sock.settimeout(self.timeout) + except Exception as e: + raise ConnectionError(f"Failed to connect to {self.host}:{self.port} - {str(e)}") + + def close(self): + """ + 关闭与智达GCMS设备的TCP连接 + """ + if self.sock: + try: + self.sock.close() + except Exception: + pass # 忽略关闭时的错误 + finally: + self.sock = None + + def _send_command(self, cmd: dict) -> dict: + """ + 发送命令到智达GCMS设备并接收响应 + + Args: + cmd (dict): 要发送的命令字典 + + Returns: + dict: 设备响应的JSON数据 + + Raises: + ConnectionError: 连接错误 + TimeoutError: 超时错误 + """ + if not self.sock: + raise ConnectionError("Not connected to device") + + try: + # 发送JSON命令(UTF-8编码) + payload = json.dumps(cmd, ensure_ascii=False).encode('utf-8') + self.sock.sendall(payload) + + # 循环接收数据直到能成功解析完整JSON + buffer = bytearray() + start = time.time() + while True: + try: + chunk = self.sock.recv(4096) + if not chunk: + # 对端关闭连接,尝试解析已接收的数据 + if buffer: + try: + text = buffer.decode('utf-8', errors='strict') + return json.loads(text) + except (UnicodeDecodeError, json.JSONDecodeError): + pass + break + buffer.extend(chunk) + + # 尝试解码和解析JSON + text = buffer.decode('utf-8', errors='strict') + try: + return json.loads(text) + except json.JSONDecodeError: + # JSON不完整,继续接收 + pass + + except socket.timeout: + # 超时时,尝试解析已接收的数据 + if buffer: + try: + text = buffer.decode('utf-8', errors='strict') + return json.loads(text) + except (UnicodeDecodeError, json.JSONDecodeError): + pass + raise TimeoutError(f"recv() timed out after {self.timeout:.1f}s") + + # 防止死循环,总时长超过2倍超时时间就报错 + if time.time() - start > self.timeout * 2: + # 最后尝试解析已接收的数据 + if buffer: + try: + text = buffer.decode('utf-8', errors='strict') + return json.loads(text) + except (UnicodeDecodeError, json.JSONDecodeError): + pass + raise TimeoutError(f"No complete JSON received after {time.time() - start:.1f}s") + + # 连接关闭,如果有数据则尝试解析 + if buffer: + try: + text = buffer.decode('utf-8', errors='strict') + return json.loads(text) + except (UnicodeDecodeError, json.JSONDecodeError): + pass + + raise ConnectionError("Connection closed before JSON could be parsed") + + except Exception as e: + if isinstance(e, (ConnectionError, TimeoutError)): + raise + else: + raise ConnectionError(f"Command send failed: {str(e)}") + + @staticmethod + def _parse_status_detail(raw_status: str) -> Dict[str, str]: + """ + 功能: + 将智达协议返回的原始状态拆分为主状态和子状态, 统一复合状态判定. + 参数: + raw_status: 协议返回的 result 字段, 例如 "Idle" 或 "Idle#SeqRun:Error". + 返回: + Dict[str, str], 包含 raw_status/base_status/sub_status 三个键. + """ + normalized_raw_status = raw_status.strip() if isinstance(raw_status, str) else "" + if normalized_raw_status == "": + return {"raw_status": "", "base_status": "Unknown", "sub_status": ""} + + base_status, separator, sub_status = normalized_raw_status.partition("#") + base_status = base_status.strip() or "Unknown" + sub_status = sub_status.strip() if separator else "" + return { + "raw_status": normalized_raw_status, + "base_status": base_status, + "sub_status": sub_status, + } + + def get_status_detail(self) -> Dict[str, str]: + """ + 功能: + 查询设备状态并返回原始状态、归一化主状态和子状态, 便于流程判定与排障. + 参数: + 无. + 返回: + Dict[str, str], 形如 {"raw_status": str, "base_status": str, "sub_status": str}. + """ + if not self.sock: + # 尝试重新连接, 避免长连接断开后状态查询直接失败. + try: + self.connect() + if self._ros_node: + self._ros_node.lab_logger().info("智达GCMS设备重新连接成功") + except Exception as e: + if self._ros_node: + self._ros_node.lab_logger().warning(f"智达GCMS设备连接失败: {e}") + return self._parse_status_detail("Offline") + + try: + response = self._send_command({"command": "getstatus"}) + return self._parse_status_detail(response.get("result", "")) + except Exception as e: + if self._ros_node: + self._ros_node.lab_logger().warning(f"获取设备状态失败: {e}") + return self._parse_status_detail("Error") + + def get_status(self) -> str: + """ + 功能: + 获取归一化后的设备主状态, 兼容旧调用方的字符串状态接口. + 参数: + 无. + 返回: + str, 设备主状态. 复合状态如 "Idle#SeqRun:Error" 会返回 "Idle". + """ + return self.get_status_detail()["base_status"] + + def get_methods(self) -> dict: + """ + 获取当前Project的方法列表 + + Returns: + dict: 包含方法列表的响应 + """ + if not self.sock: + try: + self.connect() + if self._ros_node: + self._ros_node.lab_logger().info("智达GCMS设备重新连接成功") + except Exception as e: + if self._ros_node: + self._ros_node.lab_logger().warning(f"智达GCMS设备连接失败: {e}") + return {"error": "Device not connected"} + + try: + return self._send_command({"command": "getmethods"}) + except Exception as e: + if self._ros_node: + self._ros_node.lab_logger().warning(f"获取方法列表失败: {e}") + return {"error": str(e)} + + def get_version(self) -> dict: + """ + 获取接口版本和InLabPAL固件版本 + + Returns: + dict: 响应格式 {"result": "OK|Error", "message": "Interface:x.x.x;FW:x.x.x.xxx"} + """ + return self._send_command({"command": "version"}) + + def put_tray(self) -> dict: + """ + 放盘操作,准备InLabPAL进样器 + + 注意:此功能仅在特殊场景下使用,例如: + - 机械臂比较短,需要让开一个位置 + - 盘支架是可移动的,需要进样器配合做动作 + + 对于宜宾深势这套配置,空间足够,不需要这个额外的控制组件。 + + Returns: + dict: 响应格式 {"result": "OK|Error", "message": "ready_info|error_info"} + """ + return self._send_command({"command": "puttray"}) + + def start_with_csv_file(self, string: str = None, csv_file_path: str = None) -> dict: + """ + 使用CSV文件启动分析(支持ROS2动作调用) + + Args: + string (str): CSV文件路径(ROS2参数名) + csv_file_path (str): CSV文件路径(兼容旧接口) + + Returns: + dict: ROS2动作结果格式 {"return_info": str, "success": bool} + + Raises: + FileNotFoundError: CSV文件不存在 + Exception: 文件读取或通信错误 + """ + try: + # 支持两种参数传递方式:ROS2的string参数和直接的csv_file_path参数 + file_path = string if string is not None else csv_file_path + if file_path is None: + error_msg = "未提供CSV文件路径参数" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + # 使用Path对象进行更健壮的文件处理 + csv_path = Path(file_path) + if not csv_path.exists(): + error_msg = f"CSV文件不存在: {file_path}" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + # 读取CSV文件内容(UTF-8编码,替换未知字符) + csv_content = csv_path.read_text(encoding="utf-8", errors="replace") + + # 转换为Base64编码 + b64_content = base64.b64encode(csv_content.encode('utf-8')).decode('ascii') + + if self._ros_node: + self._ros_node.lab_logger().info(f"正在发送CSV文件到智达GCMS: {file_path}") + self._ros_node.lab_logger().info(f"Base64编码长度: {len(b64_content)} 字符") + + # 发送start命令 + response = self._send_command({ + "command": "start", + "message": b64_content + }) + + # 转换为ROS2动作结果格式 + if response.get("result") == "OK": + success_msg = f"智达GCMS分析启动成功: {response.get('message', 'Unknown')}" + if self._ros_node: + self._ros_node.lab_logger().info(success_msg) + return {"return_info": success_msg, "success": True} + else: + error_msg = f"智达GCMS分析启动失败: {response.get('message', 'Unknown error')}" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + except Exception as e: + error_msg = f"CSV文件处理失败: {str(e)}" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + def start(self, string: str = None, text: str = None) -> dict: + """ + 使用Base64编码数据启动分析(支持ROS2动作调用) + + Args: + string (str): Base64编码的CSV数据(ROS2参数名) + text (str): Base64编码的CSV数据(兼容旧接口) + + Returns: + dict: ROS2动作结果格式 {"return_info": str, "success": bool} + """ + try: + # 支持两种参数传递方式:ROS2的string参数和原有的text参数 + b64_content = string if string is not None else text + if b64_content is None: + error_msg = "未提供Base64编码数据参数" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + if self._ros_node: + self._ros_node.lab_logger().info(f"正在发送Base64数据到智达GCMS") + self._ros_node.lab_logger().info(f"Base64编码长度: {len(b64_content)} 字符") + + # 发送start命令 + response = self._send_command({ + "command": "start", + "message": b64_content + }) + + # 转换为ROS2动作结果格式 + if response.get("result") == "OK": + success_msg = f"智达GCMS分析启动成功: {response.get('message', 'Unknown')}" + if self._ros_node: + self._ros_node.lab_logger().info(success_msg) + return {"return_info": success_msg, "success": True} + else: + error_msg = f"智达GCMS分析启动失败: {response.get('message', 'Unknown error')}" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + except Exception as e: + error_msg = f"Base64数据处理失败: {str(e)}" + if self._ros_node: + self._ros_node.lab_logger().error(error_msg) + return {"return_info": error_msg, "success": False} + + def abort(self) -> dict: + """ + 停止当前运行的分析 + + Returns: + dict: 响应格式 {"result": "OK|Error", "message": "error_info"} + """ + return self._send_command({"command": "abort"}) + + +def test_zhida_client(): + """ + 测试智达GCMS客户端功能 + """ + client = ZhidaClient() + + try: + # 连接设备 + print("Connecting to Zhida GCMS...") + client.connect() + print("Connected successfully!") + + # 获取设备状态 + print(f"Device status: {client.get_status()}") + + # 获取版本信息 + version_info = client.get_version() + print(f"Version info: {version_info}") + + # 获取方法列表 + methods = client.get_methods() + print(f"Available methods: {methods}") + + # # 测试CSV文件发送(如果文件存在) + # csv_file = Path(__file__).parent / "zhida_gcms-test_1.csv" + # if csv_file.exists(): + # print(f"Testing CSV file: {csv_file}") + # result = client.start_with_csv_file(str(csv_file)) + # print(f"Start result: {result}") + + except Exception as e: + print(f"Error: {str(e)}") + + finally: + # 关闭连接 + client.close() + print("Connection closed.") + + +if __name__ == "__main__": + test_zhida_client() diff --git a/unilabos/devices/eit_analysis_station/processor/.gitignore b/unilabos/devices/eit_analysis_station/processor/.gitignore new file mode 100644 index 00000000..d132b995 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/.gitignore @@ -0,0 +1,3 @@ +# 论文 PDF、本地数据 txt 不参与 PR / 上游版本库 +*.pdf +data/*.txt diff --git a/unilabos/devices/eit_analysis_station/processor/__init__.py b/unilabos/devices/eit_analysis_station/processor/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_analysis_station/processor/chromatogram_plotter.py b/unilabos/devices/eit_analysis_station/processor/chromatogram_plotter.py new file mode 100644 index 00000000..76ad3ae9 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/chromatogram_plotter.py @@ -0,0 +1,441 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 生成色谱图 (TIC / FID) 的积分结果可视化图. + 标注峰区域, 保留时间, 峰面积, 化合物名称等. +参数: + 无. +返回: + 无. +""" + +import logging +import textwrap +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import matplotlib +matplotlib.use("Agg") # 非交互式后端, 不依赖 GUI +import matplotlib.pyplot as plt +import numpy as np + +plt.rcParams["axes.unicode_minus"] = False # 负号正常显示 + +from .nist_matcher import CompoundMatch +from .peak_integrator import PeakResult + +logger = logging.getLogger(__name__) + +# 峰区域填充色板 +_PEAK_COLORS = [ + "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", + "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf", +] + + +class ChromatogramPlotter: + """ + 功能: + 绘制色谱图并标注积分峰, 支持 TIC 和 FID. + 参数: + chromatogram_ppi: TIC/FID 色谱图导出分辨率. + ms_spectrum_ppi: 质谱图导出分辨率. + figsize: 图片尺寸 (宽, 高) 英寸. + 返回: + 无. + """ + + def __init__( + self, + chromatogram_ppi: int = 150, + ms_spectrum_ppi: int = 150, + figsize: tuple = (14, 6), + ): + self._chromatogram_ppi = chromatogram_ppi + self._ms_spectrum_ppi = ms_spectrum_ppi + self._figsize = figsize + + def plot_chromatogram( + self, + times: np.ndarray, + intensities: np.ndarray, + peaks: List[PeakResult], + compound_matches: Optional[Dict[float, List[CompoundMatch]]] = None, + title: str = "", + ylabel: str = "Intensity", + output_path: Optional[Path] = None, + rt_min: Optional[float] = None, + rt_max: Optional[float] = None, + y_range_min: float = 0, + baseline: Optional[np.ndarray] = None, + fill_baseline_mode: str = "local", + ) -> Path: + """ + 功能: + 绘制色谱图, 标注每个峰的积分区域, 保留时间和峰面积. + TIC 图额外标注化合物名称 (通过 compound_matches 参数). + 密集峰自动调整标注位置避免遮挡. + 参数: + times: 保留时间数组 (min). + intensities: 信号强度数组. + peaks: 峰检测积分结果列表. + compound_matches: 保留时间 -> 化合物匹配列表, None 表示不标注化合物名. + title: 图片标题. + ylabel: Y 轴标签. + output_path: 输出图片路径. + rt_min: X 轴最小保留时间 (min), None 使用数据范围. + rt_max: X 轴最大保留时间 (min), None 使用数据范围. + y_range_min: Y 轴最小显示范围, 确保小信号图不会过于压缩. + baseline: 全局基线数组, 与 times/intensities 等长. 提供时用于峰区域填充, + None 时回退到峰端点连线基线. + fill_baseline_mode: 填充基线模式, local 表示局部端点连线, global 表示优先使用传入 baseline. + 返回: + Path: 保存的图片路径. + """ + fig, ax = plt.subplots(1, 1, figsize=self._figsize) + global_baseline_valid = ( + fill_baseline_mode == "global" + and baseline is not None + and len(baseline) == len(times) + ) + if fill_baseline_mode == "global" and not global_baseline_valid: + logger.warning("全局基线填充不可用, 自动回退到局部基线填充.") + + # 绘制色谱基线 + ax.plot(times, intensities, color="black", linewidth=0.6, label="Signal") + + for i, peak in enumerate(peaks): + color = _PEAK_COLORS[i % len(_PEAK_COLORS)] + + # 填充峰区域: 从基线到信号, 与积分计算一致 + mask = (times >= peak.start_time) & (times <= peak.end_time) + if mask.any(): + peak_times = times[mask] + peak_intensities = intensities[mask] + if global_baseline_valid: + peak_baseline = np.minimum(baseline[mask], peak_intensities) + else: + # 默认使用局部端点连线, 与积分算法保持一致. + peak_baseline = np.linspace( + peak_intensities[0], + peak_intensities[-1], + len(peak_times), + ) + ax.fill_between( + peak_times, peak_baseline, peak_intensities, + alpha=0.3, color=color, + ) + + # X 轴范围: 受 rt_min / rt_max 控制 + x_lo = rt_min if rt_min is not None else float(times[0]) + x_hi = rt_max if rt_max is not None else float(times[-1]) + ax.set_xlim(x_lo, x_hi) + + # Y 轴范围: 基于可见区域内信号最大值, 留上方余量给标注 + visible_mask = (times >= x_lo) & (times <= x_hi) + if visible_mask.any(): + y_max = float(intensities[visible_mask].max()) + # 确保最小显示范围 + y_max = max(y_max, y_range_min) + # 留 30% 上方空间给标注文字 + ax.set_ylim(bottom=0, top=y_max * 1.30) + + # 标注峰: 按峰高降序排列, 高峰优先占据正上方位置 + sorted_peaks = sorted(enumerate(peaks), key=lambda x: x[1].height, reverse=True) + annotations = self._annotate_peaks( + ax, fig, sorted_peaks, compound_matches, x_lo, x_hi, + ) + + ax.set_xlabel("Retention Time (min)") + ax.set_ylabel(ylabel) + ax.set_title(title) + ax.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + + plt.tight_layout() + + if output_path is None: + output_path = Path("chromatogram.png") + output_path.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(str(output_path), dpi=self._chromatogram_ppi, bbox_inches="tight") + plt.close(fig) + + logger.info("色谱图已保存: %s", output_path) + return output_path + + def _annotate_peaks( + self, + ax: plt.Axes, + fig: plt.Figure, + sorted_peaks: List[Tuple[int, PeakResult]], + compound_matches: Optional[Dict[float, List[CompoundMatch]]], + x_lo: float, + x_hi: float, + ) -> None: + """ + 功能: + 为所有峰添加标注 (RT + 面积 + 化合物名), 自动调整偏移避免遮挡. + 使用 display 坐标检测文字碰撞, 密集区域交替左右偏移. + 参数: + ax: matplotlib 坐标轴. + fig: matplotlib 图形 (用于坐标变换). + sorted_peaks: 按峰高降序排列的 (原始索引, PeakResult) 列表. + compound_matches: 化合物匹配字典. + x_lo: X 轴显示下限. + x_hi: X 轴显示上限. + 返回: + 无. + """ + # 收集所有标注信息 + label_items = [] + for _, peak in sorted_peaks: + # 跳过不在显示范围内的峰 + if peak.retention_time < x_lo or peak.retention_time > x_hi: + continue + + # 构建标注文本: RT + 面积 + area_str = self._format_area(peak.area) + label_text = f"RT {peak.retention_time:.2f}\nArea: {area_str}" + + # TIC 图标注化合物名称 (显示全名) + if compound_matches is not None: + match_list = self._find_match(peak.retention_time, compound_matches) + if match_list is not None and len(match_list) > 0: + name = self._wrap_compound_name(match_list[0].compound_name) + label_text += f"\n{name}" + + label_items.append((peak, label_text)) + + # 第一遍: 绘制所有标注, 默认正上方 + fig.canvas.draw() # 需要先渲染才能获取文字 bbox + placed_bboxes = [] # 已放置标注的 display 坐标 bbox 列表 + + for peak, label_text in label_items: + # 计算偏移: 检查是否与已有标注重叠 + offset_x, offset_y = self._compute_offset( + ax, fig, peak, placed_bboxes, label_text, + ) + + ann = ax.annotate( + label_text, + xy=(peak.retention_time, peak.height), + xytext=(offset_x, offset_y), + textcoords="offset points", + ha="center", va="bottom", + fontsize=6, + arrowprops=dict(arrowstyle="-", color="gray", lw=0.5), + ) + + # 记录已放置标注的 bbox + fig.canvas.draw() + bbox = ann.get_window_extent(renderer=fig.canvas.get_renderer()) + placed_bboxes.append(bbox) + + def _compute_offset( + self, + ax: plt.Axes, + fig: plt.Figure, + peak: PeakResult, + placed_bboxes: list, + label_text: str, + ) -> Tuple[float, float]: + """ + 功能: + 计算标注的 (x_offset, y_offset) 偏移量 (单位: points). + 若正上方位置与已有标注重叠, 则交替向左/右偏移, 直到不重叠. + 参数: + ax: 坐标轴. + fig: 图形. + peak: 当前峰. + placed_bboxes: 已放置标注的 display bbox 列表. + label_text: 标注文本. + 返回: + Tuple[float, float]: (x_offset, y_offset) 偏移量, 单位 points. + """ + if not placed_bboxes: + return (0, 8) + + renderer = fig.canvas.get_renderer() + # 创建临时标注来获取文字尺寸 + tmp = ax.annotate( + label_text, + xy=(peak.retention_time, peak.height), + xytext=(0, 8), + textcoords="offset points", + ha="center", va="bottom", + fontsize=6, + ) + fig.canvas.draw() + tmp_bbox = tmp.get_window_extent(renderer) + tmp.remove() + + # 检查是否与已有标注重叠 + if not self._has_overlap(tmp_bbox, placed_bboxes): + return (0, 8) + + # 仅沿 y 方向逐级上移, 避免标注重叠 + for shift in range(1, 8): + y_off = 8 + shift * 12 # 同时逐级上移避免箭头交叉 + + tmp2 = ax.annotate( + label_text, + xy=(peak.retention_time, peak.height), + xytext=(0, y_off), + textcoords="offset points", + ha="center", va="bottom", + fontsize=6, + ) + fig.canvas.draw() + bbox2 = tmp2.get_window_extent(renderer) + tmp2.remove() + + if not self._has_overlap(bbox2, placed_bboxes): + return (0, y_off) + + # 所有位置都重叠, 用最大偏移 + return (0, 8 + 7 * 12) + + @staticmethod + def _has_overlap(bbox, placed_bboxes: list, padding: float = 2.0) -> bool: + """ + 功能: + 检查 bbox 是否与已放置的任一标注 bbox 重叠. + 参数: + bbox: 待检测的 display 坐标 bbox. + placed_bboxes: 已放置标注的 bbox 列表. + padding: 额外间距 (display pixels). + 返回: + bool: True 表示有重叠. + """ + expanded = bbox.expanded( + 1 + padding / max(bbox.width, 1), + 1 + padding / max(bbox.height, 1), + ) + for existing in placed_bboxes: + if expanded.overlaps(existing): + return True + return False + + @staticmethod + def _format_area(area: float) -> str: + """ + 功能: + 将峰面积格式化为紧凑的科学计数法字符串. + 参数: + area: 峰面积. + 返回: + str: 格式化字符串, 如 "1.23e6", "456". + """ + if abs(area) >= 1e6: + return f"{area:.2e}" + elif abs(area) >= 1000: + return f"{area:.0f}" + elif abs(area) >= 1: + return f"{area:.2f}" + else: + return f"{area:.4f}" + + @staticmethod + def _wrap_compound_name(name: str, max_line_length: int = 22) -> str: + """ + 功能: + 对过长化合物名称自动换行, 减少峰标注的横向占位. + 参数: + name: 原始化合物名称. + max_line_length: 单行最大字符数. + 返回: + str, 插入换行后的化合物名称. + """ + normalized_name = " ".join(str(name).split()) + if len(normalized_name) <= max_line_length: + return normalized_name + + # 优先按空格和连字符断行, 实在过长时再切分连续长串. + return textwrap.fill( + normalized_name, + width=max_line_length, + break_long_words=True, + break_on_hyphens=True, + ) + + def plot_ms_spectrum( + self, + mz: np.ndarray, + intensities: np.ndarray, + title: str = "", + output_path: Optional[Path] = None, + top_n_labels: int = 10, + ) -> Path: + """ + 功能: + 绘制单个峰的质谱棒状图 (m/z vs relative intensity). + 自动标注强度最高的 top_n_labels 个离子的 m/z 值. + 参数: + mz: m/z 数组. + intensities: 强度数组. + title: 图片标题. + output_path: 输出图片路径, None 使用默认路径. + top_n_labels: 标注 m/z 值的最强峰数量. + 返回: + Path: 保存的图片路径. + """ + fig, ax = plt.subplots(1, 1, figsize=(12, 5)) + + # 归一化为相对强度 (%) + max_intensity = intensities.max() if len(intensities) > 0 else 1.0 + if max_intensity == 0: + max_intensity = 1.0 + rel_intensities = intensities / max_intensity * 100.0 + + # 棒状图绘制 + ax.vlines(mz, 0, rel_intensities, colors="#1f77b4", linewidth=0.8) + + # 标注最强的 top_n_labels 个峰的 m/z 值 + if len(mz) > 0: + n = min(top_n_labels, len(mz)) + top_indices = np.argsort(rel_intensities)[-n:] + for idx in top_indices: + ax.annotate( + f"{mz[idx]:.1f}", + xy=(mz[idx], rel_intensities[idx]), + xytext=(0, 4), + textcoords="offset points", + ha="center", va="bottom", + fontsize=7, + color="#333333", + ) + + ax.set_xlabel("m/z") + ax.set_ylabel("Relative Intensity (%)") + ax.set_title(title) + ax.set_ylim(bottom=0, top=110) + + # X 轴留边距 + if len(mz) > 0: + margin = (mz.max() - mz.min()) * 0.05 + ax.set_xlim(mz.min() - max(margin, 5), mz.max() + max(margin, 5)) + + plt.tight_layout() + + if output_path is None: + output_path = Path("ms_spectrum.png") + output_path.parent.mkdir(parents=True, exist_ok=True) + fig.savefig(str(output_path), dpi=self._ms_spectrum_ppi, bbox_inches="tight") + plt.close(fig) + + logger.info("质谱图已保存: %s", output_path) + return output_path + + @staticmethod + def _find_match( + rt: float, + matches: Dict[float, List[CompoundMatch]], + tolerance: float = 0.05, + ) -> Optional[List[CompoundMatch]]: + """按保留时间查找最接近的化合物匹配.""" + if not matches: + return None + closest_rt = min(matches.keys(), key=lambda r: abs(r - rt)) + if abs(closest_rt - rt) <= tolerance: + return matches[closest_rt] + return None diff --git a/unilabos/devices/eit_analysis_station/processor/data_reader.py b/unilabos/devices/eit_analysis_station/processor/data_reader.py new file mode 100644 index 00000000..d2386bb3 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/data_reader.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 读取 Agilent .D 目录中的色谱/质谱数据. + 支持 TIC (总离子流色谱), FID (火焰离子化检测器) 和单扫描质谱. + TIC 优先从智达软件自动导出的 tic_front.csv 读取, + 备选使用 rainbow-api 解析 data.ms 和 FID*.ch 二进制文件. +参数: + 无. +返回: + 无. +""" + +import csv +import logging +import xml.etree.ElementTree as ET +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import numpy as np + +logger = logging.getLogger(__name__) + + +class GCMSDataReader: + """ + 功能: + 读取 .D 目录中的色谱/质谱数据. + 优先使用 tic_front.csv (智达自动导出), + 备选使用 rainbow-api 解析 data.ms 和 FID1A.ch 二进制文件. + 参数: + 无. + 返回: + 无. + """ + + def read_tic(self, d_dir: Path) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 读取 TIC (总离子流色谱) 数据. + 优先从 tic_front.csv 解析, 不存在时用 rainbow-api 解析 data.ms. + 参数: + d_dir: .D 目录路径. + 返回: + Tuple[np.ndarray, np.ndarray]: (保留时间数组, 强度数组). + """ + csv_path = d_dir / "tic_front.csv" + if csv_path.exists(): + return self._read_tic_from_csv(csv_path) + + logger.info("tic_front.csv 不存在, 使用 rainbow-api 解析 data.ms") + return self._read_tic_from_ms(d_dir) + + def _read_tic_from_csv(self, csv_path: Path) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 从智达软件导出的 tic_front.csv 解析 TIC 数据. + 文件格式: 第1行为路径信息, 第2行为 "Start of data points", + 后续每行为 "retention_time,intensity". + 参数: + csv_path: tic_front.csv 文件路径. + 返回: + Tuple[np.ndarray, np.ndarray]: (保留时间数组, 强度数组). + """ + times = [] + intensities = [] + + with csv_path.open("r", encoding="utf-8") as f: + for line in f: + line = line.strip() + # 跳过头部信息行 + if not line or line.startswith("Start of") or not line[0].isdigit(): + continue + parts = line.split(",") + if len(parts) >= 2: + try: + times.append(float(parts[0])) + intensities.append(float(parts[1])) + except ValueError: + continue + + logger.info("从 tic_front.csv 读取 %d 个数据点", len(times)) + return np.array(times), np.array(intensities) + + def _read_tic_from_ms(self, d_dir: Path) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 使用 rainbow-api 解析 data.ms, 计算 TIC (各扫描全离子强度之和). + 参数: + d_dir: .D 目录路径. + 返回: + Tuple[np.ndarray, np.ndarray]: (保留时间数组, TIC 强度数组). + """ + import rainbow as rb + + datadir = rb.read(str(d_dir)) + ms_file = datadir.get_file("data.ms") + if ms_file is None: + raise FileNotFoundError(f"未找到 data.ms: {d_dir}") + + times = ms_file.xlabels # shape: (n_scans,) + tic = ms_file.data.sum(axis=1) # 每次扫描的总离子强度 + + logger.info("从 data.ms 读取 %d 个扫描, TIC 范围: %.0f - %.0f", + len(times), tic.min(), tic.max()) + return times, tic + + def read_fid(self, d_dir: Path) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 使用 rainbow-api 解析 FID .ch 文件, 读取 FID 检测器信号. + 兼容 FID1A.ch, FID1B.ch 等不同通道命名. + 优先使用 FID1A.ch, 若不存在则自动查找目录中其他 FID*.ch 文件. + 参数: + d_dir: .D 目录路径. + 返回: + Tuple[np.ndarray, np.ndarray]: (保留时间数组, FID 强度数组). + """ + import rainbow as rb + + # 先扫描目录中所有 FID*.ch 文件, 确定实际可用的文件名 + fid_candidates = sorted(d_dir.glob("FID*.ch")) + if len(fid_candidates) == 0: + raise FileNotFoundError(f"未找到任何 FID*.ch 文件: {d_dir}") + + # 优先使用 FID1A.ch, 不存在则取排序后的第一个 + fid_name = "FID1A.ch" + has_default = any(f.name == fid_name for f in fid_candidates) + if has_default is False: + fid_name = fid_candidates[0].name + if len(fid_candidates) > 1: + candidate_names = [f.name for f in fid_candidates] + logger.warning( + "未找到 FID1A.ch, 目录中存在多个 FID 文件 %s, 使用 %s", + candidate_names, fid_name, + ) + else: + logger.info("未找到 FID1A.ch, 使用备选文件: %s", fid_name) + + datadir = rb.read(str(d_dir)) + fid_file = datadir.get_file(fid_name) + + times = fid_file.xlabels # shape: (n_points,) + intensities = fid_file.data[:, 0] # shape: (n_points,), 取第一列 + + logger.info("从 %s 读取 %d 个数据点, 信号范围: %.2f - %.2f", + fid_name, len(times), intensities.min(), intensities.max()) + return times, intensities + + def read_ms_spectra_at_rt( + self, d_dir: Path, retention_time: float, tolerance: float = 0.02 + ) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 读取指定保留时间处的质谱数据 (m/z vs intensity). + 在 tolerance 范围内找到最近的扫描. + 参数: + d_dir: .D 目录路径. + retention_time: 目标保留时间 (min). + tolerance: 保留时间匹配容差 (min). + 返回: + Tuple[np.ndarray, np.ndarray]: (m/z 数组, 强度数组). + """ + import rainbow as rb + + datadir = rb.read(str(d_dir)) + ms_file = datadir.get_file("data.ms") + if ms_file is None: + raise FileNotFoundError(f"未找到 data.ms: {d_dir}") + + # 找到最近的扫描索引 + idx = int(np.argmin(np.abs(ms_file.xlabels - retention_time))) + actual_rt = ms_file.xlabels[idx] + + if abs(actual_rt - retention_time) > tolerance: + logger.warning( + "最近扫描 RT=%.3f 与目标 RT=%.3f 偏差 %.3f min, 超出容差 %.3f", + actual_rt, retention_time, abs(actual_rt - retention_time), tolerance + ) + + spectrum = ms_file.data[idx] # shape: (n_mz,) + mz_values = ms_file.ylabels # shape: (n_mz,) + + # 过滤零强度离子 + nonzero = spectrum > 0 + return mz_values[nonzero], spectrum[nonzero] + + def read_ms_spectra_at_peak( + self, + d_dir: Path, + start_time: float, + end_time: float, + avg_scans: int = 3, + ) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 读取峰边界范围内 TIC 强度最高的质谱 (apex), 并对附近扫描取平均以提升信噪比. + 相比 read_ms_spectra_at_rt 只取最近单次扫描, 本方法: + 1. 在 [start_time, end_time] 内找到 TIC 最大的扫描 (真正的 apex). + 2. 以 apex 为中心, 平均 avg_scans 个扫描, 降低噪声. + 参数: + d_dir: .D 目录路径. + start_time: 峰起始时间 (min). + end_time: 峰结束时间 (min). + avg_scans: 以 apex 为中心的平均扫描数 (奇数, 默认 3). + 返回: + Tuple[np.ndarray, np.ndarray]: (m/z 数组, 平均强度数组). + """ + import rainbow as rb + + datadir = rb.read(str(d_dir)) + ms_file = datadir.get_file("data.ms") + if ms_file is None: + raise FileNotFoundError(f"未找到 data.ms: {d_dir}") + + scan_times = ms_file.xlabels # shape: (n_scans,) + + # 找到峰边界内的扫描索引范围 + mask = (scan_times >= start_time) & (scan_times <= end_time) + boundary_indices = np.where(mask)[0] + + if len(boundary_indices) == 0: + # 降级: 使用原始最近扫描方法 + logger.warning( + "峰范围 [%.3f, %.3f] 内无扫描, 降级为最近扫描", + start_time, end_time, + ) + mid_rt = (start_time + end_time) / 2.0 + return self.read_ms_spectra_at_rt(d_dir, mid_rt) + + # 计算范围内每个扫描的 TIC, 找到最大值 (apex) + tic_in_range = ms_file.data[boundary_indices].sum(axis=1) + apex_local_idx = int(np.argmax(tic_in_range)) + apex_idx = boundary_indices[apex_local_idx] + + # 以 apex 为中心取 avg_scans 个扫描做平均 + half = avg_scans // 2 + avg_start = max(0, apex_idx - half) + avg_end = min(ms_file.data.shape[0], apex_idx + half + 1) + + avg_spectrum = ms_file.data[avg_start:avg_end].mean(axis=0) # shape: (n_mz,) + mz_values = ms_file.ylabels # shape: (n_mz,) + + # 过滤零强度离子 + nonzero = avg_spectrum > 0 + + logger.debug( + "峰 apex 扫描: idx=%d, RT=%.3f, 平均 %d 个扫描 [%d:%d]", + apex_idx, scan_times[apex_idx], avg_end - avg_start, avg_start, avg_end, + ) + + return mz_values[nonzero], avg_spectrum[nonzero] + + def read_sample_info(self, d_dir: Path) -> Dict: + """ + 功能: + 从 AcqData/sample_info.xml 读取样品元数据. + 参数: + d_dir: .D 目录路径. + 返回: + Dict: 包含 sample_name, acq_time, method, data_file 等键. + """ + info_path = d_dir / "AcqData" / "sample_info.xml" + result = {} + + if not info_path.exists(): + logger.warning("sample_info.xml 不存在: %s", info_path) + return result + + tree = ET.parse(str(info_path)) + root = tree.getroot() + + # 字段名到键名的映射 + field_map = { + "Sample Name": "sample_name", + "Sample Position": "sample_position", + "Data File": "data_file", + "Method": "method", + "AcqTime": "acq_time", + "RunCompletedFlag": "run_completed", + "InstrumentName": "instrument_name", + "Inj Vol (µl)": "inj_vol", + } + + for field_elem in root.findall("Field"): + name = field_elem.findtext("Name", "").strip() + value = field_elem.findtext("Value", "").strip() + if name in field_map: + result[field_map[name]] = value + + logger.info("样品信息: %s", result.get("sample_name", "未知")) + return result diff --git a/unilabos/devices/eit_analysis_station/processor/ecn.py b/unilabos/devices/eit_analysis_station/processor/ecn.py new file mode 100644 index 00000000..67edb347 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/ecn.py @@ -0,0 +1,189 @@ +""" +gcpy: python utilities for chromatographs +Copyright (C) 2021 David Ollodart + +功能: + 根据 SMILES 字符串分类碳原子的官能团, 并计算有效碳数(ECN). + ECN 用于 GC-FID 定量分析中的响应因子校正. +参数: + 无. +返回: + 无. +""" + +import networkx as nx +from pysmiles.read_smiles import read_smiles +from collections import defaultdict + +ary_dct = {0: '', 1: 'primary', 2: 'secondary', 3: 'tertiary', 4: 'quaternary'} +alk_dct = {2: 'alkyne', 3: 'alkene', 4: 'alkane'} + + +def smiles2carbontypes(smiles): + """ + 功能: + 将 SMILES 字符串转换为碳原子分类结果. + 逐个检查碳原子及其最近/次近邻居, 判断所属官能团和芳香性. + 参数: + smiles: str, 化合物 SMILES 字符串. + 返回: + Generator[Tuple[str, str]], 每个碳原子的 (芳香性分类, 官能团类型). + """ + graph = read_smiles(smiles, explicit_hydrogen=True) + + cycles_list = nx.algorithms.cycle_basis(graph) + cyc_nodes = [] + for cyc in cycles_list: + # 非芳香性环原子不影响 ECN + if len(cyc) % 2 == 0 and len(cyc) >= 4: + for n in cyc: + if n not in cyc_nodes: + cyc_nodes.append(n) + + global dldct + dldct = graph.nodes(data='element') + + for node0 in graph.nodes: + + if dldct[node0] != 'C': + continue + # ECN 仅需统计碳原子 + + nbors1 = '' + nbors2 = '' + edge01 = [] + edge12 = [] + for node1 in graph.neighbors(node0): + nbors1 += dldct[node1] + edge01.append(node1) + tmp = [] + for node2 in graph.neighbors(node1): + nbors2 += dldct[node2] + if node2 != node0: + tmp.append(node2) + edge12.append(tmp) + + nbors1 = ''.join(sorted(nbors1)) + nbors2 = ''.join(sorted(nbors2)) + + is_cycle = node0 in cyc_nodes + + yield classify(nbors1, nbors2, edge01, edge12, is_cycle) + + +def classify(nbors1, nbors2, edge01, edge12, is_cycle): + """ + 功能: + 根据碳原子的邻居信息判断其官能团类型. + 参数: + nbors1: str, 最近邻原子元素(排序后). + nbors2: str, 次近邻原子元素(排序后). + edge01: list, 最近邻节点列表. + edge12: list, 次近邻节点列表(按最近邻分组). + is_cycle: bool, 该碳原子是否在环上. + 返回: + Tuple[str, str], (芳香性分类, 官能团类型). + """ + ary = nbors1.count('C') + hyd = nbors1.count('H') + + ln1 = len(nbors1) + if ln1 == ary + hyd: + if ln1 == 3 and is_cycle: + return ary_dct[ary], 'aromatic' + return ary_dct[ary], alk_dct[ln1] + + if nbors1 == 'CCO': + return '', 'ketone' + if nbors1 == 'CHO': + return '', 'aldehyde' + + if nbors1 == 'CHHO' or nbors1 == 'CCHO': + for i in range(len(edge01)): + if dldct[edge01[i]] == 'O': + n = edge12[i] + if len(n) == 1 and dldct[n[0]] == 'H': + return ary_dct[ary], 'alcohol' + return '', 'ether' + + if nbors1 == 'COO': + for i in range(len(edge01)): + if dldct[edge01[i]] == 'O': + n = edge12[i] + if len(n) == 1 and dldct[n[0]] == 'H': + return '', 'carboxylic acid' + return '', 'acid anhydride or ester' + + if 'N' in nbors1: + if nbors1 == 'CNO': + return '', 'amide' + elif nbors1 == 'CN': + return '', 'nitrile' + else: + for i in range(len(edge01)): + if dldct[edge01[i]] == 'N': + n = edge12[i] + amine_ary = 1 + for j in n: + if dldct[j] == 'C': + return '', 'amine, or carbon adjacent to amide' + elif dldct[j] == 'C': + amine_ary += 1 + return ary_dct[amine_ary], 'amine' + + if 'S' in nbors1: + for i in range(len(edge01)): + if dldct[edge01[i]] == 'S': + n = edge12[i] + if len(n) == 1 and dldct[n[0]] == 'H': + return '', 'thiol' + return '', 'sulfide' + + if 'F' in nbors1 or 'Cl' in nbors1 or 'Br' in nbors1 or 'I' in nbors1: + return ary_dct[ary], 'halide' + + return '', 'unknown' + + +# 官能团 → ECN 大类映射 +class_dct = {'alkane': 'aliphatic', + 'alkene': 'olefinic', + 'alkyne': 'acetylinic', + 'carboxylic acid': 'carboxyl', + 'aldehyde': 'carbonyl', + 'ketone': 'carbonyl', + 'ester': 'carboxyl', + 'nitrile': 'nitrile', + 'ether': 'ether', + 'amine': 'amine', + 'alcohol': 'alcohol', + 'acid anhydride': 'ester', + 'amide': 'aliphatic', + 'acid anhydride or ester': 'ester', + 'aromatic': 'aromatic' + } + + +def factory(): + return 0 + + +class_dct = defaultdict(factory, class_dct) +u = 0 + +# ECN 大类 → 数值映射 +ecn_dct = {"aliphatic": 1.00, + "aromatic": 1.00, + "olefinic": 0.95, + "acetylinic": 1.30, + "carbonyl": u, + "carboxyl": u, + "nitrile": 0.30, + "ether": -1.00 + 1.5, + "primary alcohol": -0.50 + 1, + "secondary alcohol": -0.75 + 1, + "tertiary alcohol": -0.25 + 1, + "amine": u + 1 + } + +ecn_dct = defaultdict(factory, ecn_dct) diff --git a/unilabos/devices/eit_analysis_station/processor/molecular_mass_predictor.py b/unilabos/devices/eit_analysis_station/processor/molecular_mass_predictor.py new file mode 100644 index 00000000..9d047e46 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/molecular_mass_predictor.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 基于 EI 质谱实现 PIM (Peak Interpretation Method) 分子量预测. + 输入单个峰的 m/z-强度谱图后, 输出预测分子离子峰和置信指数. +参数: + 无. +返回: + 无. +""" + +import logging +import math +from pathlib import Path +from typing import Dict, Optional, Set, Tuple + +import numpy as np + +from .report_generator import PIMPrediction + +logger = logging.getLogger(__name__) + + +class PIMPredictor: + """ + 功能: + 使用 PIM 算法对 EI 质谱进行分子量预测. + 算法流程: + 1. 光谱归一化. + 2. 计算 PIM 核心预测质量. + 3. 计算 PIM 置信指数. + 参数: + ab_m: 分子离子峰最小相对丰度阈值, 单位 %. + beta: 置信指数公式中的斜率参数. + epsilon_f: 置信指数公式中的噪声修正参数. + illogical_losses_path: 非逻辑损失列表文件路径. + 返回: + 无. + """ + + def __init__( + self, + ab_m: float = 0.3, + beta: float = 5.0, + epsilon_f: float = 0.0, + illogical_losses_path: Optional[Path] = None, + ) -> None: + self._ab_m = float(ab_m) + self._beta = float(beta) + self._epsilon_f = float(epsilon_f) + if illogical_losses_path is None: + self._illogical_losses_path = Path(__file__).parent / "data" / "illogical_losses.txt" + else: + self._illogical_losses_path = illogical_losses_path + self._illogical_losses = self._load_illogical_losses(self._illogical_losses_path) + + @staticmethod + def _load_illogical_losses(file_path: Path) -> Set[int]: + """ + 功能: + 读取非逻辑损失列表文件. + 参数: + file_path: 损失列表文件路径, 每行一个整数. + 返回: + Set[int], 非逻辑损失集合. + """ + losses: Set[int] = set() + if file_path.exists() is False: + logger.warning("PIM 非逻辑损失文件不存在: %s", file_path) + return losses + + lines = file_path.read_text(encoding="utf-8", errors="replace").splitlines() + for line_no, raw_line in enumerate(lines, start=1): + text = raw_line.strip() + if text == "": + continue + + try: + loss_value = int(text) + except ValueError: + logger.warning("PIM 非逻辑损失文件含非法行, 已忽略: %s:%d -> %s", file_path, line_no, text) + continue + + losses.add(loss_value) + + logger.info("PIM 非逻辑损失加载完成: %d 项", len(losses)) + return losses + + @staticmethod + def _normalize_spectrum( + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> Tuple[np.ndarray, np.ndarray]: + """ + 功能: + 归一化输入光谱: + 1. 过滤无效值和非正强度. + 2. m/z 四舍五入为整数质量. + 3. 合并重复 m/z, 取最大强度. + 4. 按 m/z 升序输出. + 参数: + mz_values: m/z 数组. + intensities: 强度数组. + 返回: + Tuple[np.ndarray, np.ndarray], 归一化后的 m/z 和强度数组. + """ + mz_array = np.asarray(mz_values, dtype=float) + intensity_array = np.asarray(intensities, dtype=float) + + if len(mz_array) != len(intensity_array): + raise ValueError("m/z 与强度数组长度不一致") + + if len(mz_array) == 0: + return np.array([], dtype=float), np.array([], dtype=float) + + valid_mask = np.isfinite(mz_array) & np.isfinite(intensity_array) & (intensity_array > 0) + mz_valid = mz_array[valid_mask] + intensity_valid = intensity_array[valid_mask] + + if len(mz_valid) == 0: + return np.array([], dtype=float), np.array([], dtype=float) + + merged_intensity: Dict[int, float] = {} + rounded_mz = np.rint(mz_valid).astype(int) + for mz_item, intensity_item in zip(rounded_mz, intensity_valid): + mz_key = int(mz_item) + intensity_value = float(intensity_item) + old_value = merged_intensity.get(mz_key) + + if old_value is None: + merged_intensity[mz_key] = intensity_value + continue + + if intensity_value > old_value: + merged_intensity[mz_key] = intensity_value + + sorted_mz = sorted(merged_intensity.keys()) + normalized_mz = np.array(sorted_mz, dtype=float) + normalized_intensity = np.array([merged_intensity[mz_key] for mz_key in sorted_mz], dtype=float) + return normalized_mz, normalized_intensity + + def _pim_core( + self, + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> float: + """ + 功能: + 计算 PIM 核心预测质量. + 参数: + mz_values: 归一化后的 m/z 数组, 升序. + intensities: 归一化后的强度数组. + 返回: + float, 预测的分子离子质量. + """ + if len(mz_values) == 0: + raise ValueError("光谱为空, 无法执行 PIM 预测") + + if len(mz_values) < 2: + return float(mz_values[0]) + + base_peak = float(np.max(intensities)) + lowest_ab = (self._ab_m / 100.0) * base_peak + n_peaks = len(mz_values) + + for peak_index in range(n_peaks - 1, 1, -1): + m0 = float(mz_values[peak_index]) + a0 = float(intensities[peak_index]) + m1 = float(mz_values[peak_index - 1]) + a1 = float(intensities[peak_index - 1]) + md1 = m0 - m1 + + if peak_index - 2 < 1: + md2 = 0.0 + a2 = 0.0 + else: + m2 = float(mz_values[peak_index - 2]) + a2 = float(intensities[peak_index - 2]) + md2 = m0 - m2 + + if a0 < lowest_ab: + continue + + if md1 > 2.0: + return m0 + + if abs(md1 - 2.0) <= 1e-8: + if a1 < int(a0 / 2.0): + return m0 + elif abs(md1 - 1.0) <= 1e-8: + isocalc = (a1 * 0.011 * m1) / 14.0 + + if int(3.0 * isocalc) > a0 or (a0 - int(isocalc)) < lowest_ab: + if peak_index == 2: + return m1 + continue + + if md2 > 2.0: + return m0 + + if peak_index - 2 >= 1: + if a2 < int(a0 / 2.0): + return m0 + + return float(mz_values[n_peaks - 1]) + + def _compute_confidence_index( + self, + gamma: float, + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> Optional[float]: + """ + 功能: + 计算 PIM 置信指数. + I = 2 - 2 / (1 + exp(-beta * r_tau)). + 参数: + gamma: PIM 预测质量. + mz_values: 归一化后的 m/z 数组. + intensities: 归一化后的强度数组. + 返回: + Optional[float], 置信指数. 无法计算时返回 None. + """ + if len(mz_values) == 0: + return None + + nearest_index = int(np.argmin(np.abs(mz_values - gamma))) + p_ab = float(intensities[nearest_index]) + if p_ab <= 0: + return None + + max_ab = float(np.max(intensities)) + lambda_values = gamma - mz_values + + numerator = 0.0 + for idx, loss_candidate in enumerate(lambda_values): + loss_key = int(round(float(loss_candidate))) + if loss_key in self._illogical_losses: + penalty = max(float(intensities[idx]) - self._epsilon_f * max_ab, 0.0) + numerator += penalty + + r_tau = numerator / p_ab + try: + confidence_index = 2.0 - 2.0 / (1.0 + math.exp(-1.0 * self._beta * r_tau)) + except OverflowError: + confidence_index = 2.0 + return confidence_index + + def predict( + self, + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> PIMPrediction: + """ + 功能: + 执行 PIM 预测并返回结构化结果. + 参数: + mz_values: 原始 m/z 数组. + intensities: 原始强度数组. + 返回: + PIMPrediction, 预测结果对象. + """ + try: + normalized_mz, normalized_intensity = self._normalize_spectrum(mz_values, intensities) + except Exception as exc: + logger.error("PIM 光谱归一化失败: %s", exc) + return PIMPrediction( + status="error", + message=f"光谱归一化失败: {exc}", + ) + + if len(normalized_mz) == 0: + return PIMPrediction( + status="no_spectrum", + message="峰内无有效质谱信号", + ) + + try: + gamma = self._pim_core(normalized_mz, normalized_intensity) + except Exception as exc: + logger.error("PIM 预测失败: %s", exc) + return PIMPrediction( + status="error", + message=f"PIM 核心预测失败: {exc}", + ) + + predicted_mass = int(round(gamma)) + confidence_index = self._compute_confidence_index(gamma, normalized_mz, normalized_intensity) + + if confidence_index is None: + return PIMPrediction( + predicted_mz=predicted_mass, + predicted_mw=predicted_mass, + confidence_index=None, + status="error", + message="PIM 置信指数计算失败", + ) + + return PIMPrediction( + predicted_mz=predicted_mass, + predicted_mw=predicted_mass, + confidence_index=confidence_index, + status="ok", + message="PIM 预测成功", + ) diff --git a/unilabos/devices/eit_analysis_station/processor/mspepsearch_predictor.py b/unilabos/devices/eit_analysis_station/processor/mspepsearch_predictor.py new file mode 100644 index 00000000..808b221f --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/mspepsearch_predictor.py @@ -0,0 +1,682 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 封装 SS-HM (Simple Search Hitlist Method) 和 iHS-HM (iterative Hybrid Search + Hitlist Method) 两种基于 MSPepSearch 的分子量预测算法. + 参考论文: Moorthy et al. "Inferring the nominal molecular mass of an analyte + from its electron ionization mass spectrum" (2021). +参数: + 无. +返回: + 无. +""" + +import logging +import os +import subprocess +import tempfile +import time +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import numpy as np + +from .molecular_mass_predictor import PIMPredictor +from .nist_library_reader import NistLibraryReader +from .report_generator import SSHMPrediction, iHSHMPrediction + +logger = logging.getLogger(__name__) + + +class MSPepSearchPredictor: + """ + 功能: + 封装 SS-HM 和 iHS-HM 两种基于 MSPepSearch 谱库搜索的分子量预测算法. + SS-HM: 简单搜索 → 命中修正 → 加权概率. + iHS-HM: 候选质量遍历 → 混合搜索 → Omega 指标. + 参数: + mspepsearch_exe: MSPepSearch64.exe 路径. + lib_path: NIST 谱库路径 (如 mainlib 目录). + lib_type: 库类型标识, "MAIN"/"REPL"/"LIB". + pim_predictor: PIMPredictor 实例, 复用 PIM 核心算法. + nist_lib_reader: NistLibraryReader 实例, 用于 SS-HM 库谱查找. + work_dir: 临时文件工作目录. + sshm_hits: SS-HM 命中深度. + sshm_b_ss: SS-HM 指数缩放因子. + ihshm_hits: iHS-HM 命中深度. + ihshm_mEMF: iHS-HM 最低期望匹配因子. + timeout: MSPepSearch 单次搜索超时(秒). + 返回: + 无. + """ + + def __init__( + self, + mspepsearch_exe: Path, + lib_path: Path, + lib_type: str = "MAIN", + pim_predictor: Optional[PIMPredictor] = None, + nist_lib_reader: Optional[NistLibraryReader] = None, + work_dir: Optional[Path] = None, + sshm_hits: int = 25, + sshm_b_ss: int = 75, + ihshm_hits: int = 25, + ihshm_mEMF: int = 700, + timeout: float = 120.0, + ) -> None: + self._exe = Path(mspepsearch_exe) + self._lib_path = Path(lib_path) + self._lib_type = lib_type.upper() + self._pim = pim_predictor + self._lib_reader = nist_lib_reader + self._work_dir = Path(work_dir) if work_dir is not None else Path(tempfile.gettempdir()) + self._sshm_hits = sshm_hits + self._sshm_b_ss = sshm_b_ss + self._ihshm_hits = ihshm_hits + self._ihshm_mEMF = ihshm_mEMF + self._timeout = timeout + + self._available = self._exe.exists() and self._lib_path.exists() + if not self._available: + logger.warning( + "MSPepSearch 不可用: exe=%s (存在=%s), lib=%s (存在=%s)", + self._exe, self._exe.exists(), + self._lib_path, self._lib_path.exists(), + ) + + @property + def available(self) -> bool: + """MSPepSearch 是否可用.""" + return self._available + + # ================================================================== + # SS-HM: Simple Search Hitlist Method + # ================================================================== + + def predict_sshm( + self, + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> SSHMPrediction: + """ + 功能: + 完整 SS-HM 预测. + 1. PIM_core(query) → gamma. + 2. MSPepSearch 简单搜索 → hitlist. + 3. 对每个命中: correction = lib_MW - PIM(lib_spectrum), 库谱不可用时降级. + 4. 按 MF 加权计算各唯一 correction 的概率. + 5. sigma = gamma + best_correction. + 参数: + mz_values: 查询质谱 m/z 数组. + intensities: 查询质谱强度数组. + 返回: + SSHMPrediction. + """ + if not self._available: + return SSHMPrediction(status="error", message="MSPepSearch 不可用") + + if self._pim is None: + return SSHMPrediction(status="error", message="PIMPredictor 未配置") + + # 1. PIM 预测查询谱 + try: + norm_mz, norm_ab = PIMPredictor._normalize_spectrum(mz_values, intensities) + if len(norm_mz) == 0: + return SSHMPrediction(status="no_spectrum", message="无有效质谱信号") + gamma = self._pim._pim_core(norm_mz, norm_ab) + except Exception as exc: + return SSHMPrediction(status="error", message=f"PIM 预测失败: {exc}") + + # 2. 写入 MSP 并搜索 + msp_path = self._work_dir / "query_sshm.msp" + out_path = self._work_dir / "hitlist_sshm.txt" + self._write_query_msp("Query", norm_mz, norm_ab, msp_path) + + if not self._run_mspepsearch("Sf", msp_path, out_path): + return SSHMPrediction(status="error", message="MSPepSearch 简单搜索失败") + + # 3. 解析 hitlist + hits = self._parse_ss_hitlist(out_path) + if len(hits) == 0: + return SSHMPrediction(status="error", message="SS-HM 未获得有效命中") + + # 4. 计算修正因子 + corrections: List[int] = [] + match_factors: List[int] = [] + + for hit_name, hit_id, lib_mw, mf in hits: + correction = self._compute_sshm_correction(hit_name, lib_mw, gamma) + corrections.append(correction) + match_factors.append(mf) + + # 5. 加权概率计算 + sigma, i_sigma, best_correction = self._compute_sshm_probability( + gamma, corrections, match_factors + ) + + return SSHMPrediction( + predicted_mw=int(round(sigma)), + confidence=round(i_sigma, 4), + correction=best_correction, + status="ok", + message=f"SS-HM 预测成功, {len(hits)} 个命中", + ) + + def _compute_sshm_correction( + self, + hit_name: str, + lib_mw: int, + gamma_query: float, + ) -> int: + """ + 功能: + 计算单个命中的 SS-HM 修正因子. + 完整版: correction = lib_MW - PIM(lib_spectrum). + 降级版: correction = lib_MW - gamma_query. + 参数: + hit_name: 命中化合物名称. + lib_mw: 命中化合物的库 MW. + gamma_query: 查询谱的 PIM 预测. + 返回: + int, 修正因子. + """ + # 尝试从库中获取命中化合物的质谱并运行 PIM + if self._lib_reader is not None and self._lib_reader.available: + spectrum = self._lib_reader.get_spectrum(hit_name) + if spectrum is not None: + lib_mz, lib_ab, _ = spectrum + try: + lib_norm_mz, lib_norm_ab = PIMPredictor._normalize_spectrum(lib_mz, lib_ab) + if len(lib_norm_mz) >= 2: + gamma_lib = self._pim._pim_core(lib_norm_mz, lib_norm_ab) + return int(round(lib_mw - gamma_lib)) + except Exception: + pass # 降级到简化模式 + + # 降级: 直接用 lib_MW - gamma_query + return int(round(lib_mw - gamma_query)) + + @staticmethod + def _compute_sshm_probability( + gamma: float, + corrections: List[int], + match_factors: List[int], + ) -> Tuple[float, float, int]: + """ + 功能: + 按匹配因子加权计算各唯一修正值的概率. + P(c) = sum(2^((MF_j - MF_best) / B_SS)) for j with correction_j == c + / sum(2^((MF_j - MF_best) / B_SS)) for all j + 参数: + gamma: PIM 查询预测. + corrections: 每个命中的修正因子列表. + match_factors: 每个命中的匹配因子列表. + 返回: + Tuple[float, float, int]: (sigma, confidence, best_correction). + """ + if len(corrections) == 0: + return gamma, 0.0, 0 + + best_mf = match_factors[0] # 已按 MF 降序排列 + b_ss = 75 + + # 计算唯一修正值的概率 + unique_corrections: List[int] = [] + for c in corrections: + if c not in unique_corrections: + unique_corrections.append(c) + + correction_probs: List[float] = [] + denominator = 0.0 + for mf in match_factors: + denominator += 2.0 ** ((mf - best_mf) / b_ss) + + for uc in unique_corrections: + numerator = 0.0 + for j, c in enumerate(corrections): + if c == uc: + numerator += 2.0 ** ((match_factors[j] - best_mf) / b_ss) + correction_probs.append(numerator / denominator if denominator > 0 else 0.0) + + # 找最高概率的修正值 + best_idx = 0 + best_prob = 0.0 + for idx, prob in enumerate(correction_probs): + if prob > best_prob: + best_prob = prob + best_idx = idx + + best_correction = unique_corrections[best_idx] + sigma = gamma + best_correction + return sigma, best_prob, best_correction + + # ================================================================== + # iHS-HM: iterative Hybrid Search Hitlist Method + # ================================================================== + + def predict_ihshm( + self, + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> iHSHMPrediction: + """ + 功能: + iHS-HM 预测. + 1. PIM_core → gamma, 确定候选范围 [gamma-18, gamma+basepeak_mz]. + 2. 批量写入 MSP (每个候选质量一个条目). + 3. 单次 MSPepSearch Hf 混合搜索. + 4. 解析 hitlist, 逐块计算 Omega. + 5. theta = argmax(Omega), I_theta = (Omega1 - Omega2) / 999. + 参数: + mz_values: 查询质谱 m/z 数组. + intensities: 查询质谱强度数组. + 返回: + iHSHMPrediction. + """ + if not self._available: + return iHSHMPrediction(status="error", message="MSPepSearch 不可用") + + if self._pim is None: + return iHSHMPrediction(status="error", message="PIMPredictor 未配置") + + # 1. PIM 预测 + 确定候选范围 + try: + norm_mz, norm_ab = PIMPredictor._normalize_spectrum(mz_values, intensities) + if len(norm_mz) == 0: + return iHSHMPrediction(status="no_spectrum", message="无有效质谱信号") + gamma = self._pim._pim_core(norm_mz, norm_ab) + except Exception as exc: + return iHSHMPrediction(status="error", message=f"PIM 预测失败: {exc}") + + # 候选范围: [gamma-18, gamma+basepeak_mz] (参考 C++ 实现) + bp_mz = 0 + bp_val = float(np.max(norm_ab)) + for i in range(len(norm_ab)): + if float(norm_ab[i]) == bp_val: + bp_mz = int(norm_mz[i]) + + min_mw = int(gamma) - 18 + max_mw = int(gamma) + bp_mz + if min_mw < 1: + min_mw = 1 + candidate_masses = list(range(min_mw, max_mw + 1)) + + if len(candidate_masses) == 0: + return iHSHMPrediction(status="error", message="候选质量范围为空") + + logger.info( + "iHS-HM 候选范围: %d-%d Da (%d 个候选), PIM=%.0f, basepeak_mz=%d", + min_mw, max_mw, len(candidate_masses), gamma, bp_mz, + ) + + # 2. 批量写入 MSP + msp_path = self._work_dir / "query_ihshm.msp" + out_path = self._work_dir / "hitlist_ihshm.txt" + self._write_batch_hs_msp(norm_mz, norm_ab, candidate_masses, msp_path) + + # 3. 单次混合搜索 + if not self._run_mspepsearch("Hf", msp_path, out_path, extra_flags=["/OutDeltaMW"]): + return iHSHMPrediction(status="error", message="MSPepSearch 混合搜索失败") + + # 4. 解析 hitlist 并计算 Omega + omega_values = self._parse_hs_hitlist_and_compute_omega( + out_path, len(candidate_masses) + ) + + if len(omega_values) == 0: + return iHSHMPrediction(status="error", message="iHS-HM 未获得有效 Omega 值") + + # 5. 找最大 Omega 对应的候选质量 + omega1 = 0.0 + omega2 = 0.0 + theta = float(candidate_masses[0]) + + for i, omega_val in enumerate(omega_values): + if omega_val > omega1: + omega2 = omega1 + omega1 = omega_val + theta = float(candidate_masses[i]) + elif omega_val > omega2: + omega2 = omega_val + + confidence = (omega1 - omega2) / 999.0 + + return iHSHMPrediction( + predicted_mw=int(round(theta)), + confidence=round(confidence, 6), + status="ok", + message=f"iHS-HM 预测成功, 范围 {min_mw}-{max_mw} Da", + ) + + # ================================================================== + # MSP 文件写入 + # ================================================================== + + @staticmethod + def _write_query_msp( + name: str, + mz_values: np.ndarray, + intensities: np.ndarray, + output_path: Path, + mw: Optional[int] = None, + ) -> None: + """ + 功能: + 写入单个查询质谱 MSP 文件. + 参数: + name: 质谱名称. + mz_values: m/z 数组 (已归一化). + intensities: 强度数组 (已归一化). + output_path: 输出文件路径. + mw: 可选分子量 (用于混合搜索). + 返回: + 无. + """ + output_path.parent.mkdir(parents=True, exist_ok=True) + lines = [f"Name: {name}"] + if mw is not None: + lines.append(f"MW: {mw}") + lines.append(f"Num Peaks: {len(mz_values)}") + + # 峰数据: "mz ab; mz ab; ..." + pairs = [] + for mz, ab in zip(mz_values, intensities): + pairs.append(f"{int(round(mz))} {int(round(ab))}") + # 每行最多 10 对 + for i in range(0, len(pairs), 10): + lines.append("; ".join(pairs[i:i + 10])) + lines.append("") + + output_path.write_text("\r\n".join(lines), encoding="ascii", errors="replace") + + @staticmethod + def _write_batch_hs_msp( + mz_values: np.ndarray, + intensities: np.ndarray, + candidate_masses: List[int], + output_path: Path, + ) -> None: + """ + 功能: + 批量写入 iHS-HM 混合搜索 MSP 文件. + 每个候选质量写入一个 MSP 条目, 共 N 个条目. + 参考 R 实现 (app.R:614-636). + 参数: + mz_values: m/z 数组 (已归一化). + intensities: 强度数组 (已归一化). + candidate_masses: 候选质量列表. + output_path: 输出文件路径. + 返回: + 无. + """ + output_path.parent.mkdir(parents=True, exist_ok=True) + + # 预生成峰数据字符串 + pairs = [] + for mz, ab in zip(mz_values, intensities): + pairs.append(f"{int(round(mz))} {int(round(ab))}") + peak_lines = [] + for i in range(0, len(pairs), 10): + peak_lines.append("; ".join(pairs[i:i + 10])) + peak_block = "\r\n".join(peak_lines) + + all_lines = [] + for mass in candidate_masses: + all_lines.append(f"Name: Query") + all_lines.append(f"MW: {mass}") + all_lines.append(f"Num Peaks: {len(mz_values)}") + all_lines.append(peak_block) + all_lines.append("") # 空行分隔 + all_lines.append("") + + output_path.write_text("\r\n".join(all_lines), encoding="ascii", errors="replace") + logger.debug("iHS-HM 批量 MSP 已写入: %s (%d 个候选)", output_path, len(candidate_masses)) + + # ================================================================== + # MSPepSearch 执行 + # ================================================================== + + def _run_mspepsearch( + self, + search_mode: str, + input_msp: Path, + output_tab: Path, + extra_flags: Optional[List[str]] = None, + ) -> bool: + """ + 功能: + 构建并执行 MSPepSearch 命令. + 通过 batch 文件调用以避免路径中 '/' 与选项冲突. + 参数: + search_mode: 搜索模式 ("Sf" / "Hf" 等). + input_msp: 输入 MSP 文件路径. + output_tab: 输出 tab 文件路径. + extra_flags: 额外的命令行标志 (如 ["/OutDeltaMW"]). + 返回: + bool, 搜索成功返回 True. + """ + # 确定库类型标志 + if self._lib_type == "MAIN": + lib_flag = "/MAIN" + elif self._lib_type == "REPL": + lib_flag = "/REPL" + else: + lib_flag = "/LIB" + + # 确定命中数 + if search_mode.startswith("S"): + hits = self._sshm_hits + else: + hits = self._ihshm_hits + + # 构建命令行各段 + cmd_parts = [ + f'"{self._exe}" {search_mode}^', + f" {lib_flag} {self._lib_path}^", + f" /HITS {hits}^", + f" /INP {input_msp}^", + " /OutNumMP^", + " /OutMW^", + ] + if extra_flags is not None: + for flag in extra_flags: + cmd_parts.append(f" {flag}^") + cmd_parts.append(f" /OUTTAB {output_tab}") + + # 写入 batch 文件 + bat_path = self._work_dir / "mspepsearch_run.bat" + bat_content = "@echo off\r\n" + "\r\n".join(cmd_parts) + "\r\n" + bat_path.write_text(bat_content, encoding="ascii") + + # 删除旧输出 + if output_tab.exists(): + output_tab.unlink() + + try: + logger.info("启动 MSPepSearch: mode=%s, input=%s", search_mode, input_msp.name) + result = subprocess.run( + ["cmd.exe", "/c", str(bat_path)], + capture_output=True, + text=True, + timeout=self._timeout, + cwd=str(self._work_dir), + ) + if result.returncode != 0: + logger.warning( + "MSPepSearch 返回非零退出码: %d, stderr: %s", + result.returncode, result.stderr[:500], + ) + # 即使退出码非零, 输出文件可能已生成 + if output_tab.exists(): + logger.info("MSPepSearch 搜索完成: %s", output_tab.name) + return True + else: + logger.error("MSPepSearch 输出文件不存在: %s", output_tab) + return False + except subprocess.TimeoutExpired: + logger.error("MSPepSearch 搜索超时 (%.0f 秒)", self._timeout) + return False + except Exception as exc: + logger.error("MSPepSearch 执行失败: %s", exc) + return False + + # ================================================================== + # Hitlist 解析 + # ================================================================== + + @staticmethod + def _parse_ss_hitlist( + hitlist_path: Path, + ) -> List[Tuple[str, int, int, int]]: + """ + 功能: + 解析 SS-HM 简单搜索命中列表. + 输出格式 (tab 分隔): + Unknown Rank Library Id Mass Lib MW MF Prob(%) Name ... + 参数: + hitlist_path: hitlist 文件路径. + 返回: + List[Tuple[str, int, int, int]]: [(name, id, lib_mw, mf), ...], + 按 Rank 升序 (MF 降序). + """ + hits: List[Tuple[str, int, int, int]] = [] + + if not hitlist_path.exists(): + logger.warning("SS hitlist 文件不存在: %s", hitlist_path) + return hits + + try: + content = hitlist_path.read_text(encoding="utf-8", errors="replace") + for line in content.splitlines(): + line = line.strip() + # 跳过注释行 (以 > 开头) 和表头行 + if line.startswith(">") or line.startswith("Unknown"): + continue + if not line: + continue + + fields = line.split("\t") + if len(fields) < 9: + continue + + try: + hit_name = fields[8].strip() # Name 列 + hit_id = int(fields[3].strip()) # Id 列 + lib_mw = int(float(fields[5].strip())) # Lib MW 列 + mf = int(float(fields[6].strip())) # MF 列 + hits.append((hit_name, hit_id, lib_mw, mf)) + except (ValueError, IndexError): + continue + + except Exception as exc: + logger.error("解析 SS hitlist 失败: %s", exc) + + logger.debug("SS hitlist 解析完成: %d 个命中", len(hits)) + return hits + + def _parse_hs_hitlist_and_compute_omega( + self, + hitlist_path: Path, + n_candidates: int, + ) -> List[float]: + """ + 功能: + 解析 iHS-HM 混合搜索命中列表并计算每个候选质量的 Omega 值. + 输出格式 (tab 分隔): + Unknown Rank Library Id Mass DeltaMW Lib MW MF(hMF) Prob NumMP + o.NumMP o.Match(sMF) o.R.Match Name ... + + 对每个候选质量, 取其 ≤ihshm_hits 个命中: + - 过滤 hMF >= mEMF + - DeltaMW==0 (isomer): isomerOmega += hMF * (hMF-mEMF)/(1000-mEMF) + - DeltaMW!=0 (cognate): cognateOmega += (hMF-sMF) * (hMF-mEMF)/(1000-mEMF) + - Omega = (isomerOmega + cognateOmega) / (N_isomers + N_cognates) + 参数: + hitlist_path: hitlist 文件路径. + n_candidates: 候选质量数. + 返回: + List[float]: 每个候选质量的 Omega 值列表. + """ + omega_values: List[float] = [0.0] * n_candidates + + if not hitlist_path.exists(): + logger.warning("HS hitlist 文件不存在: %s", hitlist_path) + return omega_values + + mEMF = self._ihshm_mEMF + hits_per_candidate = self._ihshm_hits + + try: + content = hitlist_path.read_text(encoding="utf-8", errors="replace") + data_lines: List[str] = [] + for line in content.splitlines(): + line = line.strip() + if line.startswith(">") or line.startswith("Unknown") or not line: + continue + data_lines.append(line) + + # 按候选质量分块处理 + # 每个候选最多 hits_per_candidate 行 + line_idx = 0 + for candidate_idx in range(n_candidates): + n_cognates = 0 + n_isomers = 0 + cognate_omega = 0.0 + isomer_omega = 0.0 + + hits_read = 0 + while line_idx < len(data_lines) and hits_read < hits_per_candidate: + fields = data_lines[line_idx].split("\t") + line_idx += 1 + + if len(fields) < 14: + continue + + try: + rank = int(fields[1].strip()) + except ValueError: + continue + + # 检查 rank 是否连续, 若 rank != hits_read+1 说明该候选的命中已结束 + if rank != hits_read + 1: + # 回退一行, 这行属于下一个候选 + line_idx -= 1 + break + + hits_read += 1 + + try: + delta_mw = int(float(fields[5].strip())) # DeltaMW 列 + h_mf = int(float(fields[7].strip())) # MF(hMF) 列 + s_mf = int(float(fields[11].strip())) # o.Match(sMF) 列 + except (ValueError, IndexError): + continue + + if h_mf < mEMF: + # 低于阈值的命中不参与计算, 但继续读取直到本候选结束 + continue + + weight = (h_mf - mEMF) / (1000.0 - mEMF) + + if delta_mw == 0: + # isomer: 同质量匹配 + n_isomers += 1 + isomer_omega += h_mf * weight + else: + # cognate: 不同质量匹配 + n_cognates += 1 + cognate_omega += (h_mf - s_mf) * weight + + total = n_cognates + n_isomers + if total > 0: + omega_values[candidate_idx] = (cognate_omega + isomer_omega) / total + + except Exception as exc: + logger.error("解析 HS hitlist 失败: %s", exc) + + logger.debug( + "iHS-HM Omega 计算完成: %d 个候选, 最大 Omega=%.2f", + n_candidates, max(omega_values) if omega_values else 0.0, + ) + return omega_values diff --git a/unilabos/devices/eit_analysis_station/processor/nist_library_reader.py b/unilabos/devices/eit_analysis_station/processor/nist_library_reader.py new file mode 100644 index 00000000..0dae7522 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/nist_library_reader.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 从 lib2nist 导出的 MSP 文本文件中按名称查找库谱. + 用于 SS-HM 算法对命中化合物运行 PIM 修正. +参数: + 无. +返回: + 无. +""" + +import logging +import pickle +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import numpy as np + +logger = logging.getLogger(__name__) + + +class NistLibraryReader: + """ + 功能: + 从 lib2nist 导出的 MSP 文本文件中按化合物名称查找质谱数据. + 首次加载时扫描整个 MSP 文件构建 {name: [byte_offset, ...]} 索引, 并缓存为 pickle 文件. + 同名化合物可能存在多条质谱记录, 索引保留全部偏移, 查找时返回第一条. + 参数: + msp_path: lib2nist 导出的 MSP 文件路径. + index_cache_path: 索引缓存路径, None 时自动推导为 .idx.pkl. + 返回: + 无. + """ + + def __init__( + self, + msp_path: Path, + index_cache_path: Optional[Path] = None, + ) -> None: + self._msp_path = Path(msp_path) + if index_cache_path is None: + self._index_cache_path = self._msp_path.with_suffix(".idx.pkl") + else: + self._index_cache_path = Path(index_cache_path) + + # 名称 -> 文件字节偏移列表 (同名化合物可能有多条质谱) + self._index: Dict[str, List[int]] = {} + self._total_spectra = 0 # MSP 文件中的质谱总数 + self._loaded = False + + if self._msp_path.exists(): + self._load_or_build_index() + else: + logger.warning("NIST 库 MSP 文件不存在: %s", self._msp_path) + + @property + def available(self) -> bool: + """MSP 文件和索引是否可用.""" + return self._loaded and self._total_spectra > 0 + + @property + def spectrum_count(self) -> int: + """索引中的质谱总数 (含同名重复).""" + return self._total_spectra + + @property + def unique_name_count(self) -> int: + """索引中的唯一化合物名称数.""" + return len(self._index) + + # ------------------------------------------------------------------ + # 索引管理 + # ------------------------------------------------------------------ + + def _load_or_build_index(self) -> None: + """ + 功能: + 加载或构建索引. 优先从缓存加载, 缓存不存在或过期时重建. + 参数: + 无. + 返回: + 无. + """ + if self._try_load_cache(): + return + self._build_index() + self._save_cache() + + def _try_load_cache(self) -> bool: + """ + 功能: + 尝试从 pickle 缓存加载索引. + 缓存有效条件: 文件存在且修改时间晚于 MSP 文件. + 参数: + 无. + 返回: + bool, 加载成功返回 True. + """ + if not self._index_cache_path.exists(): + return False + + # 缓存修改时间必须晚于 MSP 文件 + cache_mtime = self._index_cache_path.stat().st_mtime + msp_mtime = self._msp_path.stat().st_mtime + if cache_mtime < msp_mtime: + logger.info("索引缓存已过期, 将重建: %s", self._index_cache_path) + return False + + try: + with self._index_cache_path.open("rb") as f: + cached = pickle.load(f) + + # 兼容旧格式 (Dict[str, int]) 和新格式 (Dict[str, List[int]]) + if isinstance(cached, dict): + sample_val = next(iter(cached.values()), None) if cached else None + if isinstance(sample_val, list): + # 新格式: Dict[str, List[int]] + self._index = cached + else: + # 旧格式: Dict[str, int] -> 转换为新格式 + logger.info("检测到旧格式索引缓存, 将重建") + return False + else: + logger.warning("索引缓存格式异常, 将重建") + return False + + self._total_spectra = sum(len(offsets) for offsets in self._index.values()) + self._loaded = True + logger.info( + "已从缓存加载 NIST 库索引: %d 个质谱, %d 个唯一名称, 来源: %s", + self._total_spectra, len(self._index), self._index_cache_path, + ) + return True + except Exception as exc: + logger.warning("加载索引缓存失败, 将重建: %s", exc) + return False + + def _build_index(self) -> None: + """ + 功能: + 扫描整个 MSP 文件, 记录每个 "Name: xxx" 行的字节偏移. + 同名化合物的多条记录全部保留. + 以二进制模式读取以获取精确字节偏移. + 参数: + 无. + 返回: + 无. + """ + logger.info("开始构建 NIST 库索引, MSP 文件: %s", self._msp_path) + self._index.clear() + total = 0 + + try: + with self._msp_path.open("rb") as f: + while True: + offset = f.tell() + raw_line = f.readline() + if raw_line == b"": + break # EOF + + # 检测 "Name: " 开头行 + stripped = raw_line.strip() + if stripped.startswith(b"Name:") or stripped.startswith(b"NAME:"): + # 提取名称 + name = stripped.split(b":", 1)[1].strip().decode( + "utf-8", errors="replace" + ) + if name: + self._index.setdefault(name, []).append(offset) + total += 1 + + self._total_spectra = total + self._loaded = True + logger.info( + "NIST 库索引构建完成: %d 个质谱, %d 个唯一名称", + total, len(self._index), + ) + except Exception as exc: + logger.error("构建 NIST 库索引失败: %s", exc) + self._loaded = False + + def _save_cache(self) -> None: + """ + 功能: + 将索引保存为 pickle 缓存文件. + 参数: + 无. + 返回: + 无. + """ + try: + self._index_cache_path.parent.mkdir(parents=True, exist_ok=True) + with self._index_cache_path.open("wb") as f: + pickle.dump(self._index, f, protocol=pickle.HIGHEST_PROTOCOL) + logger.info("索引缓存已保存: %s", self._index_cache_path) + except Exception as exc: + logger.warning("保存索引缓存失败: %s", exc) + + # ------------------------------------------------------------------ + # 质谱查找 + # ------------------------------------------------------------------ + + def get_spectrum( + self, + name: str, + ) -> Optional[Tuple[np.ndarray, np.ndarray, int]]: + """ + 功能: + 按化合物名称查找质谱, 返回 (mz, abundance, mw). + 参数: + name: 化合物名称 (与 MSP 文件中 Name 字段匹配). + 返回: + Optional[Tuple[np.ndarray, np.ndarray, int]]: + (mz 数组, 强度数组, 分子量). + 未找到或解析失败返回 None. + """ + if not self._loaded: + return None + + offsets = self._index.get(name) + if offsets is None or len(offsets) == 0: + return None + + # 返回第一条同名质谱 + return self._read_spectrum_at_offset(offsets[0]) + + def _read_spectrum_at_offset( + self, + offset: int, + ) -> Optional[Tuple[np.ndarray, np.ndarray, int]]: + """ + 功能: + 从 MSP 文件指定偏移处读取一个质谱记录. + MSP 格式: + Name: xxx + MW: 383 + Num Peaks: 176 + 41 91; 42 15; 43 16; ... + 参数: + offset: 文件字节偏移 (Name 行起始位置). + 返回: + Optional[Tuple[np.ndarray, np.ndarray, int]]: + (mz, abundance, mw), 解析失败返回 None. + """ + try: + mw = 0 + num_peaks = 0 + reading_peaks = False + mz_list: List[float] = [] + ab_list: List[float] = [] + + with self._msp_path.open("r", encoding="utf-8", errors="replace") as f: + f.seek(offset) + for line in f: + stripped = line.strip() + + # 空行表示记录结束 + if stripped == "" and reading_peaks: + break + + upper = stripped.upper() + if upper.startswith("NAME:"): + continue + + if upper.startswith("MW:"): + try: + mw = int(stripped.split(":", 1)[1].strip()) + except ValueError: + pass + continue + + if upper.startswith("NUM PEAKS:"): + try: + num_peaks = int(stripped.split(":", 1)[1].strip()) + except ValueError: + pass + reading_peaks = True + continue + + # 跳过其他元数据行 (如 Formula, InChIKey 等) + if not reading_peaks: + continue + + # 解析峰数据行, 支持两种格式: + # 1. "mz ab; mz ab; ..." (分号分隔) + # 2. "mz ab\nmz ab\n..." (每行一对) + self._parse_peak_line(stripped, mz_list, ab_list) + + # 遇到下一个 Name 行则停止 + if upper.startswith("NAME:"): + break + + if len(mz_list) == 0: + return None + + return ( + np.array(mz_list, dtype=float), + np.array(ab_list, dtype=float), + mw, + ) + except Exception as exc: + logger.warning("读取偏移 %d 处质谱失败: %s", offset, exc) + return None + + @staticmethod + def _parse_peak_line( + line: str, + mz_list: List[float], + ab_list: List[float], + ) -> None: + """ + 功能: + 解析单行峰数据, 支持分号分隔和空格分隔两种格式. + "41 91; 42 15; 43 16" → [(41, 91), (42, 15), (43, 16)] + "41 91" → [(41, 91)] + "(41,91) (42,15)" → [(41, 91), (42, 15)] + 参数: + line: 峰数据行文本. + mz_list: m/z 值累加列表 (输出参数). + ab_list: 强度值累加列表 (输出参数). + 返回: + 无. + """ + if not line: + return + + # 分号分隔的情况 + if ";" in line: + segments = line.split(";") + else: + segments = [line] + + for seg in segments: + seg = seg.strip() + if not seg: + continue + + # 支持 "mz ab" 和 "mz\tab" 格式 + parts = seg.replace("\t", " ").split() + if len(parts) >= 2: + try: + mz_val = float(parts[0]) + ab_val = float(parts[1]) + mz_list.append(mz_val) + ab_list.append(ab_val) + except ValueError: + continue diff --git a/unilabos/devices/eit_analysis_station/processor/nist_matcher.py b/unilabos/devices/eit_analysis_station/processor/nist_matcher.py new file mode 100644 index 00000000..a3974bdb --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/nist_matcher.py @@ -0,0 +1,868 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 对峰的质谱数据进行 NIST 谱库匹配. + 实现策略 (按优先级): + 1. 调用本地 NIST MS Search 程序的文件自动化接口 (AUTOIMP.MSD 协议). + 2. 解析 .D/Results/Qual/ 中 MassHunter 已生成的定性结果 (降级方案). +参数: + 无. +返回: + 无. +""" + +import logging +import os +import re +import shutil +import subprocess +import struct +import time +from dataclasses import dataclass, field +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import numpy as np + +logger = logging.getLogger(__name__) + + +@dataclass +class CompoundMatch: + """ + 功能: + 存储单个化合物的谱库匹配结果. + 参数: + compound_name: 化合物名称. + inchikey: InChIKey, 用于结构回退时优先精确定位 PubChem 记录. + cas_number: CAS 注册号. + match_score: 匹配度 (0-100), 取 MF (Match Factor) 的百分制值. + reverse_match_score: 反向匹配度 (0-100), 取 RMF. + probability: NIST 概率匹配度 (0-100). + formula: 分子式. + mw: 分子量. + library: 匹配来源谱库名称. + nist_id: NIST 库命中编号 (Id 字段). + 返回: + CompoundMatch. + """ + compound_name: str = "" + inchikey: str = "" + cas_number: str = "" + match_score: float = 0.0 + reverse_match_score: float = 0.0 + probability: float = 0.0 + formula: str = "" + mw: float = 0.0 + library: str = "" + nist_id: Optional[int] = None + + +class NISTMatcher: + """ + 功能: + 对峰的质谱数据进行 NIST 谱库匹配. + 核心方案: 通过 NIST MS Search 的文件自动化接口批量搜索质谱. + 降级方案: 从 MassHunter 导出的 CSV 报告中提取已有定性结果. + 参数: + nist_path: NIST MS Search 安装目录 (含 nistms$.exe 的目录). + max_hits: 每个质谱返回的最大匹配数. + search_timeout: 单次搜索等待超时时间(秒). + 返回: + 无. + """ + + def __init__( + self, + nist_path: Optional[Path] = None, + max_hits: int = 5, + search_timeout: float = 60.0, + ) -> None: + self._nist_path = nist_path + self._max_hits = max_hits + self._search_timeout = search_timeout + + # 验证 NIST MS Search 安装 + if self._nist_path is not None: + self._nistms_exe = self._nist_path / "nistms$.exe" + self._autoimp_path = self._nist_path / "AUTOIMP.MSD" + self._srcreslt_path = self._nist_path / "SRCRESLT.TXT" + self._srcready_path = self._nist_path / "SRCREADY.TXT" + + if not self._nistms_exe.exists(): + logger.warning("NIST MS Search 自动化程序不存在: %s", self._nistms_exe) + self._nist_path = None + elif not self._autoimp_path.exists(): + logger.warning("AUTOIMP.MSD 不存在: %s", self._autoimp_path) + self._nist_path = None + else: + # 读取 AUTOIMP.MSD 获取第二级定位文件路径 + self._filespec_path = Path( + self._autoimp_path.read_text(encoding="ascii", errors="replace").strip() + ) + logger.info( + "NIST MS Search 就绪: %s, FILESPEC: %s", + self._nistms_exe, self._filespec_path + ) + + @property + def nist_available(self) -> bool: + """NIST MS Search 自动化接口是否可用.""" + return self._nist_path is not None + + # ------------------------------------------------------------------ + # 核心方案: NIST MS Search 文件自动化接口 + # ------------------------------------------------------------------ + + def _write_msp_file( + self, + spectra: List[Tuple[str, np.ndarray, np.ndarray]], + output_path: Path, + ) -> None: + """ + 功能: + 将多个质谱写入 NIST MSP 文本文件格式. + 每个质谱包含 Name, Num Peaks, 以及 m/z-intensity 对. + 参数: + spectra: [(名称, m/z数组, intensity数组), ...]. + output_path: MSP 文件输出路径. + 返回: + 无. + """ + lines = [] + for name, mz_values, intensities in spectra: + # 归一化强度到 0-999 (NIST 标准范围) + max_int = intensities.max() if len(intensities) > 0 else 1.0 + if max_int <= 0: + max_int = 1.0 + norm_intensities = (intensities / max_int * 999).astype(int) + + lines.append(f"Name: {name}") + lines.append(f"Num Peaks: {len(mz_values)}") + + # 每行最多 5 个 m/z-intensity 对 + pairs = [] + for mz, inten in zip(mz_values, norm_intensities): + pairs.append(f"{int(round(mz))} {max(inten, 1)}") + # 每行 5 对 + for i in range(0, len(pairs), 5): + lines.append("; ".join(pairs[i:i + 5])) + + lines.append("") # 空行分隔不同质谱 + + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text("\r\n".join(lines), encoding="ascii", errors="replace") + logger.info("MSP 文件已写入: %s (%d 个质谱)", output_path, len(spectra)) + + def _write_filespec(self, msp_path: Path) -> None: + """ + 功能: + 写入第二级定位文件 (FILESPEC.TXT), 指向 MSP 数据文件. + 格式: " OVERWRITE" + OVERWRITE 表示替换 Spec List 中已有的质谱. + 参数: + msp_path: MSP 文件的完整路径. + 返回: + 无. + """ + content = f"{msp_path} OVERWRITE" + self._filespec_path.parent.mkdir(parents=True, exist_ok=True) + self._filespec_path.write_text(content, encoding="ascii", errors="replace") + logger.debug("FILESPEC 写入: %s", content) + + def _cleanup_result_files(self) -> None: + """ + 功能: + 清理上一次搜索的结果文件, 避免读取到旧数据. + 参数: + 无. + 返回: + 无. + """ + for path in (self._srcreslt_path, self._srcready_path): + if path.exists(): + path.unlink() + logger.debug("已清理: %s", path) + + def _normalize_max_hits(self) -> int: + """ + 功能: + 归一化 max_hits 配置值, 确保用于 NIST 配置同步的命中数量至少为 1. + 参数: + 无. + 返回: + int, 归一化后的命中数量. + """ + normalized_hits = self._max_hits + if normalized_hits < 1: + logger.warning( + "NIST 命中数量配置无效: max_hits=%s, 已强制修正为 1", + self._max_hits, + ) + normalized_hits = 1 + return normalized_hits + + def _limit_match_list(self, hits: List[CompoundMatch]) -> List[CompoundMatch]: + """ + 功能: + 按当前 max_hits 配置裁剪单个质谱的命中列表. + 参数: + hits: 单个质谱对应的命中列表. + 返回: + List[CompoundMatch], 裁剪后的命中列表. + """ + normalized_hits = self._normalize_max_hits() + return list(hits[:normalized_hits]) + + def _limit_match_dict( + self, + results: Dict, + ) -> Dict: + """ + 功能: + 按当前 max_hits 配置裁剪多组命中结果. + 参数: + results: 任意键 -> 命中列表 的字典. + 返回: + Dict, 裁剪后的结果字典. + """ + limited_results: Dict = {} + for key, hits in results.items(): + limited_results[key] = self._limit_match_list(hits) + return limited_results + + @staticmethod + def _find_section_range( + lines: List[str], + section_name: str, + ) -> Tuple[Optional[int], Optional[int]]: + """ + 功能: + 在 INI 行列表中定位指定 section 的起止下标. + 返回区间为左闭右开形式 [start, end). + 参数: + lines: INI 文件按行拆分后的文本列表. + section_name: 目标 section 名称, 不包含方括号. + 返回: + Tuple[Optional[int], Optional[int]]: + section 起始行下标和结束行下标. + 当 section 不存在时, 两个值均为 None. + """ + section_start: Optional[int] = None + section_end: Optional[int] = None + + for line_index, raw_line in enumerate(lines): + stripped_line = raw_line.strip() + if not stripped_line.startswith("["): + continue + if not stripped_line.endswith("]"): + continue + + current_section = stripped_line[1:-1].strip() + if section_start is None: + if current_section == section_name: + section_start = line_index + continue + + section_end = line_index + break + + if section_start is None: + return None, None + if section_end is None: + section_end = len(lines) + return section_start, section_end + + def _set_ini_key_value( + self, + lines: List[str], + section_name: str, + key_name: str, + key_value: int, + ) -> Tuple[List[str], bool, bool]: + """ + 功能: + 在指定 section 中精确写入 key=value. + 1. key 存在时更新值. + 2. key 不存在时追加到 section 末尾. + 3. section 不存在时不改动内容. + 参数: + lines: INI 文件按行拆分后的文本列表. + section_name: 目标 section 名称. + key_name: 目标 key 名称. + key_value: 目标 key 值. + 返回: + Tuple[List[str], bool, bool]: + 更新后的行列表, 是否发生变更, section 是否存在. + """ + section_start, section_end = self._find_section_range(lines, section_name) + if section_start is None: + return lines, False, False + if section_end is None: + return lines, False, False + + target_value = str(key_value) + for line_index in range(section_start + 1, section_end): + raw_line = lines[line_index] + stripped_line = raw_line.strip() + + if not stripped_line: + continue + if stripped_line.startswith(";"): + continue + if stripped_line.startswith("#"): + continue + if "=" not in raw_line: + continue + + left_part, _, right_part = raw_line.partition("=") + if left_part.strip() != key_name: + continue + + current_value = right_part.strip() + if current_value == target_value: + return lines, False, True + + leading_spaces = raw_line[: len(raw_line) - len(raw_line.lstrip())] + lines[line_index] = f"{leading_spaces}{key_name}={target_value}" + return lines, True, True + + lines.insert(section_end, f"{key_name}={target_value}") + return lines, True, True + + def _sync_single_ini_hits(self, ini_path: Path, normalized_hits: int) -> None: + """ + 功能: + 将单个 INI 文件中与命中条数相关的配置同步为指定值. + 同步目标: + 1. [Search Options] Hits to Print. + 2. [REPORT] First Hits Number. + 3. [Compare List] Hits. + 参数: + ini_path: 目标 INI 文件路径. + normalized_hits: 归一化后的命中数量. + 返回: + 无. + """ + if not ini_path.exists(): + logger.warning("NIST 配置文件不存在, 跳过同步: %s", ini_path) + return + + try: + ini_text = ini_path.read_text(encoding="utf-8", errors="replace") + except Exception as exc: + logger.warning("读取 NIST 配置文件失败, 已跳过: %s, 错误: %s", ini_path, exc) + return + + line_ending = "\n" + if "\r\n" in ini_text: + line_ending = "\r\n" + keep_trailing_newline = ini_text.endswith("\n") or ini_text.endswith("\r") + + ini_lines = ini_text.splitlines() + has_changed = False + + target_keys = [ + ("Search Options", "Hits to Print"), + ("REPORT", "First Hits Number"), + ("Compare List", "Hits"), + ] + for section_name, key_name in target_keys: + ini_lines, key_changed, section_exists = self._set_ini_key_value( + lines=ini_lines, + section_name=section_name, + key_name=key_name, + key_value=normalized_hits, + ) + if section_exists is False: + logger.warning( + "NIST 配置文件缺少 [%s], 跳过键 %s: %s", + section_name, + key_name, + ini_path, + ) + if key_changed is True: + has_changed = True + + if has_changed is False: + logger.debug("NIST 配置文件无需更新: %s", ini_path) + return + + new_text = line_ending.join(ini_lines) + if keep_trailing_newline: + new_text = f"{new_text}{line_ending}" + + try: + ini_path.write_text(new_text, encoding="utf-8") + logger.info("已同步 NIST 命中配置: %s, hits=%d", ini_path.name, normalized_hits) + except Exception as exc: + logger.warning("写入 NIST 配置文件失败, 已跳过: %s, 错误: %s", ini_path, exc) + + def _update_ini_hits(self) -> None: + """ + 功能: + 同步 NIST 命中条数配置到多个 INI 文件. + 同步目标文件: + 1. nistms.INI. + 2. settings_EI.INI. + 3. settings_MSMS.INI. + 4. settings_PEP.INI. + 同步目标键: + 1. [Search Options] Hits to Print. + 2. [REPORT] First Hits Number. + 3. [Compare List] Hits. + 参数: + 无. + 返回: + 无. + """ + if self._nist_path is None: + logger.warning("NIST 路径不可用, 跳过命中条数配置同步") + return + + normalized_hits = self._normalize_max_hits() + ini_names = [ + "nistms.INI", + "settings_EI.INI", + "settings_MSMS.INI", + "settings_PEP.INI", + ] + for ini_name in ini_names: + ini_path = self._nist_path / ini_name + self._sync_single_ini_hits(ini_path=ini_path, normalized_hits=normalized_hits) + + def _launch_nist_search(self) -> bool: + """ + 功能: + 启动 nistms$.exe 并等待搜索完成. + 使用 /PAR=2 参数启动自动搜索模式, 程序会: + 1. 读取 AUTOIMP.MSD -> FILESPEC.TXT -> MSP 文件 + 2. 导入质谱到 Spec List + 3. 对每个质谱执行谱库搜索 + 4. 将结果写入 SRCRESLT.TXT + 5. 创建 SRCREADY.TXT 表示搜索完成 + 参数: + 无. + 返回: + bool: True 表示搜索完成, False 表示超时或失败. + """ + try: + # 启动 nistms$.exe /PAR=2 (自动搜索模式) + cmd = [str(self._nistms_exe), "/PAR=2"] + logger.info("启动 NIST MS Search: %s", " ".join(cmd)) + subprocess.Popen( + cmd, + cwd=str(self._nist_path), + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + except Exception as e: + logger.error("启动 NIST MS Search 失败: %s", e) + return False + + # 等待 SRCREADY.TXT 出现, 表示搜索完成 + start_time = time.time() + while time.time() - start_time < self._search_timeout: + if self._srcready_path.exists(): + elapsed = time.time() - start_time + logger.info("NIST 搜索完成, 耗时 %.1f 秒", elapsed) + return True + time.sleep(0.5) + + logger.warning("NIST 搜索超时 (%.0f 秒)", self._search_timeout) + return False + + def _parse_srcreslt(self) -> Dict[str, List[CompoundMatch]]: + """ + 功能: + 解析 NIST MS Search 输出的 SRCRESLT.TXT 文件. + 文件格式 (每行一个 Hit): + Hit 1 : <>; <>; MF: 824; RMF: 856; + Prob: 98.10; CAS: 74-95-3; Mw: 172; Lib: <>; Id: 10054; RI: 0.2 + Unknown 行分隔不同查询质谱: + Unknown: <> + 参数: + 无. + 返回: + Dict[str, List[CompoundMatch]]: 质谱名称 -> 匹配结果列表. + """ + results: Dict[str, List[CompoundMatch]] = {} + + if not self._srcreslt_path.exists(): + logger.warning("SRCRESLT.TXT 不存在: %s", self._srcreslt_path) + return results + + content = self._srcreslt_path.read_text(encoding="utf-8", errors="replace") + current_name = "" + + for line in content.splitlines(): + line = line.strip() + if not line: + continue + + # 检测 Unknown 行: "Unknown: <>" 或 "Unknown: name Compound in Library Factor = ..." + unknown_match = re.match(r"Unknown:\s*(?:<<)?(.+?)(?:>>)?\s*(?:Compound\s+in\s+Library.*)?$", line) + if unknown_match: + current_name = unknown_match.group(1).strip() + if current_name not in results: + results[current_name] = [] + continue + + # 检测 Hit 行 + hit_match = re.match(r"Hit\s+\d+\s*:", line) + if hit_match is None: + continue + + # 提取字段 + compound = self._extract_field(line, r":\s*<<(.+?)>>") + formula = self._extract_field(line, r">>\s*;\s*<<(.+?)>>") + mf = self._extract_float_field(line, r"MF:\s*([\d.]+)") + rmf = self._extract_float_field(line, r"RMF:\s*([\d.]+)") + prob = self._extract_float_field(line, r"Prob:\s*([\d.]+)") + cas_raw = self._extract_field(line, r"CAS:\s*([\d-]+)") + mw = self._extract_float_field(line, r"Mw:\s*([\d.]+)") + lib = self._extract_field(line, r"Lib:\s*<<(.+?)>>") + nist_id = self._extract_int_field(line, r"Id:\s*(\d+)") + + # CAS=0 表示无有效 CAS, 统一按空字符串处理. + cas = cas_raw or "" + if cas in ("0", "0-00-0"): + logger.debug( + "NIST 命中 CAS 无效(CAS=0), Unknown=%s, 化合物=%s, 行=%s", + current_name or "(未知)", + compound or "(未知)", + line, + ) + cas = "" + + # Id 缺失时记录可追踪日志, 便于后续结构链路排查. + if nist_id is None: + logger.debug( + "NIST 命中缺少 Id 字段, Unknown=%s, 化合物=%s, 行=%s", + current_name or "(未知)", + compound or "(未知)", + line, + ) + + match = CompoundMatch( + compound_name=compound or "", + cas_number=cas or "", + match_score=mf / 10.0 if mf else 0.0, # MF 0-1000 转为 0-100 + reverse_match_score=rmf / 10.0 if rmf else 0.0, + probability=prob or 0.0, + formula=formula or "", + mw=mw or 0.0, + library=lib or "", + nist_id=nist_id, + ) + + if current_name not in results: + results[current_name] = [] + results[current_name].append(match) + + logger.info("SRCRESLT 解析完成: %d 个质谱, 共 %d 个匹配", + len(results), sum(len(v) for v in results.values())) + return results + + @staticmethod + def _extract_field(text: str, pattern: str) -> Optional[str]: + """用正则从文本中提取字符串字段.""" + m = re.search(pattern, text) + return m.group(1).strip() if m else None + + @staticmethod + def _extract_float_field(text: str, pattern: str) -> Optional[float]: + """用正则从文本中提取浮点字段.""" + m = re.search(pattern, text) + if m: + try: + return float(m.group(1)) + except ValueError: + pass + return None + + @staticmethod + def _extract_int_field(text: str, pattern: str) -> Optional[int]: + """用正则从文本中提取整数字段.""" + m = re.search(pattern, text) + if m: + try: + return int(m.group(1)) + except ValueError: + pass + return None + + def search_spectra( + self, + spectra: List[Tuple[str, np.ndarray, np.ndarray]], + work_dir: Optional[Path] = None, + ) -> Dict[str, List[CompoundMatch]]: + """ + 功能: + 通过 NIST MS Search 文件自动化接口批量搜索多个质谱. + 完整流程: + 1. 将质谱写入 MSP 文件. + 2. 写入 FILESPEC.TXT 指向 MSP 文件. + 3. 清理旧的结果文件. + 4. 启动 nistms$.exe /PAR=2 自动搜索. + 5. 等待 SRCREADY.TXT 信号. + 6. 解析 SRCRESLT.TXT 获取匹配结果. + 参数: + spectra: [(名称, m/z数组, intensity数组), ...]. + work_dir: 工作目录, 用于存放临时 MSP 文件. 默认使用 NIST 目录. + 返回: + Dict[str, List[CompoundMatch]]: 质谱名称 -> 匹配结果列表. + """ + if not self.nist_available: + logger.warning("NIST MS Search 不可用, 无法执行搜索") + return {} + + if not spectra: + return {} + + # 确定 MSP 文件路径 + if work_dir is None: + work_dir = self._nist_path + msp_path = work_dir / "autosearch_spectra.msp" + + # 写入 MSP 文件 + self._write_msp_file(spectra, msp_path) + + # 写入 FILESPEC + self._write_filespec(msp_path) + + # 清理旧结果 + self._cleanup_result_files() + + # 确保 NIST 返回足够数量的匹配结果 + self._update_ini_hits() + + # 启动搜索并等待 + if not self._launch_nist_search(): + logger.warning("NIST 搜索未完成, 尝试读取已有结果") + + # 解析结果 + return self._limit_match_dict(self._parse_srcreslt()) + + def search_single_spectrum( + self, + name: str, + mz_values: np.ndarray, + intensities: np.ndarray, + ) -> List[CompoundMatch]: + """ + 功能: + 搜索单个质谱, 返回匹配结果列表. + 参数: + name: 质谱标识名称. + mz_values: m/z 值数组. + intensities: 强度数组. + 返回: + List[CompoundMatch]: 匹配结果, 按匹配度降序排列. + """ + results = self.search_spectra([(name, mz_values, intensities)]) + return results.get(name, []) + + # ------------------------------------------------------------------ + # 批量匹配: 对 .D 目录中所有 TIC 峰进行 NIST 搜索 + # ------------------------------------------------------------------ + + def match_peaks_with_nist( + self, + d_dir: Path, + peak_results: List["PeakResult"], + reader: "GCMSDataReader", + avg_scans: int = 3, + ) -> Dict[float, List[CompoundMatch]]: + """ + 功能: + 对 .D 目录中指定峰逐一提取 apex 质谱并通过 NIST 搜索. + 使用峰边界范围内 TIC 最大的扫描, 并平均周围扫描以提升信噪比. + 参数: + d_dir: .D 目录路径. + peak_results: 峰检测积分结果列表 (含 start_time/end_time/retention_time). + reader: GCMSDataReader 实例. + avg_scans: 以 apex 为中心的平均扫描数. + 返回: + Dict[float, List[CompoundMatch]]: 保留时间 -> 按 max_hits 裁剪后的匹配结果列表. + """ + if not self.nist_available: + logger.info("NIST 不可用, 跳过峰匹配") + return {} + + # 收集所有峰的质谱 + spectra: List[Tuple[str, np.ndarray, np.ndarray]] = [] + rt_name_map: Dict[str, float] = {} + + for peak in peak_results: + try: + mz_values, intensities = reader.read_ms_spectra_at_peak( + d_dir, peak.start_time, peak.end_time, avg_scans + ) + if len(mz_values) == 0: + continue + # 用 RT 作为标识名 + name = f"RT_{peak.retention_time:.3f}" + spectra.append((name, mz_values, intensities)) + rt_name_map[name] = peak.retention_time + except Exception as e: + logger.warning("提取 RT=%.3f 处质谱失败: %s", peak.retention_time, e) + + if not spectra: + logger.info("无有效质谱数据, 跳过 NIST 搜索") + return {} + + logger.info("准备搜索 %d 个峰的质谱", len(spectra)) + + # 批量搜索 + all_results = self.search_spectra(spectra, work_dir=d_dir.parent) + + # 保存 NIST 结果文件副本, 避免后续样品搜索覆盖 + self._saved_srcreslt: Optional[Path] = None + if self._srcreslt_path.exists(): + saved_path = d_dir.parent / f"SRCRESLT_{d_dir.stem}.TXT" + try: + shutil.copy2(str(self._srcreslt_path), str(saved_path)) + self._saved_srcreslt = saved_path + logger.debug("NIST 结果已备份: %s", saved_path) + except Exception as e: + logger.warning("备份 SRCRESLT.TXT 失败: %s", e) + + # 映射回保留时间, 每个峰保留 setting.py 配置的命中上限 + matches: Dict[float, List[CompoundMatch]] = {} + for name, hits in all_results.items(): + if name in rt_name_map and hits: + rt = rt_name_map[name] + matches[rt] = hits + + logger.info("NIST 峰匹配完成: %d/%d 个峰有匹配结果", + len(matches), len(spectra)) + return matches + + # ------------------------------------------------------------------ + # 降级方案: MassHunter 报告文件 + # ------------------------------------------------------------------ + + def match_from_qual_results(self, d_dir: Path) -> Dict[float, List[CompoundMatch]]: + """ + 功能: + 从 MassHunter 定性结果中提取化合物匹配信息. + MassHunter 的 QualResult.bin 为二进制格式, + 当前采用降级策略: 若 MassHunter 已在 .D 目录下 + 生成了 CSV 或 TXT 格式的报告文件, 则解析这些文件. + 否则返回空字典, 由上层调用者决定后续处理. + 参数: + d_dir: .D 目录路径. + 返回: + Dict[float, List[CompoundMatch]]: 保留时间 -> 化合物匹配结果列表. + 返回空字典表示无可用的定性结果. + """ + results: Dict[float, List[CompoundMatch]] = {} + + # 策略1: 尝试读取 MassHunter 导出的报告文件 + report_files = list(d_dir.glob("*.report.csv")) + list(d_dir.glob("Report*.csv")) + if report_files: + results = self._limit_match_dict(self._parse_masshunter_report_csv(report_files[0])) + if results: + logger.info("从 MassHunter 报告文件提取 %d 个化合物匹配", len(results)) + return results + + # 策略2: 检查 Results/Qual 目录是否存在 + qual_dir = d_dir / "Results" / "Qual" + if qual_dir.exists(): + logger.info( + "MassHunter 定性结果目录存在但 QualResult.bin 为二进制格式, " + "建议使用 NIST MS Search 自动化接口进行定性." + ) + + return results + + def _parse_masshunter_report_csv(self, csv_path: Path) -> Dict[float, List[CompoundMatch]]: + """ + 功能: + 解析 MassHunter 导出的 CSV 格式报告文件. + 常见列名: RT, Name, CAS#, Match Score, Formula, MW 等. + 参数: + csv_path: CSV 报告文件路径. + 返回: + Dict[float, List[CompoundMatch]]: 保留时间 -> 化合物匹配结果列表. + """ + import csv + + results: Dict[float, List[CompoundMatch]] = {} + + try: + with csv_path.open("r", encoding="utf-8", errors="replace") as f: + reader = csv.DictReader(f) + for row in reader: + # MassHunter 报告的列名可能因版本而异, 逐一尝试 + rt = self._extract_float_from_row(row, ["RT", "R.T.", "Retention Time", "RetTime"]) + if rt is None: + continue + + name = self._extract_str_from_row(row, ["Name", "Compound Name", "Hit Name", "Library Hit"]) + inchikey = self._extract_str_from_row(row, ["InChIKey", "InChI Key"]) + cas = self._extract_str_from_row(row, ["CAS#", "CAS", "CAS Number"]) + score = self._extract_float_from_row(row, ["Match Score", "Score", "Match", "Quality"]) + formula = self._extract_str_from_row(row, ["Formula", "Molecular Formula"]) + mw = self._extract_float_from_row(row, ["MW", "Molecular Weight", "Mol. Weight"]) + + results.setdefault(rt, []).append(CompoundMatch( + compound_name=name or "", + inchikey=inchikey or "", + cas_number=cas or "", + match_score=score or 0.0, + formula=formula or "", + mw=mw or 0.0, + )) + + except Exception as e: + logger.warning("解析 MassHunter 报告文件失败: %s - %s", csv_path, e) + + return results + + @staticmethod + def _extract_str_from_row(row: Dict, keys: List[str]) -> Optional[str]: + """从 CSV 行中按多个候选列名提取字符串值.""" + for key in keys: + if key in row and row[key].strip(): + return row[key].strip() + return None + + @staticmethod + def _extract_float_from_row(row: Dict, keys: List[str]) -> Optional[float]: + """从 CSV 行中按多个候选列名提取浮点值.""" + for key in keys: + if key in row and row[key].strip(): + try: + return float(row[key].strip()) + except ValueError: + continue + return None + + def find_closest_match( + self, + target_rt: float, + matches: Dict[float, List[CompoundMatch]], + tolerance: float = 0.05, + ) -> Optional[List[CompoundMatch]]: + """ + 功能: + 在已有的化合物匹配字典中, 按保留时间查找最接近的匹配列表. + 参数: + target_rt: 目标保留时间 (min). + matches: 保留时间 -> 化合物匹配列表字典. + tolerance: 保留时间容差 (min). + 返回: + Optional[List[CompoundMatch]]: 最接近的匹配列表, 超出容差返回 None. + """ + if not matches: + return None + + closest_rt = min(matches.keys(), key=lambda rt: abs(rt - target_rt)) + if abs(closest_rt - target_rt) <= tolerance: + return matches[closest_rt] + + return None + diff --git a/unilabos/devices/eit_analysis_station/processor/peak_integrator.py b/unilabos/devices/eit_analysis_station/processor/peak_integrator.py new file mode 100644 index 00000000..86ececd8 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/peak_integrator.py @@ -0,0 +1,1542 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 对色谱信号(TIC/FID)执行峰检测与积分, 支持 legacy, robust_v2, robust_v3 和 gcpy 模式. +参数: + 无. +返回: + 无. +""" + +import logging +from dataclasses import dataclass +from typing import List, Optional, Tuple + +import numpy as np +from scipy import sparse +from scipy.ndimage import percentile_filter +from scipy.signal import find_peaks, peak_prominences, peak_widths, savgol_filter +from scipy.sparse.linalg import spsolve + +logger = logging.getLogger(__name__) + + +@dataclass +class PeakResult: + """ + 功能: + 存储单个色谱峰的检测与积分结果. + 参数: + peak_index: 峰顶在原始数组中的索引. + retention_time: 峰顶保留时间(min). + height: 峰高(原始强度). + area: 峰面积(局部基线梯形积分). + area_percent: 面积百分比(%). + start_time: 峰起始时间(min). + end_time: 峰结束时间(min). + width: 峰宽(min). + 返回: + PeakResult. + """ + + peak_index: int + retention_time: float + height: float + area: float + area_percent: float = 0.0 + start_time: float = 0.0 + end_time: float = 0.0 + width: float = 0.0 + + +class PeakIntegrator: + """ + 功能: + 执行色谱峰检测与积分. + + - legacy: 兼容历史流程(ALS/非ALS + valley/peak_widths). + - robust_v2: 滚动分位数基线(可回退 ALS) + 噪声阈值 + 自适应限宽边界. + - robust_v3: robust_v2 + find_peaks 后相邻假峰过滤. + - gcpy: Whittaker 平滑 + 线性插值基线扣除. + + 参数: + smoothing_window: Savitzky-Golay 平滑窗口(奇数). + prominence: 峰检测最小 prominence. + min_distance: 相邻峰最小距离(点数). + width_rel_height: legacy 模式下 peak_widths 定界相对高度. + use_als_baseline: legacy 模式是否启用 ALS 基线. + als_lambda: ALS 平滑参数. + als_p: ALS 不对称权重参数. + use_valley_boundary: legacy 模式是否使用 valley+回落定界. + integration_mode: 积分模式, robust_v2, robust_v3, legacy 或 gcpy. + baseline_method: robust_v2/robust_v3 基线方式, rolling_quantile 或 als. + baseline_quantile: rolling_quantile 的分位数(0-100). + baseline_window_min: rolling_quantile 窗口长度(min). + boundary_sigma_factor: 边界噪声阈值倍数. + boundary_edge_ratio: 边界峰高比例阈值. + boundary_expand_factor: 基于半高宽扩展搜索半径系数. + boundary_min_span_min: 边界最小半径(min). + boundary_max_span_min: 边界最大半径(min). + shoulder_filter_enable: 是否启用肩峰过滤. + shoulder_filter_width_max_min: 判定肩峰的半高宽上限(min). + shoulder_filter_gap_max_min: 判定肩峰的邻峰间隔上限(min). + shoulder_filter_relative_prominence_max: 判定肩峰的相对显著性上限. + tail_artifact_filter_enable: 是否启用拖尾假峰过滤. + tail_artifact_gap_max_min: 判定拖尾假峰与前峰的最大间隔(min). + tail_artifact_relative_prominence_max: 判定拖尾假峰的相对显著性上限. + tail_artifact_half_width_asymmetry_min: 判定拖尾假峰的右左半高宽比下限. + tail_monotonic_filter_enable: 是否启用平滑信号单调下降拖尾过滤. + tail_monotonic_ratio_max: 平滑信号从前峰到当前峰的上升步占比上限, 低于此值判定为单调下降拖尾. + leading_edge_filter_enable: 是否启用前沿假峰过滤, 检测强峰上升沿上的假峰并丢弃. + leading_edge_relative_prominence_max: 判定前沿假峰的相对后峰显著性上限. + leading_edge_monotonic_ratio_min: 平滑信号从当前峰到后峰的上升步占比下限, 高于此值判定为前沿假峰. + max_peak_width_min: 峰最大边界宽度(min), 超出判定为基线抬升假峰, 设0关闭. + use_cwt_detection: 是否使用 CWT 多尺度峰检测替代 find_peaks. + cwt_min_width_min: CWT 最小小波宽度(min), 控制能检测的最窄峰. + cwt_max_width_min: CWT 最大小波宽度(min), 控制能检测的最宽峰. + cwt_min_snr: CWT 脊线最小信噪比, 调高减少噪声假峰. + cwt_noise_perc: CWT 噪声估计分位数, 调低使噪声估计更保守. + 返回: + 无. + """ + + def __init__( + self, + smoothing_window: int = 11, + prominence: float = 5000.0, + min_distance: int = 5, + width_rel_height: float = 0.95, + use_als_baseline: bool = True, + als_lambda: float = 1e7, + als_p: float = 0.01, + use_valley_boundary: bool = True, + integration_mode: str = "robust_v2", + baseline_method: str = "rolling_quantile", + baseline_quantile: float = 20.0, + baseline_window_min: float = 0.9, + boundary_sigma_factor: float = 3.0, + boundary_edge_ratio: float = 0.01, + boundary_expand_factor: float = 6.0, + boundary_min_span_min: float = 0.08, + boundary_max_span_min: float = 0.80, + shoulder_filter_enable: bool = False, + shoulder_filter_width_max_min: float = 0.035, + shoulder_filter_gap_max_min: float = 0.09, + shoulder_filter_relative_prominence_max: float = 0.15, + tail_artifact_filter_enable: bool = True, + tail_artifact_gap_max_min: float = 0.12, + tail_artifact_relative_prominence_max: float = 0.08, + tail_artifact_half_width_asymmetry_min: float = 4.0, + tail_monotonic_filter_enable: bool = True, + tail_monotonic_ratio_max: float = 0.25, + leading_edge_filter_enable: bool = False, + leading_edge_relative_prominence_max: float = 0.25, + leading_edge_monotonic_ratio_min: float = 0.65, + max_peak_width_min: float = 0.5, + gcpy_whittaker_lmbd: float = 10.0, + use_cwt_detection: bool = True, + cwt_min_width_min: float = 0.01, + cwt_max_width_min: float = 0.40, + cwt_min_snr: float = 2.0, + cwt_noise_perc: float = 10.0, + ): + self._smoothing_window = self._ensure_odd(max(3, int(smoothing_window))) + self._prominence = float(prominence) + self._min_distance = int(min_distance) + self._width_rel_height = float(width_rel_height) + + # legacy 参数 + self._use_als_baseline = bool(use_als_baseline) + self._als_lambda = float(als_lambda) + self._als_p = float(als_p) + self._use_valley_boundary = bool(use_valley_boundary) + + # robust_v2/v3 参数 + self._integration_mode = str(integration_mode).strip().lower() + self._baseline_method = str(baseline_method).strip().lower() + self._baseline_quantile = float(baseline_quantile) + self._baseline_window_min = float(baseline_window_min) + self._boundary_sigma_factor = float(boundary_sigma_factor) + self._boundary_edge_ratio = float(boundary_edge_ratio) + self._boundary_expand_factor = float(boundary_expand_factor) + self._boundary_min_span_min = float(boundary_min_span_min) + self._boundary_max_span_min = float(boundary_max_span_min) + self._shoulder_filter_enable = bool(shoulder_filter_enable) + self._shoulder_filter_width_max_min = float(shoulder_filter_width_max_min) + self._shoulder_filter_gap_max_min = float(shoulder_filter_gap_max_min) + self._shoulder_filter_relative_prominence_max = float(shoulder_filter_relative_prominence_max) + self._tail_artifact_filter_enable = bool(tail_artifact_filter_enable) + self._tail_artifact_gap_max_min = float(tail_artifact_gap_max_min) + self._tail_artifact_relative_prominence_max = float(tail_artifact_relative_prominence_max) + self._tail_artifact_half_width_asymmetry_min = float(tail_artifact_half_width_asymmetry_min) + + # 拖尾单调下降过滤参数 + self._tail_monotonic_filter_enable = bool(tail_monotonic_filter_enable) + self._tail_monotonic_ratio_max = float(tail_monotonic_ratio_max) + + # 前沿假峰过滤参数 + self._leading_edge_filter_enable = bool(leading_edge_filter_enable) + self._leading_edge_relative_prominence_max = float(leading_edge_relative_prominence_max) + self._leading_edge_monotonic_ratio_min = float(leading_edge_monotonic_ratio_min) + + # 基线抬升超宽假峰过滤参数 + self._max_peak_width_min = float(max_peak_width_min) + + # gcpy 参数 + self._gcpy_whittaker_lmbd = float(gcpy_whittaker_lmbd) + + # CWT 多尺度峰检测参数 + self._use_cwt_detection = bool(use_cwt_detection) + self._cwt_min_width_min = float(cwt_min_width_min) + self._cwt_max_width_min = float(cwt_max_width_min) + self._cwt_min_snr = float(cwt_min_snr) + self._cwt_noise_perc = float(cwt_noise_perc) + + # integrate() 完成后可读取最后一次使用的基线. + self.last_baseline: Optional[np.ndarray] = None + + @staticmethod + def _ensure_odd(value: int) -> int: + """ + 功能: + 将整数调整为奇数. + 参数: + value: 输入整数. + 返回: + int, 奇数结果. + """ + if value % 2 == 0: + return value + 1 + return value + + @staticmethod + def _safe_ratio(numerator: float, denominator: float) -> float: + """ + 功能: + 计算安全比值, 避免 0 或非法值导致异常. + 参数: + numerator: 分子. + denominator: 分母. + 返回: + float, 合法时返回比值, 否则返回 inf. + """ + if not np.isfinite(numerator) or not np.isfinite(denominator): + return float("inf") + + if denominator <= 0: + return float("inf") + + return float(numerator / denominator) + + def _als_baseline( + self, + y: np.ndarray, + lam: float, + p: float, + n_iter: int = 10, + ) -> np.ndarray: + """ + 功能: + ALS(Asymmetric Least Squares)基线估计. + 参数: + y: 原始信号数组. + lam: 平滑参数. + p: 不对称权重. + n_iter: 迭代次数. + 返回: + np.ndarray, 基线数组. + """ + n_points = len(y) + diag_vals = np.array([1.0, -2.0, 1.0], dtype=np.float64) + diff_matrix = sparse.diags( + diag_vals, + offsets=[0, -1, -2], + shape=(n_points, n_points - 2), + format="csc", + ) + smooth_matrix = lam * diff_matrix.dot(diff_matrix.T) + + weights = np.ones(n_points, dtype=np.float64) + for _ in range(n_iter): + weight_matrix = sparse.spdiags(weights, 0, n_points, n_points, format="csc") + system = weight_matrix + smooth_matrix + baseline = spsolve(system, weights * y) + weights = p * (y > baseline).astype(np.float64) + (1.0 - p) * (y <= baseline).astype(np.float64) + + return baseline + + @staticmethod + def _median_dt(times: np.ndarray) -> float: + """ + 功能: + 估计时间轴步长中位数. + 参数: + times: 时间数组. + 返回: + float, 步长中位数. + """ + diffs = np.diff(times) + valid_diffs = diffs[diffs > 0] + if len(valid_diffs) == 0: + return 0.0 + return float(np.median(valid_diffs)) + + def _compute_window_points(self, dt: float, duration_min: float, n_points: int) -> int: + """ + 功能: + 将分钟级窗口转换为点数窗口, 并限制为奇数合法值. + 参数: + dt: 时间步长(min). + duration_min: 目标窗口(min). + n_points: 信号总点数. + 返回: + int, 合法窗口点数. + """ + if dt <= 0: + base = 31 + else: + base = int(round(duration_min / dt)) + + if base < 5: + base = 5 + + base = self._ensure_odd(base) + + if base >= n_points: + if n_points % 2 == 0: + base = n_points - 1 + else: + base = n_points + + if base < 3: + base = 3 + + return base + + def _rolling_quantile_baseline(self, signal: np.ndarray, times: np.ndarray) -> np.ndarray: + """ + 功能: + 使用滚动分位数估计局部基线, 并通过 SG 再平滑. + 参数: + signal: 平滑后的信号. + times: 时间数组. + 返回: + np.ndarray, 基线数组. + """ + dt = self._median_dt(times) + window_points = self._compute_window_points(dt, self._baseline_window_min, len(signal)) + + baseline_raw = percentile_filter( + signal, + percentile=self._baseline_quantile, + size=window_points, + mode="nearest", + ) + + baseline = baseline_raw + if window_points >= 5 and window_points <= len(signal): + poly_order = 3 + if window_points <= poly_order: + poly_order = window_points - 1 + if poly_order >= 1: + baseline = savgol_filter(baseline_raw, window_points, polyorder=poly_order) + + return baseline + + @staticmethod + def _baseline_is_valid(signal: np.ndarray, baseline: np.ndarray) -> bool: + """ + 功能: + 判断基线是否可用于后续计算. + 参数: + signal: 参考信号. + baseline: 待校验基线. + 返回: + bool, True 表示基线有效. + """ + if baseline is None: + return False + + if len(signal) != len(baseline): + return False + + if np.any(~np.isfinite(baseline)): + return False + + signal_span = float(np.max(signal) - np.min(signal)) + if signal_span <= 0: + return True + + upper_limit = float(np.max(signal) + signal_span) + lower_limit = float(np.min(signal) - signal_span) + if np.any(baseline > upper_limit): + return False + if np.any(baseline < lower_limit): + return False + + return True + + @staticmethod + def _find_drop_index( + signal: np.ndarray, + start: int, + direction: int, + threshold: float, + left_limit: int, + right_limit: int, + ) -> int: + """ + 功能: + 从峰顶向两侧搜索, 找到首次低于阈值的索引. + 参数: + signal: 目标信号. + start: 起始索引. + direction: 搜索方向, -1 左, +1 右. + threshold: 回落阈值. + left_limit: 左边界约束. + right_limit: 右边界约束. + 返回: + int, 回落索引. + """ + idx = start + if direction < 0: + while idx > left_limit and signal[idx] > threshold: + idx -= 1 + return idx + + while idx < right_limit and signal[idx] > threshold: + idx += 1 + return idx + + @staticmethod + def _find_valley(signal: np.ndarray, start: int, end: int) -> int: + """ + 功能: + 在闭区间[start, end]寻找局部谷点. + 参数: + signal: 目标信号. + start: 起始索引. + end: 结束索引. + 返回: + int, 谷点索引. + """ + if end < start: + return start + + region = signal[start:end + 1] + if len(region) == 0: + return start + + return start + int(np.argmin(region)) + + @staticmethod + def _estimate_noise_sigma(corrected_signal: np.ndarray) -> float: + """ + 功能: + 基于 MAD 估计低信号区噪声标准差. + 参数: + corrected_signal: 基线校正后的非负信号. + 返回: + float, 噪声 sigma 估计. + """ + finite_values = corrected_signal[np.isfinite(corrected_signal)] + if len(finite_values) == 0: + return 0.0 + + quantile_threshold = float(np.percentile(finite_values, 70.0)) + lower_values = finite_values[finite_values <= quantile_threshold] + if len(lower_values) < 10: + lower_values = finite_values + + median_value = float(np.median(lower_values)) + mad_value = float(np.median(np.abs(lower_values - median_value))) + sigma = 1.4826 * mad_value + + if not np.isfinite(sigma): + return 0.0 + + if sigma < 0: + return 0.0 + + return sigma + + def _find_peaks_cwt( + self, + times: np.ndarray, + corrected_signal: np.ndarray, + ) -> np.ndarray: + """ + 功能: + 混合峰检测: CWT 多尺度检测 + find_peaks 在原始校正信号上检测, 取并集. + - CWT 擅长在不同宽度尺度上定位峰, 不易遗漏宽峰. + - find_peaks 在未经 SG 平滑的信号上运行, 可分辨被平滑抹平的窄双峰. + 两者取并集后统一按 prominence 阈值筛选. + 参数: + times: 时间数组(min). + corrected_signal: 基线校正后的原始信号(未经 SG 平滑). + 返回: + np.ndarray: 通过 prominence 过滤后的峰索引数组(已排序). + """ + from scipy.signal import find_peaks_cwt + import warnings + + dt = self._median_dt(times) + if dt <= 0: + logger.warning("混合峰检测: 时间轴步长无效, 回退到 find_peaks.") + return np.array([], dtype=int) + + n_pts = len(corrected_signal) + + # --- CWT 多尺度检测 --- + min_w = max(2, int(round(self._cwt_min_width_min / dt))) + max_w = max(min_w + 1, int(round(self._cwt_max_width_min / dt))) + widths = np.arange(min_w, max_w + 1) + + cwt_raw = find_peaks_cwt( + corrected_signal, + widths=widths, + min_snr=self._cwt_min_snr, + noise_perc=self._cwt_noise_perc, + ) + + # CWT 返回的位置可能偏离真实峰顶, 在邻域内对齐到局部最大值 + snap_window = max(3, min_w) + cwt_snapped = set() + for idx in cwt_raw: + lo = max(0, idx - snap_window) + hi = min(n_pts, idx + snap_window + 1) + cwt_snapped.add(lo + int(np.argmax(corrected_signal[lo:hi]))) + + # --- find_peaks 在原始校正信号上检测(可分辨窄双峰) --- + # 使用 distance=1 而非 self._min_distance, 允许检测间距极小的双峰; + # 后续统一由 prominence 过滤和去重逻辑保证质量. + fp_indices, _ = find_peaks( + corrected_signal, + prominence=self._prominence, + distance=1, + ) + + # --- 取并集 --- + combined = np.array(sorted(cwt_snapped | set(fp_indices.tolist())), dtype=int) + if len(combined) == 0: + return np.array([], dtype=int) + + # 边界保护和正值过滤 + valid = (combined >= 0) & (combined < n_pts) & (corrected_signal[combined] > 0) + combined = combined[valid] + if len(combined) == 0: + return np.array([], dtype=int) + + # 统一按 prominence 阈值筛选 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + proms = peak_prominences(corrected_signal, combined)[0] + mask = proms >= self._prominence + combined = combined[mask] + if len(combined) == 0: + return np.array([], dtype=int) + + # 去重: 间距 < 2 点的峰保留 prominence 更大者 + if len(combined) > 1: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + proms_final = peak_prominences(corrected_signal, combined)[0] + keep = np.ones(len(combined), dtype=bool) + order = np.argsort(-proms_final) + for i_rank in order: + if not keep[i_rank]: + continue + for j in range(len(combined)): + if j != i_rank and keep[j]: + if abs(int(combined[j]) - int(combined[i_rank])) < 2: + keep[j] = False + combined = np.sort(combined[keep]) + + logger.info( + "混合峰检测完成: CWT(%d~%d 点) + find_peaks, 检测到 %d 个峰.", + min_w, + max_w, + len(combined), + ) + return combined + + @staticmethod + def _integrate_with_local_baseline(times: np.ndarray, intensities: np.ndarray) -> float: + """ + 功能: + 使用峰端点线性基线进行局部积分. + 参数: + times: 峰段时间数组(min). + intensities: 峰段原始强度数组. + 返回: + float, 峰面积. + """ + if len(times) < 2: + return 0.0 + + local_baseline = np.linspace(intensities[0], intensities[-1], len(times)) + net_signal = intensities - local_baseline + net_signal = np.maximum(net_signal, 0) + return float(np.trapz(net_signal, times * 60.0)) + + @staticmethod + def _integrate_with_baseline_array( + times: np.ndarray, + intensities: np.ndarray, + baseline: np.ndarray, + ) -> float: + """ + 功能: + 使用给定基线数组进行逐点扣基线积分. + 参数: + times: 峰段时间数组(min). + intensities: 峰段原始强度数组. + baseline: 与峰段等长的全局基线数组. + 返回: + float, 峰面积. + """ + if len(times) < 2: + return 0.0 + + if len(times) != len(intensities) or len(times) != len(baseline): + return 0.0 + + if np.any(~np.isfinite(intensities)) or np.any(~np.isfinite(baseline)): + return 0.0 + + net_signal = intensities - baseline + net_signal = np.maximum(net_signal, 0.0) + return float(np.trapz(net_signal, times * 60.0)) + + def _find_legacy_boundaries( + self, + signal: np.ndarray, + peak_indices: np.ndarray, + ) -> List[Tuple[int, int]]: + """ + 功能: + legacy 模式边界搜索, 保留历史 valley+回落逻辑. + 参数: + signal: 检测信号. + peak_indices: 峰索引数组. + 返回: + List[Tuple[int, int]], 边界索引列表. + """ + n_points = len(signal) + n_peaks = len(peak_indices) + boundaries: List[Tuple[int, int]] = [] + + for i in range(n_peaks): + peak_idx = int(peak_indices[i]) + peak_height = float(signal[peak_idx]) + drop_threshold = peak_height * 0.01 + + if i == 0: + valley_left = 0 + left_region = signal[0:peak_idx] + if len(left_region) > 0: + valley_left = int(np.argmin(left_region)) + else: + prev_peak = int(peak_indices[i - 1]) + middle_region = signal[prev_peak:peak_idx] + if len(middle_region) > 0: + valley_left = prev_peak + int(np.argmin(middle_region)) + else: + valley_left = prev_peak + + if i == n_peaks - 1: + valley_right = n_points - 1 + right_region = signal[peak_idx:n_points] + if len(right_region) > 0: + valley_right = peak_idx + int(np.argmin(right_region)) + else: + next_peak = int(peak_indices[i + 1]) + middle_region = signal[peak_idx:next_peak] + if len(middle_region) > 0: + valley_right = peak_idx + int(np.argmin(middle_region)) + else: + valley_right = next_peak + + drop_left = self._find_drop_index( + signal, + peak_idx, + direction=-1, + threshold=drop_threshold, + left_limit=0, + right_limit=n_points - 1, + ) + drop_right = self._find_drop_index( + signal, + peak_idx, + direction=1, + threshold=drop_threshold, + left_limit=0, + right_limit=n_points - 1, + ) + + left_idx = max(valley_left, drop_left) + right_idx = min(valley_right, drop_right) + boundaries.append((left_idx, right_idx)) + + return boundaries + + def _find_robust_boundaries( + self, + times: np.ndarray, + corrected_signal: np.ndarray, + peak_indices: np.ndarray, + ) -> List[Optional[Tuple[int, int]]]: + """ + 功能: + robust_v2 边界搜索. + + 规则: + 1. 以半高宽推导自适应搜索半径. + 2. 叠加相邻峰中点约束, 防止跨峰吞并. + 3. 使用 max(峰高比例阈值, 噪声阈值) 搜索回落点. + 4. 与局部 valley 合并, 选择更靠近峰顶的边界. + 5. 异常时回退到半高宽边界. + + 参数: + times: 时间数组(min). + corrected_signal: 基线校正后的检测信号. + peak_indices: 峰索引数组. + 返回: + List[Optional[Tuple[int, int]]], 每个峰的边界索引, 失败项为 None. + """ + n_points = len(corrected_signal) + boundaries: List[Optional[Tuple[int, int]]] = [] + + widths_50, _, left_ips_50, right_ips_50 = peak_widths( + corrected_signal, + peak_indices, + rel_height=0.5, + ) + + dt = self._median_dt(times) + min_span_pts = self._compute_window_points(dt, self._boundary_min_span_min, n_points) + max_span_pts = self._compute_window_points(dt, self._boundary_max_span_min, n_points) + if max_span_pts < min_span_pts: + max_span_pts = min_span_pts + + noise_sigma = self._estimate_noise_sigma(corrected_signal) + + for i, peak_idx in enumerate(peak_indices): + peak_idx = int(peak_idx) + + fallback_left = max(0, int(np.floor(left_ips_50[i]))) + fallback_right = min(n_points - 1, int(np.ceil(right_ips_50[i]))) + + width_pts = float(widths_50[i]) + if not np.isfinite(width_pts): + width_pts = float(min_span_pts) + + span_pts = int(np.ceil(width_pts * self._boundary_expand_factor)) + if span_pts < min_span_pts: + span_pts = min_span_pts + if span_pts > max_span_pts: + span_pts = max_span_pts + + left_limit = max(0, peak_idx - span_pts) + right_limit = min(n_points - 1, peak_idx + span_pts) + + if i > 0: + midpoint_left = (int(peak_indices[i - 1]) + peak_idx) // 2 + if midpoint_left > left_limit: + left_limit = midpoint_left + + if i < len(peak_indices) - 1: + midpoint_right = (peak_idx + int(peak_indices[i + 1])) // 2 + if midpoint_right < right_limit: + right_limit = midpoint_right + + if left_limit >= peak_idx: + left_limit = max(0, peak_idx - 1) + + if right_limit <= peak_idx: + right_limit = min(n_points - 1, peak_idx + 1) + + peak_height = float(corrected_signal[peak_idx]) + edge_threshold = max( + peak_height * self._boundary_edge_ratio, + noise_sigma * self._boundary_sigma_factor, + ) + + drop_left = self._find_drop_index( + corrected_signal, + peak_idx, + direction=-1, + threshold=edge_threshold, + left_limit=left_limit, + right_limit=right_limit, + ) + drop_right = self._find_drop_index( + corrected_signal, + peak_idx, + direction=1, + threshold=edge_threshold, + left_limit=left_limit, + right_limit=right_limit, + ) + + valley_left = self._find_valley(corrected_signal, left_limit, peak_idx) + valley_right = self._find_valley(corrected_signal, peak_idx, right_limit) + + left_idx = max(drop_left, valley_left) + right_idx = min(drop_right, valley_right) + + if right_idx <= left_idx or (right_idx - left_idx) < 2: + if fallback_right > fallback_left: + left_idx = fallback_left + right_idx = fallback_right + logger.warning( + "RT=%.3f 的边界触发回退, 使用半高宽边界.", + float(times[peak_idx]), + ) + else: + logger.warning( + "RT=%.3f 的边界无效且回退失败, 已跳过该峰.", + float(times[peak_idx]), + ) + boundaries.append(None) + continue + + boundaries.append((left_idx, right_idx)) + + return boundaries + + def _build_peak_result( + self, + times: np.ndarray, + intensities: np.ndarray, + peak_idx: int, + left_idx: int, + right_idx: int, + area: float, + ) -> PeakResult: + """ + 功能: + 组装单个峰结果. + 参数: + times: 时间数组. + intensities: 强度数组. + peak_idx: 峰顶索引. + left_idx: 左边界索引. + right_idx: 右边界索引. + area: 峰面积. + 返回: + PeakResult. + """ + width = 0.0 + if right_idx > left_idx: + width = float(times[right_idx] - times[left_idx]) + + return PeakResult( + peak_index=int(peak_idx), + retention_time=float(times[peak_idx]), + height=float(intensities[peak_idx]), + area=float(area), + start_time=float(times[left_idx]), + end_time=float(times[right_idx]), + width=width, + ) + + @staticmethod + def _update_area_percent(results: List[PeakResult]) -> None: + """ + 功能: + 按总面积回填 area_percent. + 参数: + results: 峰结果列表. + 返回: + 无. + """ + total_area = sum(item.area for item in results) + if total_area <= 0: + return + + for item in results: + item.area_percent = item.area / total_area * 100.0 + + def _filter_adjacent_artifact_peak_indices( + self, + times: np.ndarray, + corrected_signal: np.ndarray, + peak_indices: np.ndarray, + smoothed_signal: np.ndarray, + ) -> Tuple[np.ndarray, List[Optional[int]]]: + """ + 功能: + 在 robust_v3 中识别需要并入前峰的肩峰或拖尾假峰. + 参数: + times: 时间数组(min). + corrected_signal: 基线校正后的检测信号. + peak_indices: 原始峰索引数组. + smoothed_signal: SG平滑后未扣基线的原始信号, 用于单调下降判定. + 返回: + Tuple[np.ndarray, List[Optional[int]]]: + keep_mask: True 表示保留该峰, False 表示并入前峰. + merge_targets: 记录每个峰并入的目标峰编号, 未并入时为 None. + """ + merge_targets: List[Optional[int]] = [None] * len(peak_indices) + if (self._shoulder_filter_enable is False + and self._tail_artifact_filter_enable is False + and self._tail_monotonic_filter_enable is False): + return np.ones(len(peak_indices), dtype=bool), merge_targets + + if len(peak_indices) <= 1: + return np.ones(len(peak_indices), dtype=bool), merge_targets + + dt = self._median_dt(times) + if dt <= 0: + logger.warning("时间轴步长无效, 跳过 robust_v3 后置假峰过滤.") + return np.ones(len(peak_indices), dtype=bool), merge_targets + + prominences = peak_prominences(corrected_signal, peak_indices)[0] + widths_50, _, left_ips_50, right_ips_50 = peak_widths( + corrected_signal, + peak_indices, + rel_height=0.5, + ) + widths_50_min = widths_50 * dt + + keep_mask = np.ones(len(peak_indices), dtype=bool) + for peak_no, peak_idx in enumerate(peak_indices): + if peak_no == 0: + continue + + current_width_min = float(widths_50_min[peak_no]) + current_prominence = float(prominences[peak_no]) + if not np.isfinite(current_width_min) or not np.isfinite(current_prominence): + continue + + merge_target_no = peak_no - 1 + while merge_target_no >= 0 and not keep_mask[merge_target_no]: + merge_target_no -= 1 + + if merge_target_no < 0: + continue + + current_rt = float(times[int(peak_idx)]) + previous_rt = float(times[int(peak_indices[merge_target_no])]) + previous_gap_min = float(current_rt - previous_rt) + previous_prominence = float(prominences[merge_target_no]) + + if previous_prominence <= current_prominence: + continue + + prominence_ratio = self._safe_ratio(current_prominence, previous_prominence) + + if self._shoulder_filter_enable is True: + if previous_gap_min <= self._shoulder_filter_gap_max_min: + if current_width_min <= self._shoulder_filter_width_max_min: + if prominence_ratio <= self._shoulder_filter_relative_prominence_max: + keep_mask[peak_no] = False + merge_targets[peak_no] = merge_target_no + logger.info( + "RT=%.3f 的峰判定为前峰拖尾肩峰, 并入 RT=%.3f 的前峰. 半高宽=%.4f min, 前峰间隔=%.4f min, prominence=%.4f, 前峰prominence=%.4f, 比值=%.4f", + current_rt, + previous_rt, + current_width_min, + previous_gap_min, + current_prominence, + previous_prominence, + prominence_ratio, + ) + continue + + # --- 单调下降拖尾检测: 在平滑信号(未扣基线)上判断前峰到当前峰是否近似单调递减 --- + if self._tail_monotonic_filter_enable is True: + # 仅当候选峰 prominence 显著低于前峰时检查, 避免误伤真实相邻峰 + if prominence_ratio <= 0.25: + prev_peak_idx = int(peak_indices[merge_target_no]) + curr_peak_idx = int(peak_idx) + # 至少 3 个数据点才有统计意义 + if curr_peak_idx > prev_peak_idx + 2: + segment = smoothed_signal[prev_peak_idx:curr_peak_idx + 1] + diffs = np.diff(segment) + total_steps = len(diffs) + if total_steps > 0: + rising_ratio = float(np.sum(diffs > 0)) / total_steps + if rising_ratio <= self._tail_monotonic_ratio_max: + keep_mask[peak_no] = False + merge_targets[peak_no] = merge_target_no + logger.info( + "RT=%.3f 的峰判定为前峰单调下降拖尾假峰, 并入 RT=%.3f 的前峰. " + "上升步占比=%.4f (阈值=%.4f), prominence比值=%.4f", + current_rt, + previous_rt, + rising_ratio, + self._tail_monotonic_ratio_max, + prominence_ratio, + ) + continue + + if self._tail_artifact_filter_enable is False: + continue + + if previous_gap_min > self._tail_artifact_gap_max_min: + continue + + if prominence_ratio > self._tail_artifact_relative_prominence_max: + continue + + left_half_width_min = max(0.0, float((float(peak_idx) - left_ips_50[peak_no]) * dt)) + right_half_width_min = max(0.0, float((right_ips_50[peak_no] - float(peak_idx)) * dt)) + half_width_asymmetry = self._safe_ratio(right_half_width_min, left_half_width_min) + if half_width_asymmetry < self._tail_artifact_half_width_asymmetry_min: + continue + + keep_mask[peak_no] = False + merge_targets[peak_no] = merge_target_no + logger.info( + "RT=%.3f 的峰判定为前峰拖尾假峰, 并入 RT=%.3f 的前峰. 峰间隔=%.4f min, prominence=%.4f, 前峰prominence=%.4f, 比值=%.4f, 右左半高宽比=%.4f", + current_rt, + previous_rt, + previous_gap_min, + current_prominence, + previous_prominence, + prominence_ratio, + half_width_asymmetry, + ) + continue + + return keep_mask, merge_targets + + def _filter_leading_edge_artifact_peak_indices( + self, + times: np.ndarray, + corrected_signal: np.ndarray, + peak_indices: np.ndarray, + smoothed_signal: np.ndarray, + existing_keep_mask: np.ndarray, + ) -> np.ndarray: + """ + 功能: + 在 robust_v3 中识别强峰上升沿上的前沿假峰并标记丢弃. + 与 _filter_adjacent_artifact_peak_indices 互补: 后者向后看(拖尾), + 本方法向前看(前沿). 前沿假峰是基线干扰, 直接丢弃不合并面积. + 参数: + times: 时间数组(min). + corrected_signal: 基线校正后的检测信号. + peak_indices: 原始峰索引数组. + smoothed_signal: SG平滑后未扣基线的原始信号, 用于单调上升判定. + existing_keep_mask: 前一轮(向后过滤)输出的保留掩码. + 返回: + np.ndarray: 更新后的 keep_mask, False 表示丢弃该峰. + """ + keep_mask = np.copy(existing_keep_mask) + + if self._leading_edge_filter_enable is False: + return keep_mask + + if len(peak_indices) <= 1: + return keep_mask + + dt = self._median_dt(times) + if dt <= 0: + logger.warning("时间轴步长无效, 跳过前沿假峰过滤.") + return keep_mask + + prominences = peak_prominences(corrected_signal, peak_indices)[0] + + # 逆序遍历: 保证级联丢弃时后面的峰先被处理 + for peak_no in range(len(peak_indices) - 2, -1, -1): + if not keep_mask[peak_no]: + continue + + current_prominence = float(prominences[peak_no]) + if not np.isfinite(current_prominence): + continue + + # 查找下一个保留的峰 + next_peak_no = peak_no + 1 + while next_peak_no < len(peak_indices) and not keep_mask[next_peak_no]: + next_peak_no += 1 + + if next_peak_no >= len(peak_indices): + continue + + next_prominence = float(prominences[next_peak_no]) + # 后峰必须更强 + if next_prominence <= current_prominence: + continue + + prominence_ratio = self._safe_ratio(current_prominence, next_prominence) + if prominence_ratio > self._leading_edge_relative_prominence_max: + continue + + curr_peak_idx = int(peak_indices[peak_no]) + next_peak_idx = int(peak_indices[next_peak_no]) + # 至少 4 个数据点, 保证中点后仍有足够统计量 + if next_peak_idx <= curr_peak_idx + 3: + continue + + # 取两峰中点到后峰的平滑信号, 判断上升趋势. + # 避免从当前峰顶开始(峰顶后必然先下降), 中点更能反映整体走势. + midpoint_idx = (curr_peak_idx + next_peak_idx) // 2 + segment = smoothed_signal[midpoint_idx:next_peak_idx + 1] + diffs = np.diff(segment) + total_steps = len(diffs) + if total_steps <= 0: + continue + + rising_ratio = float(np.sum(diffs > 0)) / total_steps + if rising_ratio < self._leading_edge_monotonic_ratio_min: + continue + + # 判定为前沿假峰, 直接丢弃 + keep_mask[peak_no] = False + current_rt = float(times[curr_peak_idx]) + next_rt = float(times[next_peak_idx]) + logger.info( + "RT=%.3f 的峰判定为后峰前沿假峰, 已丢弃. 后峰RT=%.3f, " + "上升步占比=%.4f (阈值=%.4f), prominence比值=%.4f", + current_rt, + next_rt, + rising_ratio, + self._leading_edge_monotonic_ratio_min, + prominence_ratio, + ) + + return keep_mask + + def _integrate_legacy(self, times: np.ndarray, intensities: np.ndarray) -> List[PeakResult]: + """ + 功能: + 兼容历史积分流程. + 参数: + times: 时间数组. + intensities: 强度数组. + 返回: + List[PeakResult], 峰列表. + """ + if self._use_als_baseline: + baseline = self._als_baseline(intensities.astype(np.float64), self._als_lambda, self._als_p) + corrected_signal = np.maximum(intensities - baseline, 0) + self.last_baseline = baseline + signal_for_detection = corrected_signal + logger.info("legacy 模式使用 ALS 基线.") + else: + corrected_signal = intensities.astype(np.float64) + self.last_baseline = None + signal_for_detection = corrected_signal + logger.info("legacy 模式不使用 ALS 基线.") + + smoothed_signal = savgol_filter(signal_for_detection, self._smoothing_window, polyorder=3) + smoothed_signal = np.maximum(smoothed_signal, 0) + + peak_indices, _ = find_peaks( + smoothed_signal, + prominence=self._prominence, + distance=self._min_distance, + ) + if len(peak_indices) == 0: + logger.info("legacy 模式未检测到峰.") + return [] + + if self._use_valley_boundary: + boundaries = self._find_legacy_boundaries(smoothed_signal, peak_indices) + else: + _, _, left_ips, right_ips = peak_widths( + smoothed_signal, + peak_indices, + rel_height=self._width_rel_height, + ) + boundaries = [] + for i in range(len(peak_indices)): + left_idx = max(0, int(np.floor(left_ips[i]))) + right_idx = min(len(times) - 1, int(np.ceil(right_ips[i]))) + boundaries.append((left_idx, right_idx)) + + results: List[PeakResult] = [] + for i, peak_idx in enumerate(peak_indices): + left_idx, right_idx = boundaries[i] + left_idx = max(0, left_idx) + right_idx = min(len(times) - 1, right_idx) + if right_idx <= left_idx: + continue + + peak_times = times[left_idx:right_idx + 1] + if self._use_als_baseline: + peak_signal = corrected_signal[left_idx:right_idx + 1] + area = 0.0 + if len(peak_times) >= 2: + area = float(np.trapz(peak_signal, peak_times * 60.0)) + else: + peak_signal = intensities[left_idx:right_idx + 1] + area = self._integrate_with_local_baseline(peak_times, peak_signal) + + results.append( + self._build_peak_result( + times=times, + intensities=intensities, + peak_idx=int(peak_idx), + left_idx=left_idx, + right_idx=right_idx, + area=area, + ) + ) + + self._update_area_percent(results) + logger.info("legacy 模式积分完成, 峰数量: %d", len(results)) + return results + + def _integrate_robust_common( + self, + times: np.ndarray, + intensities: np.ndarray, + *, + apply_shoulder_filter: bool, + mode_name: str, + ) -> List[PeakResult]: + """ + 功能: + 执行 robust 系列公共积分流程. + 参数: + times: 时间数组. + intensities: 强度数组. + apply_shoulder_filter: 是否在 find_peaks 后执行 robust_v3 后置假峰过滤. + mode_name: 当前模式名称, 用于日志. + 返回: + List[PeakResult], 峰列表. + """ + smoothed_signal = savgol_filter(intensities, self._smoothing_window, polyorder=3) + + baseline = None + if self._baseline_method == "rolling_quantile": + baseline = self._rolling_quantile_baseline(smoothed_signal, times) + if not self._baseline_is_valid(smoothed_signal, baseline): + logger.warning("滚动分位数基线异常, 自动回退 ALS 基线.") + baseline = self._als_baseline(intensities.astype(np.float64), self._als_lambda, self._als_p) + elif self._baseline_method == "als": + baseline = self._als_baseline(intensities.astype(np.float64), self._als_lambda, self._als_p) + else: + logger.warning("未知 baseline_method=%s, 自动使用 rolling_quantile.", self._baseline_method) + baseline = self._rolling_quantile_baseline(smoothed_signal, times) + if not self._baseline_is_valid(smoothed_signal, baseline): + logger.warning("滚动分位数基线异常, 自动回退 ALS 基线.") + baseline = self._als_baseline(intensities.astype(np.float64), self._als_lambda, self._als_p) + + if not self._baseline_is_valid(smoothed_signal, baseline): + logger.warning("基线仍异常, 强制使用 ALS 基线.") + baseline = self._als_baseline(intensities.astype(np.float64), self._als_lambda, self._als_p) + + self.last_baseline = baseline + corrected_signal = np.maximum(smoothed_signal - baseline, 0) + baseline_available = baseline is not None and len(baseline) == len(intensities) + + # 峰检测: CWT 混合检测或传统 find_peaks + if self._use_cwt_detection: + # 混合检测在原始信号(未经 SG 平滑)上运行, 避免平滑抹平窄双峰 + raw_corrected = np.maximum(intensities - baseline, 0) + peak_indices = self._find_peaks_cwt(times, raw_corrected) + if len(peak_indices) == 0: + # 混合检测未检测到峰时回退到 find_peaks + logger.info("%s 模式混合检测未检测到峰, 回退到 find_peaks.", mode_name) + peak_indices, _ = find_peaks( + corrected_signal, + prominence=self._prominence, + distance=self._min_distance, + ) + else: + logger.info("%s 模式使用混合峰检测, 初始峰数: %d", mode_name, len(peak_indices)) + # 混合检测在原始信号上定位峰, 后续边界/过滤也需使用原始信号以保持一致 + corrected_signal = raw_corrected + else: + peak_indices, _ = find_peaks( + corrected_signal, + prominence=self._prominence, + distance=self._min_distance, + ) + if len(peak_indices) == 0: + logger.info("%s 模式未检测到峰.", mode_name) + return [] + + # 计算噪声 sigma, 供边界检测和自适应宽度过滤复用 + noise_sigma = self._estimate_noise_sigma(corrected_signal) + + boundaries = self._find_robust_boundaries(times, corrected_signal, peak_indices) + keep_mask = np.ones(len(peak_indices), dtype=bool) + merge_targets: List[Optional[int]] = [None] * len(peak_indices) + if apply_shoulder_filter is True: + keep_mask, merge_targets = self._filter_adjacent_artifact_peak_indices( + times, + corrected_signal, + peak_indices, + smoothed_signal, + ) + if not np.any(keep_mask): + logger.info("%s 模式后置假峰合并后未检测到峰.", mode_name) + return [] + + # 前沿假峰过滤: 检测强峰上升沿上的假峰并丢弃 + if apply_shoulder_filter is True and self._leading_edge_filter_enable is True: + keep_mask = self._filter_leading_edge_artifact_peak_indices( + times, + corrected_signal, + peak_indices, + smoothed_signal, + keep_mask, + ) + if not np.any(keep_mask): + logger.info("%s 模式前沿假峰过滤后未检测到峰.", mode_name) + return [] + + merged_right_boundary_map = {} + if apply_shoulder_filter is True: + for peak_no, merge_target_no in enumerate(merge_targets): + if merge_target_no is None: + continue + + boundary = boundaries[peak_no] + if boundary is None: + continue + + _, merged_right_idx = boundary + previous_right_idx = merged_right_boundary_map.get(merge_target_no) + if previous_right_idx is None or merged_right_idx > previous_right_idx: + merged_right_boundary_map[merge_target_no] = merged_right_idx + + results: List[PeakResult] = [] + for i, peak_idx in enumerate(peak_indices): + if not keep_mask[i]: + continue + + boundary = boundaries[i] + if boundary is None: + continue + + left_idx, right_idx = boundary + merged_right_idx = merged_right_boundary_map.get(i) + if merged_right_idx is not None and merged_right_idx > right_idx: + # 并峰后将前峰右边界扩展到被并入相邻假峰的右边界. + right_idx = merged_right_idx + + left_idx = max(0, left_idx) + right_idx = min(len(times) - 1, right_idx) + + # 超宽峰过滤: 高度自适应, 高强度峰允许更宽的边界 + if self._max_peak_width_min > 0: + peak_width_check = float(times[right_idx] - times[left_idx]) + peak_h = float(corrected_signal[int(peak_idx)]) + noise_ref = max(noise_sigma, 1.0) + height_ratio = peak_h / noise_ref + adaptive_limit = self._max_peak_width_min + if height_ratio > 100: + # log 缩放: log10(100)=2→1.0x, log10(10000)=4→3.0x, 最多放大 3 倍 + scale = 1.0 + min(np.log10(height_ratio / 100.0), 2.0) + adaptive_limit = self._max_peak_width_min * scale + if peak_width_check > adaptive_limit: + logger.info( + "RT=%.3f 的峰因边界宽度 %.4f min > 自适应上限 %.4f min 被过滤.", + float(times[int(peak_idx)]), + peak_width_check, + adaptive_limit, + ) + continue + + if right_idx <= left_idx: + logger.warning("RT=%.3f 的边界非法, 已跳过.", float(times[int(peak_idx)])) + continue + + peak_times = times[left_idx:right_idx + 1] + peak_intensities = intensities[left_idx:right_idx + 1] + if baseline_available: + peak_baseline = baseline[left_idx:right_idx + 1] + if len(peak_baseline) == len(peak_times) and np.all(np.isfinite(peak_baseline)): + area = self._integrate_with_baseline_array( + peak_times, + peak_intensities, + peak_baseline, + ) + else: + logger.warning( + "RT=%.3f 的全局基线片段无效, 已回退到局部端点基线积分.", + float(times[int(peak_idx)]), + ) + area = self._integrate_with_local_baseline(peak_times, peak_intensities) + else: + logger.warning( + "RT=%.3f 缺少可用全局基线, 已回退到局部端点基线积分.", + float(times[int(peak_idx)]), + ) + area = self._integrate_with_local_baseline(peak_times, peak_intensities) + + results.append( + self._build_peak_result( + times=times, + intensities=intensities, + peak_idx=int(peak_idx), + left_idx=left_idx, + right_idx=right_idx, + area=area, + ) + ) + + self._update_area_percent(results) + logger.info("%s 模式积分完成, 峰数量: %d", mode_name, len(results)) + return results + + def _integrate_robust_v2(self, times: np.ndarray, intensities: np.ndarray) -> List[PeakResult]: + """ + 功能: + 执行 robust_v2 积分流程. + 参数: + times: 时间数组. + intensities: 强度数组. + 返回: + List[PeakResult], 峰列表. + """ + return self._integrate_robust_common( + times, + intensities, + apply_shoulder_filter=False, + mode_name="robust_v2", + ) + + def _integrate_robust_v3(self, times: np.ndarray, intensities: np.ndarray) -> List[PeakResult]: + """ + 功能: + 执行 robust_v3 积分流程. + 参数: + times: 时间数组. + intensities: 强度数组. + 返回: + List[PeakResult], 峰列表. + """ + return self._integrate_robust_common( + times, + intensities, + apply_shoulder_filter=True, + mode_name="robust_v3", + ) + + def _integrate_gcpy( + self, times: np.ndarray, intensities: np.ndarray + ) -> List[PeakResult]: + """ + 功能: + 使用 gcpy 方法执行峰检测与积分: + 1. Whittaker 平滑(gcpy.smooth)用于峰检测与边界定位. + 2. scipy.signal.find_peaks 检测峰位置. + 3. gcpy.bls.lininterp_baseline_subtract 在原始信号上全局扣除基线: + 屏蔽峰区域后对剩余点做样条插值并扣除. + 4. np.trapz 逐峰梯形积分. + 参数: + times: 时间数组(min). + intensities: 原始强度数组. + 返回: + List[PeakResult], 峰积分结果列表. + """ + from .gcpy.smooth import whittaker_smooth + from .gcpy.bls import lininterp_baseline_subtract + + signal = intensities.astype(float) + + # Whittaker 平滑, 仅用于峰检测与边界定位, 不改变积分信号 + smoothed = whittaker_smooth(signal, self._gcpy_whittaker_lmbd) + + # 峰检测 + peak_indices, _ = find_peaks( + smoothed, + prominence=self._prominence, + distance=self._min_distance, + ) + if len(peak_indices) == 0: + logger.info("gcpy 模式未检测到峰.") + self.last_baseline = np.zeros_like(signal) + return [] + + # 在平滑信号上获取峰边界 + _, _, left_ips, right_ips = peak_widths( + smoothed, peak_indices, rel_height=self._width_rel_height + ) + + # 在原始信号上做 lininterp 基线扣除, 保持与绘图坐标一致 + x_indices = np.arange(len(signal), dtype=float) + try: + baseline_sub = lininterp_baseline_subtract(signal, x_indices, left_ips, right_ips) + baseline_array = signal - baseline_sub # 纯基线数组, 供绘图备用 + except Exception as e: + logger.warning("gcpy lininterp 基线扣除失败, 回退到不扣除基线: %s", e) + baseline_sub = signal.copy() + baseline_array = np.zeros_like(signal) + + self.last_baseline = baseline_array + + # 逐峰梯形积分 + raw_areas: List[float] = [] + for center, left, right in zip(peak_indices, left_ips, right_ips): + lb = max(0, int(left)) + rb = min(len(times) - 1, int(right) + 1) + seg_times = times[lb:rb] + seg_signal = np.maximum(baseline_sub[lb:rb], 0.0) + # 时间轴转换为秒后积分 + area = float(np.trapz(seg_signal, seg_times * 60.0)) + raw_areas.append(area) + + total_area = sum(raw_areas) + + results: List[PeakResult] = [] + for center, left, right, area in zip(peak_indices, left_ips, right_ips, raw_areas): + lb = max(0, int(left)) + rb = min(len(times) - 1, int(right)) + rt = float(times[center]) + height = float(signal[center]) + area_pct = (area / total_area * 100.0) if total_area > 0 else 0.0 + start_t = float(times[lb]) + end_t = float(times[rb]) + width = end_t - start_t + results.append(PeakResult( + peak_index=int(center), + retention_time=rt, + height=height, + area=area, + area_percent=area_pct, + start_time=start_t, + end_time=end_t, + width=width, + )) + + logger.info("gcpy 模式积分完成, 峰数量: %d", len(results)) + return results + + def integrate(self, times: np.ndarray, intensities: np.ndarray) -> List[PeakResult]: + """ + 功能: + 根据配置执行峰检测与积分. + 参数: + times: 时间数组(min). + intensities: 强度数组. + 返回: + List[PeakResult], 峰检测积分结果. + """ + if len(times) != len(intensities): + logger.warning("时间与强度长度不一致, 已跳过积分.") + return [] + + if len(times) < self._smoothing_window: + logger.warning( + "数据点数 %d 小于平滑窗口 %d, 已跳过积分.", + len(times), + self._smoothing_window, + ) + return [] + + mode = self._integration_mode + if mode == "legacy": + return self._integrate_legacy(times, intensities) + + if mode == "gcpy": + return self._integrate_gcpy(times, intensities) + + if mode == "robust_v2": + return self._integrate_robust_v2(times, intensities) + + if mode == "robust_v3": + return self._integrate_robust_v3(times, intensities) + + logger.warning("未知 integration_mode=%s, 自动使用 robust_v3.", mode) + return self._integrate_robust_v3(times, intensities) diff --git a/unilabos/devices/eit_analysis_station/processor/report_generator.py b/unilabos/devices/eit_analysis_station/processor/report_generator.py new file mode 100644 index 00000000..de2d409e --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/report_generator.py @@ -0,0 +1,912 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 将积分结果汇总为 Excel (.xlsx) 表格. + 按任务汇总, 每个任务生成一个 xlsx 文件, + 包含 TIC 峰表, FID 峰表, TIC-FID 对照表, 样品汇总四个 Sheet. +参数: + 无. +返回: + 无. +""" + +import logging +import re +from dataclasses import dataclass, field +from datetime import datetime +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import openpyxl +from openpyxl.styles import Alignment, Font, PatternFill +from openpyxl.utils import get_column_letter + +from .nist_matcher import CompoundMatch +from .peak_integrator import PeakResult + +logger = logging.getLogger(__name__) + + +@dataclass +class PIMPrediction: + """ + 功能: + 存储单个峰的 PIM 分子量预测结果. + 参数: + predicted_mz: 预测分子离子峰 m/z. + predicted_mw: 预测分子量(Da). + confidence_index: PIM 置信指数. + status: 结果状态, 可选 ok/no_spectrum/error. + message: 状态说明. + 返回: + PIMPrediction. + """ + predicted_mz: Optional[int] = None + predicted_mw: Optional[int] = None + confidence_index: Optional[float] = None + status: str = "" + message: str = "" + + +@dataclass +class SSHMPrediction: + """ + 功能: + 存储单个峰的 SS-HM (Simple Search Hitlist Method) 分子量预测结果. + 参数: + predicted_mw: SS-HM 预测分子量(Da). + confidence: 概率置信度 (0-1). + correction: 最佳修正值 (sigma = PIM + correction). + status: 结果状态, 可选 ok/no_spectrum/error. + message: 状态说明. + 返回: + SSHMPrediction. + """ + predicted_mw: Optional[int] = None + confidence: Optional[float] = None + correction: Optional[int] = None + status: str = "" + message: str = "" + + +@dataclass +class iHSHMPrediction: + """ + 功能: + 存储单个峰的 iHS-HM (iterative Hybrid Search Hitlist Method) 分子量预测结果. + 参数: + predicted_mw: iHS-HM 预测分子量(Da). + confidence: 置信度 (Omega1 - Omega2) / 999. + status: 结果状态, 可选 ok/no_spectrum/error. + message: 状态说明. + 返回: + iHSHMPrediction. + """ + predicted_mw: Optional[int] = None + confidence: Optional[float] = None + status: str = "" + message: str = "" + + +@dataclass +class SampleResult: + """ + 功能: + 存储单个样品的积分结果(TIC + FID + 化合物匹配). + 参数: + sample_name: 样品名称. + d_dir: .D 目录路径. + tic_peaks: TIC 峰检测与积分结果列表. + fid_peaks: FID 峰检测与积分结果列表. + compound_matches: 保留时间 -> 化合物匹配结果列表. + acq_time: 采集时间字符串. + nist_result_path: NIST SRCRESLT 结果文件副本路径. + tic_plot_path: TIC 色谱图图片路径. + fid_plot_path: FID 色谱图图片路径. + pim_predictions: 保留时间 -> PIM 预测结果字典. + 返回: + SampleResult. + """ + sample_name: str = "" + d_dir: Path = field(default_factory=Path) + tic_peaks: List[PeakResult] = field(default_factory=list) + fid_peaks: List[PeakResult] = field(default_factory=list) + compound_matches: Dict[float, List[CompoundMatch]] = field(default_factory=dict) + acq_time: str = "" + nist_result_path: Optional[Path] = None + tic_plot_path: Optional[Path] = None + fid_plot_path: Optional[Path] = None + ms_plot_paths: Dict[int, Path] = field(default_factory=dict) # 峰号(1-based) -> 质谱图路径 + pim_predictions: Dict[float, PIMPrediction] = field(default_factory=dict) # 保留时间 -> PIM 预测结果 + sshm_predictions: Dict[float, SSHMPrediction] = field(default_factory=dict) # 保留时间 -> SS-HM 预测结果 + ihshm_predictions: Dict[float, iHSHMPrediction] = field(default_factory=dict) # 保留时间 -> iHS-HM 预测结果 + +class ReportGenerator: + """ + 功能: + 将积分结果汇总为 Excel 表格. + 按任务汇总, 生成包含 TIC峰表/FID峰表/TIC-FID对照表/样品汇总 四个 Sheet 的 xlsx 文件. + 参数: + 无. + 返回: + 无. + """ + + # 表头样式 + _HEADER_FONT = Font(bold=True, size=11) + _HEADER_FILL = PatternFill(start_color="D9E1F2", end_color="D9E1F2", fill_type="solid") + _HEADER_ALIGN = Alignment(horizontal="center", vertical="center") + + # FID 峰表列定义 + _FID_HEADERS = [ + "样品名", "峰号", "保留时间(min)", "峰高", "峰面积", + "面积%", "峰起始(min)", "峰结束(min)", "峰宽(min)", + ] + + # 样品汇总列定义 + _SUMMARY_HEADERS = [ + "样品名", "TIC峰数", "FID峰数", "TIC总面积", "FID总面积", "采集时间", + "TIC色谱图", "FID色谱图", + ] + + def generate_task_report( + self, + task_id: str, + sample_results: List[SampleResult], + output_dir: Path, + structure_images: Optional[Dict[str, Optional[Path]]] = None, + nist_max_hits: int = 5, + alignment_tolerance: float = 0.05, + include_tic_only: bool = True, + include_fid_only: bool = True, + pim_enabled: bool = True, + sshm_enabled: bool = True, + ihshm_enabled: bool = True, + ) -> Path: + """ + 功能: + 生成任务级汇总 Excel 报告. + 参数: + task_id: 任务 ID. + sample_results: 各样品的积分结果列表. + output_dir: 输出目录. + structure_images: 结构键 -> 结构图 PNG 路径映射, None 表示不嵌入结构图. + nist_max_hits: NIST 匹配化合物数量上限, 与 setting.py 中配置保持一致. + alignment_tolerance: FID 与 TIC 峰保留时间对齐容差(min). + include_tic_only: 对照表是否输出 TIC 有峰但 FID 无峰的行. + include_fid_only: 对照表是否输出 FID 有峰但 TIC 无峰的行. + pim_enabled: 是否在报告中包含 PIM 预测列. + sshm_enabled: 是否在报告中包含 SS-HM 预测列. + ihshm_enabled: 是否在报告中包含 iHS-HM 预测列. + 返回: + Path: 生成的 xlsx 文件路径. + """ + output_dir.mkdir(parents=True, exist_ok=True) + output_path = output_dir / f"{task_id}_integration_report.xlsx" + + wb = openpyxl.Workbook() + + # Sheet 1: TIC 峰表 + ws_tic = wb.active + ws_tic.title = "TIC峰表" + self._write_tic_sheet( + ws_tic, sample_results, structure_images, + nist_max_hits=nist_max_hits, + pim_enabled=pim_enabled, + sshm_enabled=sshm_enabled, + ihshm_enabled=ihshm_enabled, + ) + + # Sheet 2: FID 峰表 + ws_fid = wb.create_sheet("FID峰表") + self._write_fid_sheet(ws_fid, sample_results) + + # Sheet 3: TIC-FID 对照表 + ws_align = wb.create_sheet("TIC-FID对照表") + self._write_alignment_sheet( + ws_align, + sample_results=sample_results, + structure_images=structure_images, + nist_max_hits=nist_max_hits, + tolerance=alignment_tolerance, + include_tic_only=include_tic_only, + include_fid_only=include_fid_only, + pim_enabled=pim_enabled, + sshm_enabled=sshm_enabled, + ihshm_enabled=ihshm_enabled, + ) + + # Sheet 4: 样品汇总 + ws_summary = wb.create_sheet("样品汇总") + self._write_summary_sheet(ws_summary, sample_results) + + wb.save(str(output_path)) + logger.info("积分报告已保存: %s", output_path) + return output_path + + def _write_header(self, ws, headers: List[str]) -> None: + """写入表头行并设置样式.""" + for col_idx, header in enumerate(headers, start=1): + cell = ws.cell(row=1, column=col_idx, value=header) + cell.font = self._HEADER_FONT + cell.fill = self._HEADER_FILL + cell.alignment = self._HEADER_ALIGN + + def _auto_column_width(self, ws) -> None: + """根据内容自动调整列宽.""" + for col_cells in ws.columns: + max_length = 0 + col_letter = get_column_letter(col_cells[0].column) + for cell in col_cells: + if cell.value is not None: + # 中文字符按2字节计算宽度 + val_str = str(cell.value) + char_len = sum(2 if ord(c) > 127 else 1 for c in val_str) + max_length = max(max_length, char_len) + ws.column_dimensions[col_letter].width = min(max_length + 3, 30) + + @staticmethod + def _build_tic_headers( + compound_slot_count: int, + pim_enabled: bool, sshm_enabled: bool, ihshm_enabled: bool, + ) -> List[str]: + """ + 功能: + 根据启用的预测方法动态构建 TIC 峰表表头列表. + 参数: + compound_slot_count: 化合物候选列组数量. + pim_enabled: 是否启用 PIM 预测方法. + sshm_enabled: 是否启用 SS-HM 预测方法. + ihshm_enabled: 是否启用 iHS-HM 预测方法. + 返回: + List[str], TIC 峰表表头列表. + """ + headers = [ + "样品名", "峰号", "保留时间(min)", "峰高", "峰面积", + "面积%", "峰起始(min)", "峰结束(min)", "峰宽(min)", + ] + headers.extend( + ReportGenerator._build_compound_headers(compound_slot_count) + ) + headers.append("质谱图") + if pim_enabled is True: + headers.extend(["PIM预测分子量(Da)", "PIM置信指数"]) + if sshm_enabled is True: + headers.extend(["SS-HM预测分子量(Da)", "SS-HM置信度"]) + if ihshm_enabled is True: + headers.extend(["iHS-HM预测分子量(Da)", "iHS-HM置信度"]) + return headers + + @staticmethod + def _build_alignment_headers( + compound_slot_count: int, + pim_enabled: bool, sshm_enabled: bool, ihshm_enabled: bool, + ) -> List[str]: + """ + 功能: + 根据启用的预测方法动态构建 TIC-FID 对照表表头列表. + 参数: + compound_slot_count: 化合物候选列组数量. + pim_enabled: 是否启用 PIM 预测方法. + sshm_enabled: 是否启用 SS-HM 预测方法. + ihshm_enabled: 是否启用 iHS-HM 预测方法. + 返回: + List[str], TIC-FID 对照表表头列表. + """ + headers = [ + "样品名", "FID峰号", "FID保留时间(min)", + "TIC峰号", "TIC保留时间(min)", "FID峰面积", + ] + headers.extend( + ReportGenerator._build_compound_headers(compound_slot_count) + ) + if pim_enabled is True: + headers.extend(["PIM预测分子量(Da)", "PIM置信指数"]) + if sshm_enabled is True: + headers.extend(["SS-HM预测分子量(Da)", "SS-HM置信度"]) + if ihshm_enabled is True: + headers.extend(["iHS-HM预测分子量(Da)", "iHS-HM置信度"]) + headers.append("质谱图") + return headers + + @staticmethod + def _build_col_map(headers: List[str]) -> Dict[str, int]: + """ + 功能: + 将表头列表转换为列名 -> 1-based 列号的映射字典. + 参数: + headers: 表头名称列表. + 返回: + Dict[str, int], 列名到列号的映射. + """ + return {name: idx for idx, name in enumerate(headers, start=1)} + + @staticmethod + def _normalize_nist_max_hits(nist_max_hits: int) -> int: + """ + 功能: + 归一化 NIST 匹配化合物数量上限, 确保至少为 1. + 参数: + nist_max_hits: setting.py 中配置的命中数量上限. + 返回: + int, 归一化后的命中数量上限. + """ + if nist_max_hits < 1: + return 1 + return nist_max_hits + + @staticmethod + def _build_compound_headers(compound_slot_count: int) -> List[str]: + """ + 功能: + 根据化合物候选数量构建动态列组表头. + 参数: + compound_slot_count: 化合物候选列组数量. + 返回: + List[str], 动态化合物列组表头. + """ + headers: List[str] = [] + for rank in range(1, compound_slot_count + 1): + headers.extend([ + f"化合物{rank}(名称)", + f"化合物{rank}(匹配度)", + f"化合物{rank}(分子式)", + f"化合物{rank}(分子量)", + ]) + return headers + + @staticmethod + def _build_compound_col_groups( + col_map: Dict[str, int], + compound_slot_count: int, + ) -> List[Tuple[int, int, int, int]]: + """ + 功能: + 将动态化合物列组转换为列号元组列表. + 参数: + col_map: 表头名称到列号的映射. + compound_slot_count: 化合物候选列组数量. + 返回: + List[Tuple[int, int, int, int]], 每组依次为名称/匹配度/分子式/分子量列号. + """ + compound_cols: List[Tuple[int, int, int, int]] = [] + for rank in range(1, compound_slot_count + 1): + compound_cols.append( + ( + col_map[f"化合物{rank}(名称)"], + col_map[f"化合物{rank}(匹配度)"], + col_map[f"化合物{rank}(分子式)"], + col_map[f"化合物{rank}(分子量)"], + ) + ) + return compound_cols + + def _determine_compound_slot_count( + self, + sample_results: List[SampleResult], + nist_max_hits: int, + ) -> int: + """ + 功能: + 根据 setting.py 中的命中上限和当前任务实际结果, 确定报表中的化合物列组数量. + 参数: + sample_results: 各样品积分结果列表. + nist_max_hits: setting.py 中配置的命中数量上限. + 返回: + int, 报表应输出的化合物列组数量. + """ + normalized_hits = self._normalize_nist_max_hits(nist_max_hits) + actual_max_hits = 0 + for sample_result in sample_results: + for match_list in sample_result.compound_matches.values(): + actual_max_hits = max(actual_max_hits, len(match_list)) + + if actual_max_hits < 1: + return 1 + return min(normalized_hits, actual_max_hits) + + @staticmethod + def _format_acq_time(raw_time: str) -> str: + """ + 功能: + 将 ISO 8601 采集时间字符串格式化为 YYYY-MM-DD HH:MM:SS. + 解析失败时原样返回, 空字符串直接返回. + 参数: + raw_time: 原始时间字符串, 如 "2026-02-26T22:34:30.9070148+08:00". + 返回: + str, 格式化后的时间字符串, 如 "2026-02-26 22:34:30". + """ + if not raw_time: + return raw_time + try: + # 截断子秒到6位, 确保 fromisoformat 兼容 Python < 3.11 + truncated = re.sub(r"(\.\d{1,6})\d*", r"\1", raw_time) + dt = datetime.fromisoformat(truncated) + return dt.strftime("%Y-%m-%d %H:%M:%S") + except (ValueError, TypeError): + return raw_time + + def _write_tic_sheet( + self, + ws, + sample_results: List[SampleResult], + structure_images: Optional[Dict[str, Optional[Path]]] = None, + nist_max_hits: int = 5, + pim_enabled: bool = True, + sshm_enabled: bool = True, + ihshm_enabled: bool = True, + ) -> None: + """ + 功能: + 写入 TIC 峰表 Sheet, 包含所有样品的 TIC 峰检测积分结果, + 多候选化合物匹配结果, 并在化合物名称列写入结构图超链接. + 根据启用的预测方法动态构建表头, 未启用的方法不出现对应列. + 参数: + ws: openpyxl Worksheet. + sample_results: 各样品积分结果列表. + structure_images: 结构键 -> 结构图 PNG 路径, None 表示无结构图. + nist_max_hits: setting.py 中配置的命中数量上限. + pim_enabled: 是否启用 PIM 预测方法. + sshm_enabled: 是否启用 SS-HM 预测方法. + ihshm_enabled: 是否启用 iHS-HM 预测方法. + 返回: + 无. + """ + compound_slot_count = self._determine_compound_slot_count( + sample_results=sample_results, + nist_max_hits=nist_max_hits, + ) + headers = self._build_tic_headers( + compound_slot_count, pim_enabled, sshm_enabled, ihshm_enabled + ) + col = self._build_col_map(headers) + self._write_header(ws, headers) + + # 化合物列组: (名称列, 匹配度列, 分子式列, 分子量列) + compound_cols = self._build_compound_col_groups(col, compound_slot_count) + + row = 2 + for sr in sample_results: + for peak_num, peak in enumerate(sr.tic_peaks, start=1): + # 按保留时间查找化合物匹配列表 + match_list = self._find_match_list( + peak.retention_time, sr.compound_matches + ) + + ws.cell(row=row, column=col["样品名"], value=sr.sample_name) + ws.cell(row=row, column=col["峰号"], value=peak_num) + ws.cell(row=row, column=col["保留时间(min)"], value=round(peak.retention_time, 3)) + ws.cell(row=row, column=col["峰高"], value=round(peak.height, 0)) + ws.cell(row=row, column=col["峰面积"], value=round(peak.area, 2)) + ws.cell(row=row, column=col["面积%"], value=round(peak.area_percent, 2)) + ws.cell(row=row, column=col["峰起始(min)"], value=round(peak.start_time, 3)) + ws.cell(row=row, column=col["峰结束(min)"], value=round(peak.end_time, 3)) + ws.cell(row=row, column=col["峰宽(min)"], value=round(peak.width, 3)) + + # 填充化合物候选列组 (每个化合物占4列: 名称, 匹配度, 分子式, 分子量) + for i, (c_name, c_score, c_formula, c_mw) in enumerate(compound_cols): + if match_list is not None and i < len(match_list): + m = match_list[i] + name_cell = ws.cell(row=row, column=c_name, value=m.compound_name) + ws.cell(row=row, column=c_score, value=round(m.match_score, 1)) + # 名称单元格挂结构图链接, 保持表头简洁. + self._link_compound_name_cell(name_cell, m, structure_images) + + # 分子式和分子量 + ws.cell(row=row, column=c_formula, value=m.formula) + ws.cell(row=row, column=c_mw, value=round(m.mw, 2) if m.mw else "") + else: + ws.cell(row=row, column=c_name, value="") + ws.cell(row=row, column=c_score, value="") + ws.cell(row=row, column=c_formula, value="") + ws.cell(row=row, column=c_mw, value="") + + # 质谱图超链接 + if peak_num in sr.ms_plot_paths: + ms_path = sr.ms_plot_paths[peak_num] + if ms_path.exists(): + cell = ws.cell(row=row, column=col["质谱图"], value="查看质谱") + cell.hyperlink = str(ms_path) + cell.font = Font(color="0563C1", underline="single") + + # PIM 预测结果 + if pim_enabled is True: + pim_prediction = self._find_pim_prediction( + peak.retention_time, sr.pim_predictions + ) + if pim_prediction is not None: + if pim_prediction.predicted_mw is not None: + ws.cell(row=row, column=col["PIM预测分子量(Da)"], value=pim_prediction.predicted_mw) + if pim_prediction.confidence_index is not None: + ws.cell(row=row, column=col["PIM置信指数"], value=round(pim_prediction.confidence_index, 4)) + + # SS-HM 预测结果 + if sshm_enabled is True: + sshm_prediction = self._find_prediction_by_rt( + peak.retention_time, sr.sshm_predictions + ) + if sshm_prediction is not None: + if sshm_prediction.predicted_mw is not None: + ws.cell(row=row, column=col["SS-HM预测分子量(Da)"], value=sshm_prediction.predicted_mw) + if sshm_prediction.confidence is not None: + ws.cell(row=row, column=col["SS-HM置信度"], value=round(sshm_prediction.confidence, 4)) + + # iHS-HM 预测结果 + if ihshm_enabled is True: + ihshm_prediction = self._find_prediction_by_rt( + peak.retention_time, sr.ihshm_predictions + ) + if ihshm_prediction is not None: + if ihshm_prediction.predicted_mw is not None: + ws.cell(row=row, column=col["iHS-HM预测分子量(Da)"], value=ihshm_prediction.predicted_mw) + if ihshm_prediction.confidence is not None: + ws.cell(row=row, column=col["iHS-HM置信度"], value=round(ihshm_prediction.confidence, 6)) + + row += 1 + + self._auto_column_width(ws) + + def _write_fid_sheet(self, ws, sample_results: List[SampleResult]) -> None: + """ + 功能: + 写入 FID 峰表 Sheet, 包含所有样品的 FID 峰检测积分结果. + """ + self._write_header(ws, self._FID_HEADERS) + + row = 2 + for sr in sample_results: + for peak_num, peak in enumerate(sr.fid_peaks, start=1): + ws.cell(row=row, column=1, value=sr.sample_name) + ws.cell(row=row, column=2, value=peak_num) + ws.cell(row=row, column=3, value=round(peak.retention_time, 3)) + ws.cell(row=row, column=4, value=round(peak.height, 4)) + ws.cell(row=row, column=5, value=round(peak.area, 6)) + ws.cell(row=row, column=6, value=round(peak.area_percent, 2)) + ws.cell(row=row, column=7, value=round(peak.start_time, 3)) + ws.cell(row=row, column=8, value=round(peak.end_time, 3)) + ws.cell(row=row, column=9, value=round(peak.width, 3)) + row += 1 + + self._auto_column_width(ws) + + def _write_summary_sheet(self, ws, sample_results: List[SampleResult]) -> None: + """ + 功能: + 写入样品汇总 Sheet, 每个样品一行, 包含峰数、总面积和色谱图超链接. + """ + self._write_header(ws, self._SUMMARY_HEADERS) + + for row_idx, sr in enumerate(sample_results, start=2): + tic_total_area = sum(p.area for p in sr.tic_peaks) + fid_total_area = sum(p.area for p in sr.fid_peaks) + + ws.cell(row=row_idx, column=1, value=sr.sample_name) + ws.cell(row=row_idx, column=2, value=len(sr.tic_peaks)) + ws.cell(row=row_idx, column=3, value=len(sr.fid_peaks)) + ws.cell(row=row_idx, column=4, value=round(tic_total_area, 2)) + ws.cell(row=row_idx, column=5, value=round(fid_total_area, 6)) + ws.cell(row=row_idx, column=6, value=self._format_acq_time(sr.acq_time)) + + # TIC 色谱图超链接 + if sr.tic_plot_path is not None and sr.tic_plot_path.exists(): + cell = ws.cell(row=row_idx, column=7, value="查看色谱图") + cell.hyperlink = str(sr.tic_plot_path) + cell.font = Font(color="0563C1", underline="single") + + # FID 色谱图超链接 + if sr.fid_plot_path is not None and sr.fid_plot_path.exists(): + cell = ws.cell(row=row_idx, column=8, value="查看色谱图") + cell.hyperlink = str(sr.fid_plot_path) + cell.font = Font(color="0563C1", underline="single") + + self._auto_column_width(ws) + + @staticmethod + def _align_fid_tic_peaks( + fid_peaks: List[PeakResult], + tic_peaks: List[PeakResult], + tolerance: float = 0.05, + ) -> List[Tuple[Optional[PeakResult], Optional[int], + Optional[PeakResult], Optional[int]]]: + """ + 功能: + 按保留时间对齐 FID 峰和 TIC 峰. + 贪心匹配: 遍历 FID 峰, 对每个 FID 峰在未匹配的 TIC 峰中 + 找保留时间最接近且在容差内的峰进行配对. + 参数: + fid_peaks: FID 峰列表. + tic_peaks: TIC 峰列表. + tolerance: 保留时间容差(min), 在此范围内视为同一峰. + 返回: + List of (fid_peak, fid_peak_num, tic_peak, tic_peak_num). + 未匹配的峰对应字段为 None. + """ + matched_tic: set = set() # 已匹配的 TIC 峰索引集合 + pairs: List[Tuple[Optional[PeakResult], Optional[int], + Optional[PeakResult], Optional[int]]] = [] + + # 遍历 FID 峰, 贪心匹配最近的 TIC 峰 + for fi, fp in enumerate(fid_peaks): + best_ti: Optional[int] = None + best_diff = tolerance + 1.0 + for ti, tp in enumerate(tic_peaks): + if ti in matched_tic: + continue + diff = abs(fp.retention_time - tp.retention_time) + if diff <= tolerance and diff < best_diff: + best_diff = diff + best_ti = ti + if best_ti is not None: + # FID-TIC 配对成功 + pairs.append((fp, fi + 1, tic_peaks[best_ti], best_ti + 1)) + matched_tic.add(best_ti) + else: + # FID 峰无对应 TIC 峰 + pairs.append((fp, fi + 1, None, None)) + + # 收集未匹配的 TIC 峰 + for ti, tp in enumerate(tic_peaks): + if ti not in matched_tic: + pairs.append((None, None, tp, ti + 1)) + + # 按保留时间排序 (取非 None 峰的 RT) + pairs.sort(key=lambda x: (x[0] or x[2]).retention_time) + return pairs + + def _write_alignment_sheet( + self, + ws, + sample_results: List[SampleResult], + structure_images: Optional[Dict[str, Optional[Path]]] = None, + nist_max_hits: int = 5, + tolerance: float = 0.05, + include_tic_only: bool = True, + include_fid_only: bool = True, + pim_enabled: bool = True, + sshm_enabled: bool = True, + ihshm_enabled: bool = True, + ) -> None: + """ + 功能: + 写入 TIC-FID 对照表 Sheet, 按保留时间对齐 FID 和 TIC 峰, + 并展示 TIC 峰对应的多候选化合物预测结果. + 根据启用的预测方法动态构建表头, 未启用的方法不出现对应列. + 参数: + ws: openpyxl Worksheet. + sample_results: 各样品积分结果列表. + structure_images: 结构键 -> 结构图 PNG 路径, None 表示无结构图. + nist_max_hits: setting.py 中配置的命中数量上限. + tolerance: FID-TIC 峰保留时间对齐容差(min). + include_tic_only: 是否输出 TIC 有峰但 FID 无峰的行. + include_fid_only: 是否输出 FID 有峰但 TIC 无峰的行. + pim_enabled: 是否启用 PIM 预测方法. + sshm_enabled: 是否启用 SS-HM 预测方法. + ihshm_enabled: 是否启用 iHS-HM 预测方法. + 返回: + 无. + """ + compound_slot_count = self._determine_compound_slot_count( + sample_results=sample_results, + nist_max_hits=nist_max_hits, + ) + headers = self._build_alignment_headers( + compound_slot_count, pim_enabled, sshm_enabled, ihshm_enabled + ) + col = self._build_col_map(headers) + self._write_header(ws, headers) + + # 化合物列组: (名称列, 匹配度列, 分子式列, 分子量列) + compound_cols = self._build_compound_col_groups(col, compound_slot_count) + + row = 2 + for sr in sample_results: + aligned = self._align_fid_tic_peaks( + sr.fid_peaks, sr.tic_peaks, tolerance + ) + for fid_peak, fid_num, tic_peak, tic_num in aligned: + # 根据配置过滤仅单侧有峰的行 + if fid_peak is None and not include_tic_only: + continue + if tic_peak is None and not include_fid_only: + continue + + ws.cell(row=row, column=col["样品名"], value=sr.sample_name) + + # FID 峰信息 + if fid_peak is not None: + ws.cell(row=row, column=col["FID峰号"], value=fid_num) + ws.cell(row=row, column=col["FID保留时间(min)"], + value=round(fid_peak.retention_time, 3)) + ws.cell(row=row, column=col["FID峰面积"], + value=round(fid_peak.area, 6)) + + # TIC 峰信息 + if tic_peak is not None: + ws.cell(row=row, column=col["TIC峰号"], value=tic_num) + ws.cell(row=row, column=col["TIC保留时间(min)"], + value=round(tic_peak.retention_time, 3)) + + # 查找化合物匹配结果 + match_list = self._find_match_list( + tic_peak.retention_time, sr.compound_matches + ) + # 填充化合物候选列组 (每个化合物占4列: 名称/匹配度/分子式/分子量) + for i, (c_name, c_score, c_formula, c_mw) in enumerate(compound_cols): + if match_list is not None and i < len(match_list): + m = match_list[i] + name_cell = ws.cell(row=row, column=c_name, value=m.compound_name) + # 对照表同样在名称列挂结构图链接. + self._link_compound_name_cell(name_cell, m, structure_images) + ws.cell(row=row, column=c_score, + value=round(m.match_score, 1)) + ws.cell(row=row, column=c_formula, + value=m.formula) + ws.cell(row=row, column=c_mw, + value=round(m.mw, 2) if m.mw else "") + else: + ws.cell(row=row, column=c_name, value="") + ws.cell(row=row, column=c_score, value="") + ws.cell(row=row, column=c_formula, value="") + ws.cell(row=row, column=c_mw, value="") + + # PIM 预测结果 + if pim_enabled is True: + pim_prediction = self._find_pim_prediction( + tic_peak.retention_time, sr.pim_predictions + ) + if pim_prediction is not None: + if pim_prediction.predicted_mw is not None: + ws.cell(row=row, column=col["PIM预测分子量(Da)"], value=pim_prediction.predicted_mw) + if pim_prediction.confidence_index is not None: + ws.cell(row=row, column=col["PIM置信指数"], value=round(pim_prediction.confidence_index, 4)) + + # SS-HM 预测结果 + if sshm_enabled is True: + sshm_prediction = self._find_prediction_by_rt( + tic_peak.retention_time, sr.sshm_predictions + ) + if sshm_prediction is not None: + if sshm_prediction.predicted_mw is not None: + ws.cell(row=row, column=col["SS-HM预测分子量(Da)"], value=sshm_prediction.predicted_mw) + if sshm_prediction.confidence is not None: + ws.cell(row=row, column=col["SS-HM置信度"], value=round(sshm_prediction.confidence, 4)) + + # iHS-HM 预测结果 + if ihshm_enabled is True: + ihshm_prediction = self._find_prediction_by_rt( + tic_peak.retention_time, sr.ihshm_predictions + ) + if ihshm_prediction is not None: + if ihshm_prediction.predicted_mw is not None: + ws.cell(row=row, column=col["iHS-HM预测分子量(Da)"], value=ihshm_prediction.predicted_mw) + if ihshm_prediction.confidence is not None: + ws.cell(row=row, column=col["iHS-HM置信度"], value=round(ihshm_prediction.confidence, 6)) + + # 质谱图超链接 + if tic_num is not None: + if tic_num in sr.ms_plot_paths: + ms_plot_path = sr.ms_plot_paths[tic_num] + if ms_plot_path.exists() is True: + ms_cell = ws.cell(row=row, column=col["质谱图"], value="查看质谱图") + ms_cell.hyperlink = str(ms_plot_path) + ms_cell.font = Font(color="0563C1", underline="single") + + row += 1 + + self._auto_column_width(ws) + + @staticmethod + def _find_match_list( + rt: float, + matches: Dict[float, List[CompoundMatch]], + tolerance: float = 0.05, + ) -> Optional[List[CompoundMatch]]: + """按保留时间在匹配字典中查找最接近的化合物匹配列表.""" + if not matches: + return None + closest_rt = min(matches.keys(), key=lambda r: abs(r - rt)) + if abs(closest_rt - rt) <= tolerance: + return matches[closest_rt] + return None + + @staticmethod + def _build_structure_key(match: CompoundMatch) -> Optional[str]: + """ + 功能: + 根据命中信息构建结构图映射键. + 规则: + 1. 有效 nist_id 时使用 NIST:. + 2. 否则使用 CAS:. + 参数: + match: 化合物命中对象. + 返回: + Optional[str], 结构键. + """ + if match.nist_id is not None and match.nist_id > 0: + return f"NIST:{match.nist_id}" + + cas_digits = re.sub(r"\D", "", match.cas_number.strip()) + if cas_digits: + return f"CAS:{cas_digits}" + return None + + @staticmethod + def _link_compound_name_cell( + cell, + match: CompoundMatch, + structure_images: Optional[Dict[str, Optional[Path]]] = None, + ) -> None: + """ + 功能: + 若结构图存在, 将化合物名称单元格设置为结构图超链接. + 参数: + cell: openpyxl 单元格对象. + match: 化合物命中对象. + structure_images: 结构键 -> 结构图 PNG 路径, None 表示不设置超链接. + 返回: + 无. + """ + if structure_images is None: + return + + structure_key = ReportGenerator._build_structure_key(match) + if structure_key is None: + return + + img_path = structure_images.get(structure_key) + if img_path is None: + return + if img_path.exists() is not True: + return + + cell.hyperlink = str(img_path) + cell.font = Font(color="0563C1", underline="single") + + @staticmethod + def _find_pim_prediction( + rt: float, + predictions: Dict[float, PIMPrediction], + tolerance: float = 0.05, + ) -> Optional[PIMPrediction]: + """ + 功能: + 按保留时间在 PIM 预测字典中查找最接近结果. + 参数: + rt: 目标保留时间(min). + predictions: 保留时间 -> PIM 预测结果. + tolerance: 保留时间容差(min). + 返回: + Optional[PIMPrediction], 容差内最近结果. + """ + if len(predictions) == 0: + return None + closest_rt = min(predictions.keys(), key=lambda key_rt: abs(key_rt - rt)) + if abs(closest_rt - rt) <= tolerance: + return predictions[closest_rt] + return None + + @staticmethod + def _find_prediction_by_rt( + rt: float, + predictions: Dict, + tolerance: float = 0.05, + ): + """ + 功能: + 按保留时间在预测字典中查找最接近结果 (通用版, 支持任意预测类型). + 参数: + rt: 目标保留时间(min). + predictions: 保留时间 -> 预测结果字典. + tolerance: 保留时间容差(min). + 返回: + 容差内最近的预测结果, 或 None. + """ + if len(predictions) == 0: + return None + closest_rt = min(predictions.keys(), key=lambda key_rt: abs(key_rt - rt)) + if abs(closest_rt - rt) <= tolerance: + return predictions[closest_rt] + return None + + diff --git a/unilabos/devices/eit_analysis_station/processor/structure_fetcher.py b/unilabos/devices/eit_analysis_station/processor/structure_fetcher.py new file mode 100644 index 00000000..7c20c304 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/structure_fetcher.py @@ -0,0 +1,1423 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 提供化合物结构图获取能力. + 1. NistLocalStructureFetcher: 运行时解析 NIST MSP 构建本地映射, 优先离线生成结构图. + 2. StructureFetcher: 历史 CAS -> PubChem SDF 下载并渲染链路, 仅作为回滚兜底保留. +参数: + 无. +返回: + 无. +""" + +import io +import logging +import pickle +import re +import shutil +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, List, Optional, Set, Tuple + +from .nist_matcher import CompoundMatch + +logger = logging.getLogger(__name__) + +# 无效 CAS 号模式, 跳过查询. +_INVALID_CAS_PATTERNS = {"", "0", "0-00-0", "---", "N/A", "n/a"} +_MSP_SEQ_REGEX = re.compile(r"NIST\s+MS#\s*(\d+).*?Seq#\s*([MR])\s*(\d+)", re.IGNORECASE) +_RUNTIME_CACHE_VERSION = 3 + + +def _normalize_cas_digits(cas_number: str) -> str: + """ + 功能: + 将 CAS 字符串标准化为纯数字形式, 用于索引键匹配. + 参数: + cas_number: 原始 CAS 字符串. + 返回: + str, 仅保留数字的 CAS 字符串. + """ + return re.sub(r"\D", "", cas_number.strip()) + + +def _sanitize_cas(cas_number: str) -> str: + """将 CAS 号转为合法文件名, 如 '74-95-3' -> '74-95-3'.""" + return re.sub(r"[^\d\-]", "_", cas_number.strip()) + + +def _normalize_inchikey(inchikey: str) -> str: + """ + 功能: + 规范化 InChIKey, 去除空白并转为大写. + 参数: + inchikey: 原始 InChIKey 字符串. + 返回: + str, 规范化后的 InChIKey, 空值返回空字符串. + """ + return re.sub(r"\s+", "", str(inchikey).strip()).upper() + + +def _sanitize_identifier(identifier: str) -> str: + """ + 功能: + 将任意查询标识转换为安全文件名片段. + 参数: + identifier: 原始查询标识, 如 InChIKey 或化合物名. + 返回: + str, 适合作为文件名的字符串. + """ + sanitized = re.sub(r"[^A-Za-z0-9._\-]+", "_", str(identifier).strip()) + sanitized = re.sub(r"_+", "_", sanitized).strip("_") + if sanitized == "": + return "query" + return sanitized + + +def build_structure_key(nist_id: Optional[int], cas_number: str) -> Optional[str]: + """ + 功能: + 生成结构图索引键. + 1. 有效 nist_id 时返回 NIST:. + 2. 否则按 CAS 纯数字返回 CAS:. + 参数: + nist_id: NIST 命中 Id. + cas_number: CAS 号. + 返回: + Optional[str], 结构图索引键. + """ + if nist_id is not None and nist_id > 0: + return f"NIST:{nist_id}" + + cas_digits = _normalize_cas_digits(cas_number) + if cas_digits != "": + return f"CAS:{cas_digits}" + + return None + + +def _load_rdkit_modules() -> Optional[Tuple[object, object]]: + """ + 功能: + 动态加载 RDKit 的 Chem 与 Draw 模块. + 参数: + 无. + 返回: + Optional[Tuple[object, object]], 成功时返回 (Chem, Draw), 失败返回 None. + """ + try: + from rdkit import Chem + from rdkit.Chem import Draw + return Chem, Draw + except ImportError: + return None + + +def _render_rdkit_mol_to_png_bytes( + draw_module: object, + mol: object, + image_size: int, + image_ppi: int, +) -> Optional[bytes]: + """ + 功能: + 将 RDKit Mol 对象渲染为 PNG 二进制数据, 并写入指定 DPI 元数据. + 参数: + draw_module: RDKit Draw 模块对象. + mol: RDKit Mol 对象. + image_size: 输出图片尺寸, 单位像素. + image_ppi: 输出图片 DPI. + 返回: + Optional[bytes], 成功时返回 PNG 二进制数据, 失败返回 None. + """ + try: + image = draw_module.MolToImage(mol, size=(image_size, image_size)) + buffer = io.BytesIO() + image.save(buffer, format="PNG", dpi=(image_ppi, image_ppi)) + return buffer.getvalue() + except Exception as exc: + logger.warning("结构图 PNG 编码失败: %s", exc) + return None + + +@dataclass(frozen=True) +class PubChemQueryCandidate: + """ + 功能: + 描述一次 PubChem 结构查询候选项. + 参数: + query_type: 查询类型, 支持 inchikey/name/cas. + identifier: 查询值. + 返回: + PubChemQueryCandidate. + """ + query_type: str + identifier: str + + +class NistLocalStructureFetcher: + """ + 功能: + 运行时解析 NIST 导出的 MSP 与 MOL 目录, 并按命中结果获取结构图. + 优先级: + 1. 任务缓存目录中已存在结构图. + 2. 运行时映射命中后按需渲染 S.MOL. + 3. 非严格离线时回退 PubChem. + 参数: + task_cache_dir: 任务级结构图缓存目录. + seed_msp_path: NIST 导出的 MSP 文件路径, 用于运行时构建映射. + seed_mol_dir: NIST 导出的 MOL 目录, 用于按需渲染结构图. + runtime_cache_path: 运行时映射缓存文件路径. + offline_only: 是否严格离线, True 时禁止回退 PubChem. + global_cache_dir: 历史链路全局缓存目录, 仅 offline_only=False 时用于回退. + image_size: 结构图渲染像素边长, 本地 MOL 与 PubChem SDF 渲染链路均生效. + image_ppi: 结构图写盘分辨率, 本地 MOL 与 PubChem SDF 渲染链路均生效. + timeout: 回退 PubChem 超时. + request_interval: 回退 PubChem 请求间隔. + 返回: + 无. + """ + + def __init__( + self, + task_cache_dir: Path, + seed_msp_path: Optional[Path] = None, + seed_mol_dir: Optional[Path] = None, + runtime_cache_path: Optional[Path] = None, + offline_only: bool = False, + global_cache_dir: Optional[Path] = None, + image_size: int = 200, + image_ppi: int = 150, + timeout: float = 10.0, + request_interval: float = 0.2, + ) -> None: + self._task_cache_dir = Path(task_cache_dir) + self._seed_msp_path = Path(seed_msp_path) if seed_msp_path is not None else None + self._seed_mol_dir = Path(seed_mol_dir) if seed_mol_dir is not None else None + self._runtime_cache_path = ( + Path(runtime_cache_path) if runtime_cache_path is not None else None + ) + self._offline_only = offline_only + self._image_size = image_size + self._image_ppi = image_ppi + + self._task_cache_dir.mkdir(parents=True, exist_ok=True) + + self._by_nist_ms: Dict[str, str] = {} + self._by_seq_mainlib: Dict[str, str] = {} + self._by_seq_replib: Dict[str, str] = {} + self._inchikey_by_nist_ms: Dict[str, str] = {} + self._inchikey_by_seq_mainlib: Dict[str, str] = {} + self._inchikey_by_seq_replib: Dict[str, str] = {} + self._load_runtime_mapping() + + self._fallback_fetcher: Optional[StructureFetcher] = None + if self._offline_only is False: + self._fallback_fetcher = StructureFetcher( + task_cache_dir=self._task_cache_dir, + global_cache_dir=global_cache_dir, + image_size=image_size, + image_ppi=image_ppi, + timeout=timeout, + request_interval=request_interval, + ) + + def fetch_batch_from_matches( + self, + matches: List[CompoundMatch], + ) -> Dict[str, Optional[Path]]: + """ + 功能: + 批量按命中结果获取结构图. + 优先使用本地缓存和本地 MOL, 失败后回退 PubChem. + 参数: + matches: 化合物命中列表. + 返回: + Dict[str, Optional[Path]], 结构键到结构图路径映射. + """ + results: Dict[str, Optional[Path]] = {} + + for match in matches: + key = build_structure_key(match.nist_id, match.cas_number) + if key is None: + logger.debug( + "命中项缺少结构定位键, 化合物=%s, CAS=%s, NIST#=%s", + match.compound_name or "(未知)", + match.cas_number or "(空)", + match.nist_id, + ) + continue + + if key in results: + continue + + cached_path = self._get_task_cache_path(key) + if cached_path is not None: + results[key] = cached_path + continue + + on_demand_path = self._render_from_seed_mol(match, key) + if on_demand_path is not None: + results[key] = on_demand_path + continue + + logger.warning( + "结构未命中本地映射, key=%s, 化合物=%s, CAS=%s, NIST#=%s, Lib=%s", + key, + match.compound_name or "(未知)", + match.cas_number or "(空)", + match.nist_id, + match.library or "(空)", + ) + + fallback_path = self._fetch_with_legacy_fallback(match) + results[key] = fallback_path + + success_count = sum(1 for path in results.values() if path is not None) + logger.info("结构图匹配完成: %d/%d 成功", success_count, len(results)) + return results + + def _get_task_cache_path(self, structure_key: str) -> Optional[Path]: + """ + 功能: + 返回任务缓存中已存在的结构图路径. + 参数: + structure_key: 结构键. + 返回: + Optional[Path], 命中时返回路径. + """ + target_path = self._task_cache_dir / f"{structure_key.replace(':', '_')}.png" + if target_path.exists() is True: + return target_path + return None + + def _load_runtime_mapping(self) -> None: + """ + 功能: + 加载运行时结构映射. + 优先尝试缓存, 缓存无效时重新解析 MSP. + 参数: + 无. + 返回: + 无. + """ + if self._seed_mol_dir is not None: + if self._seed_mol_dir.is_dir() is True: + logger.info("已启用按需结构图渲染, MOL 目录: %s", self._seed_mol_dir) + else: + logger.warning("MOL 目录不存在, 按需结构图渲染不可用: %s", self._seed_mol_dir) + + if self._seed_msp_path is None: + logger.info("未配置 seed MSP, 将仅依赖命中 CAS 与 PubChem 回退.") + return + + if self._seed_msp_path.exists() is False: + logger.warning("seed MSP 不存在, 运行时映射不可用: %s", self._seed_msp_path) + return + + if self._seed_mol_dir is None: + logger.info("未配置 seed MOL 目录, 运行时映射将仅用于 PubChem 回退 CAS.") + elif self._seed_mol_dir.is_dir() is False: + logger.warning("seed MOL 目录不存在, 运行时映射将仅用于 PubChem 回退 CAS: %s", self._seed_mol_dir) + + if self._runtime_cache_path is not None: + if self._try_load_runtime_cache() is True: + return + + ( + by_nist_ms, + by_seq_mainlib, + by_seq_replib, + inchikey_by_nist_ms, + inchikey_by_seq_mainlib, + inchikey_by_seq_replib, + ) = self._build_runtime_mapping_from_seed() + self._by_nist_ms = by_nist_ms + self._by_seq_mainlib = by_seq_mainlib + self._by_seq_replib = by_seq_replib + self._inchikey_by_nist_ms = inchikey_by_nist_ms + self._inchikey_by_seq_mainlib = inchikey_by_seq_mainlib + self._inchikey_by_seq_replib = inchikey_by_seq_replib + + if self._runtime_cache_path is not None: + self._save_runtime_cache() + + logger.info( + "运行时结构映射构建完成: NIST键=%d, mainlib序号键=%d, replib序号键=%d, InChIKey(mainlib)=%d, InChIKey(replib)=%d", + len(self._by_nist_ms), + len(self._by_seq_mainlib), + len(self._by_seq_replib), + len(self._inchikey_by_seq_mainlib), + len(self._inchikey_by_seq_replib), + ) + + def _build_runtime_mapping_from_seed( + self, + ) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, str], Dict[str, str], Dict[str, str], Dict[str, str]]: + """ + 功能: + 从 seed MSP 与 seed MOL 构建运行时映射. + 参数: + 无. + 返回: + Tuple[Dict[str, str], Dict[str, str], Dict[str, str], Dict[str, str], Dict[str, str], Dict[str, str]]: + by_nist_ms, by_seq_mainlib, by_seq_replib, + inchikey_by_nist_ms, inchikey_by_seq_mainlib, inchikey_by_seq_replib. + """ + by_nist_ms: Dict[str, str] = {} + by_seq_mainlib: Dict[str, str] = {} + by_seq_replib: Dict[str, str] = {} + inchikey_by_nist_ms: Dict[str, str] = {} + inchikey_by_seq_mainlib: Dict[str, str] = {} + inchikey_by_seq_replib: Dict[str, str] = {} + + if self._seed_msp_path is None: + return ( + by_nist_ms, + by_seq_mainlib, + by_seq_replib, + inchikey_by_nist_ms, + inchikey_by_seq_mainlib, + inchikey_by_seq_replib, + ) + if self._seed_msp_path.exists() is False: + return ( + by_nist_ms, + by_seq_mainlib, + by_seq_replib, + inchikey_by_nist_ms, + inchikey_by_seq_mainlib, + inchikey_by_seq_replib, + ) + + available_cas_digits = self._scan_seed_mol_cas_digits() + + current_cas_digits = "" + current_inchikey = "" + with self._seed_msp_path.open("r", encoding="utf-8", errors="replace") as handle: + for raw_line in handle: + line = raw_line.strip() + if line == "": + current_cas_digits = "" + current_inchikey = "" + continue + + if line.startswith("CASNO:"): + cas_raw = line.split(":", 1)[1] + current_cas_digits = _normalize_cas_digits(cas_raw) + continue + if line.startswith("InChIKey:"): + current_inchikey = _normalize_inchikey(line.split(":", 1)[1]) + continue + + if line.startswith("Comment:") is False: + continue + match = _MSP_SEQ_REGEX.search(line) + if match is None: + continue + + nist_ms = str(int(match.group(1))) + seq_prefix = match.group(2).upper() + seq_id = str(int(match.group(3))) + + if current_cas_digits != "" and nist_ms not in by_nist_ms: + by_nist_ms[nist_ms] = current_cas_digits + if current_inchikey != "" and nist_ms not in inchikey_by_nist_ms: + inchikey_by_nist_ms[nist_ms] = current_inchikey + + if seq_prefix == "M": + if current_cas_digits != "" and seq_id not in by_seq_mainlib: + by_seq_mainlib[seq_id] = current_cas_digits + if current_inchikey != "" and seq_id not in inchikey_by_seq_mainlib: + inchikey_by_seq_mainlib[seq_id] = current_inchikey + elif seq_prefix == "R": + if current_cas_digits != "" and seq_id not in by_seq_replib: + by_seq_replib[seq_id] = current_cas_digits + if current_inchikey != "" and seq_id not in inchikey_by_seq_replib: + inchikey_by_seq_replib[seq_id] = current_inchikey + + if len(available_cas_digits) > 0: + logger.info( + "运行时映射包含未落地 MOL 的 CAS, 后续将按需回退: 映射CAS=%d, 可渲染CAS=%d", + len(set(by_nist_ms.values()) | set(by_seq_mainlib.values()) | set(by_seq_replib.values())), + len(available_cas_digits), + ) + + return ( + by_nist_ms, + by_seq_mainlib, + by_seq_replib, + inchikey_by_nist_ms, + inchikey_by_seq_mainlib, + inchikey_by_seq_replib, + ) + + def _scan_seed_mol_cas_digits(self) -> Set[str]: + """ + 功能: + 扫描 seed MOL 目录中的 S.MOL 文件. + 参数: + 无. + 返回: + Set[str], 可用于渲染的 CAS 数字集合. + """ + if self._seed_mol_dir is None: + return set() + if self._seed_mol_dir.is_dir() is False: + return set() + + cas_digits_set: Set[str] = set() + for mol_path in self._seed_mol_dir.iterdir(): + if mol_path.is_file() is False: + continue + if mol_path.suffix.lower() != ".mol": + continue + + filename = mol_path.name + upper_name = filename.upper() + if upper_name.startswith("S") is False: + continue + if upper_name.endswith(".MOL") is False: + continue + + cas_digits = filename[1:-4] + if cas_digits.isdigit() is False: + continue + cas_digits_set.add(cas_digits) + + logger.info("seed MOL 扫描完成: S键=%d, 目录=%s", len(cas_digits_set), self._seed_mol_dir) + return cas_digits_set + + def _try_load_runtime_cache(self) -> bool: + """ + 功能: + 尝试从运行时缓存文件加载映射. + 参数: + 无. + 返回: + bool, 缓存加载是否成功. + """ + if self._runtime_cache_path is None: + return False + if self._runtime_cache_path.exists() is False: + return False + + try: + with self._runtime_cache_path.open("rb") as handle: + payload = pickle.load(handle) + except Exception as exc: + logger.warning("读取运行时映射缓存失败: %s, 错误=%s", self._runtime_cache_path, exc) + return False + + if self._is_runtime_cache_valid(payload) is False: + logger.info("运行时映射缓存已失效, 将重新构建: %s", self._runtime_cache_path) + return False + + self._by_nist_ms = self._sanitize_mapping_dict(payload.get("by_nist_ms")) + self._by_seq_mainlib = self._sanitize_mapping_dict(payload.get("by_seq_mainlib")) + self._by_seq_replib = self._sanitize_mapping_dict(payload.get("by_seq_replib")) + self._inchikey_by_nist_ms = self._sanitize_mapping_dict(payload.get("inchikey_by_nist_ms")) + self._inchikey_by_seq_mainlib = self._sanitize_mapping_dict(payload.get("inchikey_by_seq_mainlib")) + self._inchikey_by_seq_replib = self._sanitize_mapping_dict(payload.get("inchikey_by_seq_replib")) + + logger.info( + "已加载运行时结构映射缓存: NIST键=%d, mainlib序号键=%d, replib序号键=%d, InChIKey(mainlib)=%d, InChIKey(replib)=%d, 缓存=%s", + len(self._by_nist_ms), + len(self._by_seq_mainlib), + len(self._by_seq_replib), + len(self._inchikey_by_seq_mainlib), + len(self._inchikey_by_seq_replib), + self._runtime_cache_path, + ) + return True + + def _is_runtime_cache_valid(self, payload: object) -> bool: + """ + 功能: + 校验运行时缓存是否与当前 seed 文件一致. + 参数: + payload: 缓存反序列化对象. + 返回: + bool, 缓存是否可用. + """ + if isinstance(payload, dict) is False: + return False + if payload.get("version") != _RUNTIME_CACHE_VERSION: + return False + if self._seed_msp_path is None: + return False + + seed_msp = payload.get("seed_msp_path") + seed_mol_dir = payload.get("seed_mol_dir") + if seed_msp != str(self._seed_msp_path): + return False + if seed_mol_dir != str(self._seed_mol_dir): + return False + + msp_stat = self._seed_msp_path.stat() + if payload.get("seed_msp_size") != msp_stat.st_size: + return False + if payload.get("seed_msp_mtime") != msp_stat.st_mtime: + return False + return True + + @staticmethod + def _sanitize_mapping_dict(raw_mapping: object) -> Dict[str, str]: + """ + 功能: + 将缓存中的映射对象标准化为字符串字典. + 参数: + raw_mapping: 原始映射对象. + 返回: + Dict[str, str], 过滤后的映射. + """ + if isinstance(raw_mapping, dict) is False: + return {} + + normalized: Dict[str, str] = {} + for key, value in raw_mapping.items(): + normalized_key = str(key).strip() + normalized_value = str(value).strip() + if normalized_key == "": + continue + if normalized_value == "": + continue + normalized[normalized_key] = normalized_value + return normalized + + def _save_runtime_cache(self) -> None: + """ + 功能: + 将当前运行时映射写入缓存文件. + 参数: + 无. + 返回: + 无. + """ + if self._runtime_cache_path is None: + return + if self._seed_msp_path is None: + return + + try: + self._runtime_cache_path.parent.mkdir(parents=True, exist_ok=True) + msp_stat = self._seed_msp_path.stat() + payload = { + "version": _RUNTIME_CACHE_VERSION, + "seed_msp_path": str(self._seed_msp_path), + "seed_mol_dir": str(self._seed_mol_dir), + "seed_msp_size": msp_stat.st_size, + "seed_msp_mtime": msp_stat.st_mtime, + "by_nist_ms": self._by_nist_ms, + "by_seq_mainlib": self._by_seq_mainlib, + "by_seq_replib": self._by_seq_replib, + "inchikey_by_nist_ms": self._inchikey_by_nist_ms, + "inchikey_by_seq_mainlib": self._inchikey_by_seq_mainlib, + "inchikey_by_seq_replib": self._inchikey_by_seq_replib, + } + with self._runtime_cache_path.open("wb") as handle: + pickle.dump(payload, handle, protocol=pickle.HIGHEST_PROTOCOL) + logger.info("运行时结构映射缓存已写入: %s", self._runtime_cache_path) + except Exception as exc: + logger.warning("写入运行时结构映射缓存失败: %s, 错误=%s", self._runtime_cache_path, exc) + + def _render_from_seed_mol( + self, + match: CompoundMatch, + structure_key: str, + ) -> Optional[Path]: + """ + 功能: + 在运行时按命中结果直接查找 MOL 并渲染结构图. + 参数: + match: 单个化合物命中. + structure_key: 结构键. + 返回: + Optional[Path], 渲染成功返回任务缓存路径. + """ + if self._seed_mol_dir is None: + return None + + if self._seed_mol_dir.is_dir() is False: + return None + + cas_digits = self._resolve_seed_cas_digits(match) + if cas_digits == "": + return None + + mol_path = self._find_seed_mol_path_by_cas(cas_digits) + if mol_path is None: + return None + + target_path = self._task_cache_dir / f"{structure_key.replace(':', '_')}.png" + if target_path.exists() is True: + return target_path + + return self._render_mol_to_png(mol_path, target_path, structure_key) + + def _resolve_seed_cas_digits(self, match: CompoundMatch) -> str: + """ + 功能: + 根据命中结果推导用于本地渲染的 CAS 数字键. + 优先级: + 1. library + Id(Seq#) 映射. + 2. 当库类型未知时, 按 NIST MS# 映射. + 3. 命中项自带 CAS. + 参数: + match: 单个化合物命中. + 返回: + str, CAS 数字键. 空字符串表示不可定位. + """ + if match.nist_id is not None and match.nist_id > 0: + seq_cas_digits = self._resolve_cas_by_seq(match.library, match.nist_id) + if seq_cas_digits != "": + return seq_cas_digits + + normalized_library = self._normalize_library_name(match.library) + if normalized_library == "": + # 库未知时, 允许把 Id 视为 NIST MS# 进行兜底映射. + nist_ms_key = str(match.nist_id) + if nist_ms_key in self._by_nist_ms: + return self._by_nist_ms[nist_ms_key] + else: + logger.debug( + "库序号映射缺失, 跳过 NIST MS# 兜底避免错配: 化合物=%s, Lib=%s, Id=%s", + match.compound_name or "(未知)", + match.library or "(空)", + match.nist_id, + ) + + return _normalize_cas_digits(match.cas_number) + + def _resolve_cas_by_seq(self, library_name: str, sequence_id: int) -> str: + """ + 功能: + 按库类型与序号查找 CAS 数字键. + 参数: + library_name: 命中来源库名称. + sequence_id: 命中 Id(Seq#). + 返回: + str, CAS 数字键. 未命中返回空字符串. + """ + normalized_library = self._normalize_library_name(library_name) + sequence_key = str(sequence_id) + + if normalized_library == "mainlib": + return self._by_seq_mainlib.get(sequence_key, "") + if normalized_library == "replib": + return self._by_seq_replib.get(sequence_key, "") + return "" + + def _resolve_seed_inchikey(self, match: CompoundMatch) -> str: + """ + 功能: + 根据命中结果推导用于 PubChem 精确查询的 InChIKey. + 优先级: + 1. 命中项自带 InChIKey. + 2. library + Id(Seq#) 映射. + 3. 当库类型未知时, 按 NIST MS# 映射. + 参数: + match: 单个化合物命中. + 返回: + str, 规范化后的 InChIKey. 空字符串表示不可定位. + """ + direct_inchikey = _normalize_inchikey(match.inchikey) + if direct_inchikey != "": + return direct_inchikey + + if match.nist_id is not None and match.nist_id > 0: + seq_inchikey = self._resolve_inchikey_by_seq(match.library, match.nist_id) + if seq_inchikey != "": + return seq_inchikey + + normalized_library = self._normalize_library_name(match.library) + if normalized_library == "": + nist_ms_key = str(match.nist_id) + return self._inchikey_by_nist_ms.get(nist_ms_key, "") + + logger.debug( + "库序号 InChIKey 映射缺失, 跳过 NIST MS# 兜底避免错配: 化合物=%s, Lib=%s, Id=%s", + match.compound_name or "(未知)", + match.library or "(空)", + match.nist_id, + ) + + return "" + + def _resolve_inchikey_by_seq(self, library_name: str, sequence_id: int) -> str: + """ + 功能: + 按库类型与序号查找 InChIKey. + 参数: + library_name: 命中来源库名称. + sequence_id: 命中 Id(Seq#). + 返回: + str, InChIKey. 未命中返回空字符串. + """ + normalized_library = self._normalize_library_name(library_name) + sequence_key = str(sequence_id) + + if normalized_library == "mainlib": + return self._inchikey_by_seq_mainlib.get(sequence_key, "") + if normalized_library == "replib": + return self._inchikey_by_seq_replib.get(sequence_key, "") + return "" + + @staticmethod + def _normalize_library_name(library_name: str) -> str: + """ + 功能: + 归一化库名称, 用于匹配 mainlib/replib. + 参数: + library_name: 原始库名称. + 返回: + str, 归一化后库标识. + """ + normalized = str(library_name).strip().lower() + if "mainlib" in normalized: + return "mainlib" + if "replib" in normalized: + return "replib" + return "" + + def _find_seed_mol_path_by_cas(self, cas_digits: str) -> Optional[Path]: + """ + 功能: + 按 CAS 数字键定位 seed MOL 文件路径. + 参数: + cas_digits: CAS 纯数字键. + 返回: + Optional[Path], 命中则返回路径. + """ + if self._seed_mol_dir is None: + return None + if cas_digits == "": + return None + + filename = f"S{cas_digits}.MOL" + direct_path = self._seed_mol_dir / filename + if direct_path.exists() is True: + return direct_path + + lower_path = self._seed_mol_dir / filename.lower() + if lower_path.exists() is True: + return lower_path + return None + + def _render_mol_to_png( + self, + mol_path: Path, + target_path: Path, + structure_key: str, + ) -> Optional[Path]: + """ + 功能: + 将 MOL 文件渲染为 PNG 并写入任务缓存目录. + 参数: + mol_path: MOL 文件路径. + target_path: 目标 PNG 路径. + structure_key: 结构键, 用于日志追踪. + 返回: + Optional[Path], 渲染成功返回目标路径. + """ + rdkit_modules = _load_rdkit_modules() + if rdkit_modules is None: + logger.warning("未安装 RDKit, 无法按需生成结构图.") + return None + chem_module, draw_module = rdkit_modules + + try: + mol = chem_module.MolFromMolFile(str(mol_path), sanitize=True, removeHs=True) + if mol is None: + mol = chem_module.MolFromMolFile(str(mol_path), sanitize=False, removeHs=True) + if mol is None: + logger.warning("按需渲染失败, MOL 解析失败: %s", mol_path) + return None + + target_path.parent.mkdir(parents=True, exist_ok=True) + png_data = _render_rdkit_mol_to_png_bytes( + draw_module=draw_module, + mol=mol, + image_size=self._image_size, + image_ppi=self._image_ppi, + ) + if png_data is None: + logger.warning("按需渲染失败, PNG 编码异常: key=%s, MOL=%s", structure_key, mol_path) + return None + + target_path.write_bytes(png_data) + logger.info("按需生成结构图成功, key=%s, 来源=%s", structure_key, mol_path.name) + return target_path + except Exception as exc: + logger.warning("按需渲染结构图异常, key=%s, MOL=%s, 错误=%s", structure_key, mol_path, exc) + return None + + @staticmethod + def _format_cas_digits(cas_digits: str) -> str: + """ + 功能: + 将纯数字 CAS 转换为带连字符格式. + 参数: + cas_digits: CAS 纯数字字符串. + 返回: + str, 规范化 CAS 字符串. 不可格式化时返回空字符串. + """ + if cas_digits.isdigit() is False: + return "" + if len(cas_digits) < 3: + return "" + + left_part = cas_digits[:-3] + middle_part = cas_digits[-3:-1] + right_part = cas_digits[-1] + if left_part == "": + return "" + return f"{left_part}-{middle_part}-{right_part}" + + def _resolve_fallback_cas_candidates(self, match: CompoundMatch) -> List[str]: + """ + 功能: + 组装 PubChem 回退时的 CAS 候选列表. + 参数: + match: 单个化合物命中. + 返回: + List[str], 候选 CAS 列表. + """ + candidates: List[str] = [] + + original_cas = match.cas_number.strip() + if original_cas not in _INVALID_CAS_PATTERNS and original_cas != "": + candidates.append(original_cas) + + mapped_cas_digits = self._resolve_seed_cas_digits(match) + if mapped_cas_digits != "": + mapped_cas = self._format_cas_digits(mapped_cas_digits) + if mapped_cas and mapped_cas not in candidates: + candidates.append(mapped_cas) + if mapped_cas_digits not in candidates: + candidates.append(mapped_cas_digits) + + return candidates + + def _resolve_fallback_query_candidates(self, match: CompoundMatch) -> List[PubChemQueryCandidate]: + """ + 功能: + 组装 PubChem 回退候选, 顺序为 InChIKey -> 全名 -> CAS. + 参数: + match: 单个化合物命中. + 返回: + List[PubChemQueryCandidate], 去重后的查询候选列表. + """ + candidates: List[PubChemQueryCandidate] = [] + seen_pairs: Set[Tuple[str, str]] = set() + + def _append_candidate(query_type: str, identifier: str) -> None: + normalized_identifier = str(identifier).strip() + if normalized_identifier == "": + return + pair = (query_type, normalized_identifier) + if pair in seen_pairs: + return + seen_pairs.add(pair) + candidates.append(PubChemQueryCandidate(query_type=query_type, identifier=normalized_identifier)) + + resolved_inchikey = self._resolve_seed_inchikey(match) + if resolved_inchikey != "": + _append_candidate("inchikey", resolved_inchikey) + + compound_name = str(match.compound_name).strip() + if compound_name != "": + _append_candidate("name", compound_name) + + for cas_number in self._resolve_fallback_cas_candidates(match): + _append_candidate("cas", cas_number) + + return candidates + + def _fetch_with_legacy_fallback(self, match: CompoundMatch) -> Optional[Path]: + """ + 功能: + 在非严格离线模式下回退到 PubChem 链路. + 查询顺序: InChIKey -> 全名 -> CAS. + 参数: + match: 单个化合物命中. + 返回: + Optional[Path], 回退链路得到的结构图路径. + """ + if self._offline_only is True: + return None + + if self._fallback_fetcher is None: + return None + + query_candidates = self._resolve_fallback_query_candidates(match) + if len(query_candidates) == 0: + logger.info( + "本地结构未命中且无可用查询候选, 跳过 PubChem 回退: 化合物=%s, NIST#=%s, Lib=%s", + match.compound_name or "(未知)", + match.nist_id, + match.library or "(空)", + ) + return None + + for candidate in query_candidates: + logger.info( + "本地结构未命中, 回退 PubChem: 化合物=%s, 查询类型=%s, 值=%s, NIST#=%s, Lib=%s", + match.compound_name or "(未知)", + candidate.query_type, + candidate.identifier, + match.nist_id, + match.library or "(空)", + ) + if candidate.query_type == "cas": + fetched_path = self._fallback_fetcher.fetch_structure(candidate.identifier) + else: + fetched_path = self._fallback_fetcher.fetch_structure_by_identifier( + candidate.identifier, + candidate.query_type, + ) + if fetched_path is not None: + return fetched_path + return None + + +class StructureFetcher: + """ + 功能: + 根据 CAS 号获取化合物 2D 结构图 PNG 文件. + 优先从本地缓存读取, 未命中时通过 PubChem REST API 下载 SDF 并本地渲染. + 参数: + task_cache_dir: 任务级缓存目录 (如 report_dir/task_id/structures/). + global_cache_dir: 全局缓存目录, 跨任务共享. None 表示不使用全局缓存. + image_size: 结构图渲染尺寸 (正方形边长, 像素). + image_ppi: 结构图写盘分辨率. + timeout: 单次 HTTP 请求超时时间 (秒). + request_interval: 连续请求间的最小间隔 (秒), 遵守 PubChem 速率限制. + 返回: + 无. + """ + + _PUBCHEM_CID_BY_RN_URL = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/xref/RN/{identifier}/cids/TXT" + _PUBCHEM_CID_BY_NAME_URL = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/{identifier}/cids/TXT" + _PUBCHEM_CID_BY_INCHIKEY_URL = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/inchikey/{identifier}/cids/TXT" + _PUBCHEM_SDF_URL = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/{cid}/record/SDF" + + def __init__( + self, + task_cache_dir: Path, + global_cache_dir: Optional[Path] = None, + image_size: int = 200, + image_ppi: int = 150, + timeout: float = 10.0, + request_interval: float = 0.2, + ) -> None: + self._task_cache_dir = task_cache_dir + self._global_cache_dir = global_cache_dir + self._image_size = image_size + self._image_ppi = image_ppi + self._timeout = timeout + self._request_interval = request_interval + + # 确保缓存目录存在. + self._task_cache_dir.mkdir(parents=True, exist_ok=True) + if self._global_cache_dir is not None: + self._global_cache_dir.mkdir(parents=True, exist_ok=True) + + def fetch_structure(self, cas_number: str) -> Optional[Path]: + """ + 功能: + 根据 CAS 号获取化合物结构图 PNG 文件路径. + 查找顺序: 任务缓存 -> 全局缓存 -> PubChem 下载 SDF 并本地渲染. + 参数: + cas_number: CAS 注册号, 如 "74-95-3". + 返回: + Optional[Path]: PNG 文件路径 (位于任务缓存目录), 失败返回 None. + """ + if cas_number.strip() in _INVALID_CAS_PATTERNS: + return None + + filename = f"{_sanitize_cas(cas_number)}.png" + cached_path = self._get_cached_structure_path(filename) + if cached_path is not None: + return cached_path + + # 第三级: PubChem 在线下载 SDF, 然后本地渲染. + sdf_text = self._download_sdf_from_pubchem(cas_number) + if sdf_text is None: + return None + + png_data = self._render_pubchem_sdf_to_png(sdf_text) + if png_data is None: + return None + + task_path = self._write_cached_structure_file(filename, png_data) + logger.info("结构图已通过 PubChem SDF 渲染并缓存: %s (%s)", cas_number, task_path) + + return task_path + + def fetch_structure_by_identifier(self, identifier: str, query_type: str) -> Optional[Path]: + """ + 功能: + 按指定标识类型直接从 PubChem 获取结构图. + 适用于 InChIKey 或化合物全名等非 CAS 查询. + 参数: + identifier: 查询标识. + query_type: 查询类型, 支持 inchikey 或 name. + 返回: + Optional[Path], 结构图路径, 失败返回 None. + """ + normalized_identifier = str(identifier).strip() + if normalized_identifier == "": + return None + + if query_type not in {"inchikey", "name"}: + logger.warning("PubChem 查询类型不支持: %s", query_type) + return None + + filename = self._build_query_cache_filename(normalized_identifier, query_type) + cached_path = self._get_cached_structure_path(filename) + if cached_path is not None: + return cached_path + + sdf_text = self._download_sdf_from_pubchem_by_identifier(normalized_identifier, query_type) + if sdf_text is None: + return None + + png_data = self._render_pubchem_sdf_to_png(sdf_text) + if png_data is None: + return None + + task_path = self._write_cached_structure_file(filename, png_data) + logger.info("结构图已通过 PubChem %s 查询渲染并缓存: %s (%s)", query_type, normalized_identifier, task_path) + return task_path + + def fetch_batch(self, cas_numbers: List[str]) -> Dict[str, Optional[Path]]: + """ + 功能: + 批量获取多个 CAS 号的结构图, 自动去重, 过滤无效 CAS. + 参数: + cas_numbers: CAS 号列表. + 返回: + Dict[str, Optional[Path]]: CAS 号 -> PNG 文件路径映射. + """ + # 去重并过滤无效值. + unique_cas = set() + for cas in cas_numbers: + if cas.strip() not in _INVALID_CAS_PATTERNS: + unique_cas.add(cas.strip()) + + results: Dict[str, Optional[Path]] = {} + download_count = 0 + + for cas in unique_cas: + # 先检查本地是否已有 (不计入下载). + sanitized = _sanitize_cas(cas) + task_path = self._task_cache_dir / f"{sanitized}.png" + needs_download = not task_path.exists() + + if needs_download and self._global_cache_dir is not None: + global_path = self._global_cache_dir / f"{sanitized}.png" + needs_download = not global_path.exists() + + # 在线下载前加间隔, 避免触发 PubChem 速率限制. + if needs_download and download_count > 0: + time.sleep(self._request_interval) + + path = self.fetch_structure(cas) + results[cas] = path + + if needs_download and path is not None: + download_count += 1 + + # 统计日志. + success_count = sum(1 for v in results.values() if v is not None) + logger.info( + "批量结构图获取完成: %d/%d 成功 (其中在线下载 %d 个)", + success_count, + len(unique_cas), + download_count, + ) + return results + + @staticmethod + def _load_requests_module() -> Optional[object]: + """ + 功能: + 动态加载 requests 模块. + 参数: + 无. + 返回: + Optional[object], 成功返回 requests 模块对象, 失败返回 None. + """ + try: + import requests + return requests + except ImportError: + logger.warning("requests 库未安装, 无法从 PubChem 下载 SDF 结构.") + return None + + def _get_cached_structure_path(self, filename: str) -> Optional[Path]: + """ + 功能: + 优先从任务缓存和全局缓存中读取已存在的结构图. + 参数: + filename: 结构图文件名. + 返回: + Optional[Path], 命中时返回任务缓存中的路径. + """ + task_path = self._task_cache_dir / filename + if task_path.exists(): + return task_path + + if self._global_cache_dir is not None: + global_path = self._global_cache_dir / filename + if global_path.exists(): + shutil.copy2(str(global_path), str(task_path)) + logger.debug("从全局缓存复制结构图: %s -> %s", global_path, task_path) + return task_path + + return None + + def _write_cached_structure_file(self, filename: str, png_data: bytes) -> Path: + """ + 功能: + 将结构图写入任务缓存, 并同步到全局缓存. + 参数: + filename: 结构图文件名. + png_data: PNG 二进制内容. + 返回: + Path, 任务缓存中的目标路径. + """ + task_path = self._task_cache_dir / filename + task_path.write_bytes(png_data) + if self._global_cache_dir is not None: + global_path = self._global_cache_dir / filename + global_path.write_bytes(png_data) + return task_path + + @staticmethod + def _build_query_cache_filename(identifier: str, query_type: str) -> str: + """ + 功能: + 根据查询类型生成结构图缓存文件名. + 参数: + identifier: 查询值. + query_type: 查询类型, 如 inchikey/name. + 返回: + str, 缓存文件名. + """ + query_prefix_map = { + "inchikey": "INCHIKEY", + "name": "NAME", + } + prefix = query_prefix_map.get(query_type, "QUERY") + return f"{prefix}_{_sanitize_identifier(identifier)}.png" + + def _download_sdf_from_pubchem(self, cas_number: str) -> Optional[str]: + """ + 功能: + 通过 PubChem REST API 下载化合物 SDF. + 两步: 先由 CAS 号查 CID, 再由 CID 下载 SDF. + CID 查询顺序优先使用 xref/RN, 未命中时回退到 name. + 参数: + cas_number: CAS 注册号. + 返回: + Optional[str]: SDF 字符串, 失败返回 None. + """ + requests_module = self._load_requests_module() + if requests_module is None: + return None + + cid = self._query_pubchem_cid_with_fallbacks(requests_module, cas_number) + if cid is None: + return None + return self._query_pubchem_sdf(requests_module, cid) + + def _download_sdf_from_pubchem_by_identifier( + self, + identifier: str, + namespace: str, + ) -> Optional[str]: + """ + 功能: + 按指定命名空间从 PubChem 下载化合物 SDF. + 流程: identifier -> CID -> SDF. + 参数: + identifier: 查询值. + namespace: 查询命名空间, 支持 inchikey 或 name. + 返回: + Optional[str], SDF 字符串, 失败返回 None. + """ + requests_module = self._load_requests_module() + if requests_module is None: + return None + + cid = self._query_pubchem_cid(requests_module, identifier, namespace) + if cid is None: + return None + return self._query_pubchem_sdf(requests_module, cid) + + def _query_pubchem_cid_with_fallbacks( + self, + requests_module: object, + cas_number: str, + ) -> Optional[str]: + """ + 功能: + 按预设顺序查询 PubChem CID. + 优先用 xref/RN 精确匹配 CAS, 未命中再回退到 name. + 参数: + requests_module: requests 模块对象. + cas_number: CAS 注册号. + 返回: + Optional[str], 首个命中的 CID, 失败返回 None. + """ + query_strategies = [ + "xref/RN", + "name", + ] + + for namespace in query_strategies: + cid = self._query_pubchem_cid(requests_module, cas_number, namespace) + if cid is not None: + if namespace != "xref/RN": + logger.info("PubChem CID 已通过名称回退命中: CAS=%s, CID=%s", cas_number, cid) + return cid + + logger.info("PubChem CID 查询未命中: CAS=%s, 已尝试 xref/RN 和 name", cas_number) + return None + + def _query_pubchem_cid( + self, + requests_module: object, + identifier: str, + namespace: str = "xref/RN", + ) -> Optional[str]: + """ + 功能: + 通过 PubChem 指定命名空间查询 CID. + 参数: + requests_module: requests 模块对象. + identifier: 查询输入, 当前支持 CAS. + namespace: PubChem 查询命名空间, 支持 xref/RN 或 name. + 返回: + Optional[str], CID 字符串, 失败返回 None. + """ + if namespace == "xref/RN": + cid_url = self._PUBCHEM_CID_BY_RN_URL.format(identifier=identifier) + elif namespace == "name": + cid_url = self._PUBCHEM_CID_BY_NAME_URL.format(identifier=identifier) + elif namespace == "inchikey": + cid_url = self._PUBCHEM_CID_BY_INCHIKEY_URL.format(identifier=identifier) + else: + logger.warning("PubChem CID 查询命名空间不支持: %s", namespace) + return None + + try: + resp = requests_module.get(cid_url, timeout=self._timeout) + if resp.status_code != 200: + logger.debug( + "PubChem CID 查询失败: 输入=%s, namespace=%s, HTTP %d", + identifier, + namespace, + resp.status_code, + ) + return None + lines = resp.text.strip().splitlines() + if len(lines) == 0: + logger.debug( + "PubChem CID 查询失败: 输入=%s, namespace=%s, 返回内容为空", + identifier, + namespace, + ) + return None + cid = lines[0].strip() + if cid == "": + logger.debug( + "PubChem CID 查询失败: 输入=%s, namespace=%s, CID 为空", + identifier, + namespace, + ) + return None + return cid + except Exception as exc: + logger.debug( + "PubChem CID 查询异常: 输入=%s, namespace=%s, %s", + identifier, + namespace, + exc, + ) + return None + + def _query_pubchem_sdf(self, requests_module: object, cid: str) -> Optional[str]: + """ + 功能: + 通过 PubChem 查询 CID 对应的 SDF 结构数据. + 参数: + requests_module: requests 模块对象. + cid: PubChem CID. + 返回: + Optional[str], SDF 字符串, 失败返回 None. + """ + sdf_url = self._PUBCHEM_SDF_URL.format(cid=cid) + params = {"record_type": "2d"} + try: + resp = requests_module.get(sdf_url, params=params, timeout=self._timeout) + if resp.status_code != 200: + logger.debug("PubChem SDF 下载失败: CID=%s, HTTP %d", cid, resp.status_code) + return None + sdf_text = resp.text + if sdf_text.strip() == "": + logger.debug("PubChem SDF 下载失败: CID=%s, 内容为空", cid) + return None + return sdf_text + except Exception as exc: + logger.debug("PubChem SDF 下载异常: CID=%s, %s", cid, exc) + return None + + def _render_pubchem_sdf_to_png(self, sdf_text: str) -> Optional[bytes]: + """ + 功能: + 将 PubChem 返回的 SDF 字符串渲染为 PNG 二进制数据. + 参数: + sdf_text: PubChem SDF 字符串. + 返回: + Optional[bytes], 成功时返回 PNG 二进制数据, 失败返回 None. + """ + rdkit_modules = _load_rdkit_modules() + if rdkit_modules is None: + logger.warning("未安装 RDKit, 无法渲染 PubChem SDF 结构图.") + return None + chem_module, draw_module = rdkit_modules + + mol_block = sdf_text + if "$$$$" in mol_block: + mol_block = mol_block.split("$$$$", 1)[0] + mol_block = mol_block.strip() + if mol_block == "": + logger.warning("PubChem SDF 内容为空, 无法渲染结构图.") + return None + + try: + mol = chem_module.MolFromMolBlock(mol_block, sanitize=True, removeHs=True) + if mol is None: + mol = chem_module.MolFromMolBlock(mol_block, sanitize=False, removeHs=True) + if mol is None: + logger.warning("PubChem SDF 解析失败, 无法渲染结构图.") + return None + + png_data = _render_rdkit_mol_to_png_bytes( + draw_module=draw_module, + mol=mol, + image_size=self._image_size, + image_ppi=self._image_ppi, + ) + if png_data is None: + logger.warning("PubChem SDF 渲染失败, PNG 编码异常.") + return None + return png_data + except Exception as exc: + logger.warning("PubChem SDF 渲染异常: %s", exc) + return None diff --git a/unilabos/devices/eit_analysis_station/processor/yield_calculator.py b/unilabos/devices/eit_analysis_station/processor/yield_calculator.py new file mode 100644 index 00000000..c088a308 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/processor/yield_calculator.py @@ -0,0 +1,2879 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 基于 GC-FID 积分报告和实验方案, 计算各样品的产率. + 支持 ECN 法, 标准曲线法和响应因子法三种计算方式. + 从 chemical_list.xlsx 自动推算内标摩尔量, 无需手动输入浓度. +参数: + 无. +返回: + 无. +""" + +import logging +import re +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, List, Optional, Set, Tuple + +import openpyxl +import pandas as pd +from openpyxl.styles import Alignment, Font, PatternFill +from openpyxl.utils import get_column_letter +from pysmiles import PTE +from pysmiles.read_smiles import read_smiles + +from .ecn import smiles2carbontypes, ecn_dct, class_dct + +try: + from rdkit import Chem + from rdkit.Chem import Descriptors + from rdkit.Chem import inchi as RDKitInchi +except Exception: + Chem = None + Descriptors = None + RDKitInchi = None + +logger = logging.getLogger(__name__) +YIELD_CONFIG_SHEET_NAME = "GC产率计算" + + +# --------------------------------------------------------------------------- +# 数据结构 +# --------------------------------------------------------------------------- + +@dataclass +class TargetProduct: + """ + 功能: + 存储单个目标产物的配置信息. + 参数: + name: 目标产物名称. + smiles: SMILES 字符串. + formula: 分子式 (从 SMILES 自动推导). + ecn: 有效碳数 (从 SMILES 自动计算). + expected_rt: 预期保留时间(min), None 表示使用分子式匹配. + applicable_experiments: 适用实验编号列表, 如 [1,2,3]. + equivalent: 目标产物当量(eq), 默认1.0. 理论产物量 = 反应规模 * equivalent. + 返回: + TargetProduct. + """ + name: str = "" + smiles: str = "" + formula: str = "" + molecular_weight: Optional[float] = None + ecn: float = 0.0 + expected_rt: Optional[float] = None + applicable_experiments: List[int] = field(default_factory=list) + equivalent: float = 1.0 + nist_has_record: bool = False + nist_query_mode: str = "" + nist_reference_names: List[str] = field(default_factory=list) + + +@dataclass +class YieldCalcConfig: + """ + 功能: + 存储产率计算的完整配置, 从实验方案和 chemical_list 共同构建. + 参数: + is_name: 内标名称 (从原参数 Sheet 的 "内标种类" 读取). + is_smiles: 内标 SMILES (从 "GC产率计算" Sheet 读取). + is_formula: 内标分子式 (自动推导). + is_ecn: 内标 ECN (自动计算). + is_expected_rt: 内标预期保留时间(min), 可选. + is_amount: 内标用量原始值 (μL 或 mg). + is_moles: 内标实际加入摩尔量(mol), 从 chemical_list 推算. + reaction_scale_mmol: 反应规模(mmol). + calc_method: 产率计算方法, "ECN" / "标准曲线" / "响应因子". + curve_slope: 标准曲线斜率. + curve_intercept: 标准曲线截距. + response_factor: 响应因子. + products: 目标产物列表, 各自有适用实验范围. + 返回: + YieldCalcConfig. + """ + # 内标信息 + is_name: str = "" + is_smiles: str = "" + is_formula: str = "" + is_molecular_weight: Optional[float] = None + is_ecn: float = 0.0 + is_expected_rt: Optional[float] = None + is_amount: float = 0.0 + is_moles: float = 0.0 + is_nist_has_record: bool = False + is_nist_query_mode: str = "" + is_nist_reference_names: List[str] = field(default_factory=list) + # 反应信息 + reaction_scale_mmol: float = 0.0 + # 计算方法 + calc_method: str = "ECN" + curve_slope: Optional[float] = None + curve_intercept: Optional[float] = None + response_factor: Optional[float] = None + # 目标产物 + products: List[TargetProduct] = field(default_factory=list) + + +@dataclass +class SampleYieldResult: + """ + 功能: + 存储单个 (样品 x 产物) 组合的产率计算结果. + 参数: + sample_name: 样品名, 如 "729-1". + product_name: 目标产物名称. + product_fid_rt: 产物 FID 保留时间(min). + product_fid_area: 产物 FID 峰面积. + product_match_compound: 匹配到的 NIST 化合物名. + is_fid_rt: 内标 FID 保留时间(min). + is_fid_area: 内标 FID 峰面积. + is_match_compound: 内标匹配到的 NIST 化合物名. + area_ratio: FID 面积比 (产物/内标). + ecn_product: 产物 ECN. + ecn_is: 内标 ECN. + molar_ratio: 摩尔比 (产物/内标). + n_product_mol: 产物物质的量(mol). + yield_percent: 产率(%). + match_method: 峰匹配方式, 可选 NIST命中/RT命中/分子量命中. + confidence_level: 峰判定置信度分数, 0-100. + confidence_score: 峰判定置信度分数值, 0-100. + confidence_reason: 置信度说明. + hit_summary: 命中详情摘要. + nist_matched_mw: NIST 命中化合物分子量(Da). + pim_predicted_mw: PIM 预测分子量(Da). + sshm_predicted_mw: SS-HM 预测分子量(Da). + ihshm_predicted_mw: iHS-HM 预测分子量(Da). + warnings: 警告信息列表. + 返回: + SampleYieldResult. + """ + sample_name: str = "" + product_name: str = "" + # 产物峰 + product_fid_rt: Optional[float] = None + product_fid_area: Optional[float] = None + product_match_compound: str = "" + # 内标峰 + is_fid_rt: Optional[float] = None + is_fid_area: Optional[float] = None + is_match_compound: str = "" + # 计算结果 + area_ratio: Optional[float] = None + ecn_product: Optional[float] = None + ecn_is: Optional[float] = None + molar_ratio: Optional[float] = None + n_product_mol: Optional[float] = None + yield_percent: Optional[float] = None + match_method: str = "" + confidence_level: str = "" + confidence_score: Optional[float] = None + confidence_reason: str = "" + hit_summary: str = "" + remark: str = "" + nist_matched_mw: Optional[float] = None + pim_predicted_mw: Optional[float] = None + sshm_predicted_mw: Optional[float] = None + ihshm_predicted_mw: Optional[float] = None + warnings: List[str] = field(default_factory=list) + + +@dataclass +class NISTLibraryQueryResult: + """ + 功能: + 存储 NIST 库收录查询结果. + 参数: + has_record: 是否检索到对应记录. + query_mode: 命中模式, 可选 inchikey_exact/smiles_exact/formula_mw_fallback/not_found. + reference_names: 命中记录中的化合物名称集合. + reference_formulas: 命中记录中的分子式集合. + reference_mw: 命中记录中的分子量集合. + 返回: + NISTLibraryQueryResult. + """ + has_record: bool = False + query_mode: str = "not_found" + reference_names: List[str] = field(default_factory=list) + reference_formulas: List[str] = field(default_factory=list) + reference_mw: List[float] = field(default_factory=list) + + +@dataclass +class NISTHitInfo: + """ + 功能: + 存储单条峰行内的 NIST 命中信息. + 参数: + rank: 命中序号, 1 表示化合物1, 2 表示化合物2, 依此类推. + compound_name: 命中名称. + formula: 命中分子式. + molecular_weight: 命中分子量. + 返回: + NISTHitInfo. + """ + rank: int = 0 + compound_name: str = "" + formula: str = "" + molecular_weight: Optional[float] = None + + +@dataclass +class PeakDecision: + """ + 功能: + 存储单个候选峰的判定结果. + 参数: + row: 原始对照表行数据. + match_method: 峰匹配方式文本. + fid_rt: FID 保留时间(min). + fid_area: FID 峰面积. + nist_hit: 选中的 NIST 命中信息. + nist_target_matched: NIST 命中是否为目标化合物. + nist_mw_matched: NIST 命中分子量是否与目标分子量匹配. + nist_formula_matched: NIST 命中分子式是否与目标分子式匹配. + pim_mw: PIM 预测分子量. + sshm_mw: SS-HM 预测分子量. + ihshm_mw: iHS-HM 预测分子量. + mass_match_methods: 分子量命中的预测方法列表. + confidence_level: 置信度分数文本, 0-100. + confidence_score: 置信度分数值, 0-100. + confidence_reason: 置信度说明. + hit_summary: 命中详情摘要. + 返回: + PeakDecision. + """ + row: Dict[str, Any] = field(default_factory=dict) + match_method: str = "" + fid_rt: Optional[float] = None + fid_area: Optional[float] = None + nist_hit: Optional[NISTHitInfo] = None + nist_target_matched: bool = False + nist_mw_matched: bool = False + nist_formula_matched: bool = False + pim_mw: Optional[float] = None + sshm_mw: Optional[float] = None + ihshm_mw: Optional[float] = None + mass_match_methods: List[str] = field(default_factory=list) + confidence_level: str = "" + confidence_score: float = 0.0 + confidence_reason: str = "" + hit_summary: str = "" + remark: str = "" + + +# --------------------------------------------------------------------------- +# 核心类 +# --------------------------------------------------------------------------- + +class YieldCalculator: + """ + 功能: + 基于 GC-FID 积分报告和实验方案计算各样品的产率. + 支持 ECN 法, 标准曲线法和响应因子法. + 参数: + rt_tolerance: 保留时间匹配容差(min), 默认 0.1. + nist_mainlib_msp_path: NIST 主库导出的 MSP 文件路径, 用于查询化合物是否收录. + mw_tolerance_da: 分子量匹配容差(Da), 用于 NIST/预测分子量一致性判断. + pim_enabled: 是否启用 PIM 预测列读取. + sshm_enabled: 是否启用 SS-HM 预测列读取. + ihshm_enabled: 是否启用 iHS-HM 预测列读取. + 返回: + 无. + """ + + # 报告表头样式 + _HEADER_FONT = Font(bold=True, size=11) + _HEADER_FILL = PatternFill(start_color="D9E1F2", end_color="D9E1F2", fill_type="solid") + _HEADER_ALIGN = Alignment(horizontal="center", vertical="center") + + _PREDICTION_WEIGHTS: Dict[str, float] = { + "PIM": 20.0, + "SS-HM": 15.0, + "iHS-HM": 15.0, + } + + _PREDICTION_MW_COLUMNS: Dict[str, str] = { + "PIM": "PIM预测分子量(Da)", + "SS-HM": "SS-HM预测分子量(Da)", + "iHS-HM": "iHS-HM预测分子量(Da)", + } + + _PREDICTION_CONF_COLUMNS: Dict[str, str] = { + "PIM": "PIM置信指数", + "SS-HM": "SS-HM置信度", + "iHS-HM": "iHS-HM置信度", + } + + _CONFIDENCE_BAND_RULES: Dict[str, Tuple[float, float, float]] = { + "nist_strong_with_mw": (92.0, 100.0, 8.0), + "nist_strong_without_mw": (85.0, 92.0, 7.0), + "nist_weak": (55.0, 70.0, 15.0), + "nist_none_absent_multi_match": (65.0, 80.0, 15.0), + "nist_none_absent_single_match": (45.0, 60.0, 15.0), + "nist_none_absent_none": (15.0, 15.0, 0.0), + "nist_none_contradict_multi_match": (60.0, 75.0, 15.0), + "nist_none_contradict_single_match": (40.0, 55.0, 15.0), + "nist_none_contradict_none": (10.0, 10.0, 0.0), + } + + def __init__( + self, + rt_tolerance: float = 0.1, + nist_mainlib_msp_path: Optional[Path] = None, + mw_tolerance_da: float = 1.0, + pim_enabled: bool = True, + sshm_enabled: bool = True, + ihshm_enabled: bool = True, + ) -> None: + """ + 功能: + 初始化产率计算器并加载峰判定策略相关配置. + 参数: + rt_tolerance: 保留时间匹配容差(min). + nist_mainlib_msp_path: NIST 主库 MSP 路径. + mw_tolerance_da: 分子量匹配容差(Da). + pim_enabled: 是否启用 PIM 分子量读取. + sshm_enabled: 是否启用 SS-HM 分子量读取. + ihshm_enabled: 是否启用 iHS-HM 分子量读取. + 返回: + 无. + """ + self._rt_tolerance = rt_tolerance + self._nist_mainlib_msp_path = nist_mainlib_msp_path + self._mw_tolerance_da = mw_tolerance_da + self._pim_enabled = pim_enabled + self._sshm_enabled = sshm_enabled + self._ihshm_enabled = ihshm_enabled + + self._nist_index_loaded = False + self._nist_smiles_field_detected = False + self._nist_inchikey_field_detected = False + self._nist_index_by_inchikey: Dict[str, List[Dict[str, Any]]] = {} + self._nist_index_by_smiles: Dict[str, List[Dict[str, Any]]] = {} + self._nist_index_by_formula: Dict[str, List[Dict[str, Any]]] = {} + + def _get_enabled_prediction_methods(self) -> List[str]: + """ + 功能: + 返回当前启用的分子量预测方法列表. + 参数: + 无. + 返回: + List[str]: 启用方法名列表, 顺序固定为 PIM, SS-HM, iHS-HM. + """ + methods: List[str] = [] + if self._pim_enabled is True: + methods.append("PIM") + if self._sshm_enabled is True: + methods.append("SS-HM") + if self._ihshm_enabled is True: + methods.append("iHS-HM") + return methods + + def _build_yield_headers(self) -> List[str]: + """ + 功能: + 根据启用的预测方法动态构建产率结果表头. + 参数: + 无. + 返回: + List[str]: 动态表头列表. + """ + headers = [ + "样品名", "目标产物", "产物保留时间(min)", "产物FID面积", + "产物匹配化合物", "内标保留时间(min)", "内标FID面积", + "内标匹配化合物", "Ratio", "产物ECN", "内标ECN", + "产率(%)", "匹配方式", "置信度", + "NIST匹配分子量(Da)", + ] + + enabled_methods = self._get_enabled_prediction_methods() + for method_name in enabled_methods: + headers.append(self._PREDICTION_MW_COLUMNS[method_name]) + + headers.append("备注") + return headers + + # ------------------------------------------------------------------ + # 静态/辅助方法 + # ------------------------------------------------------------------ + + @staticmethod + def calculate_ecn(smiles: str) -> float: + """ + 功能: + 根据 SMILES 计算化合物的有效碳数(ECN). + 参数: + smiles: 化合物 SMILES 字符串. + 返回: + float: ECN 值. + """ + ecn = 0.0 + for ary, typ in smiles2carbontypes(smiles): + if typ == 'alcohol': + # 醇类需要区分伯/仲/叔 + key = (ary + ' ' + typ) if ary else typ + ecn += ecn_dct[key] + else: + ecn += ecn_dct[class_dct[typ]] + return ecn + + @staticmethod + def smiles_to_formula(smiles: str) -> str: + """ + 功能: + 从 SMILES 推导分子式, 使用 Hill 排序 (C, H 在前, 其余按字母序). + 参数: + smiles: 化合物 SMILES 字符串. + 返回: + str: 分子式, 如 "C13H13N". + """ + graph = read_smiles(smiles, explicit_hydrogen=True) + atom_counts: Dict[str, int] = {} + for node in graph.nodes: + elem = graph.nodes[node].get('element', '') + if elem: + atom_counts[elem] = atom_counts.get(elem, 0) + 1 + + # Hill 排序: C 在前, H 次之, 其余按字母序 + parts = [] + for elem in ['C', 'H']: + if elem in atom_counts: + count = atom_counts.pop(elem) + parts.append(elem + (str(count) if count > 1 else "")) + for elem in sorted(atom_counts.keys()): + count = atom_counts[elem] + parts.append(elem + (str(count) if count > 1 else "")) + return "".join(parts) + + @staticmethod + def smiles_to_molecular_weight(smiles: str) -> Optional[float]: + """ + 功能: + 从 SMILES 计算分子量. + 计算顺序: + 1. 优先使用 RDKit 的 MolWt, 覆盖元素更完整. + 2. RDKit 不可用或解析失败时, 回退到 pysmiles + PTE 原子量累加. + 参数: + smiles: 化合物 SMILES 字符串. + 返回: + Optional[float]: 分子量(Da), 失败时返回 None. + """ + smiles_text = str(smiles).strip() + if smiles_text == "": + return None + + if Chem is not None and Descriptors is not None: + try: + mol = Chem.MolFromSmiles(smiles_text) + if mol is not None: + return float(Descriptors.MolWt(mol)) + logger.warning("RDKit 无法解析 SMILES, 将回退 PTE 计算: %s", smiles_text) + except Exception as exc: + logger.warning("RDKit 计算分子量失败, 将回退 PTE 计算: %s, 错误=%s", smiles_text, exc) + + try: + graph = read_smiles(smiles_text, explicit_hydrogen=True) + except Exception as exc: + logger.warning("SMILES 解析失败, 无法计算分子量: %s, 错误=%s", smiles_text, exc) + return None + + total_weight = 0.0 + missing_elements: Set[str] = set() + for node in graph.nodes: + element_symbol = str(graph.nodes[node].get("element", "")).strip() + if element_symbol == "": + continue + pte_entry = PTE.get(element_symbol) + if pte_entry is None: + missing_elements.add(element_symbol) + continue + atomic_mass = pte_entry.get("AtomicMass") + if atomic_mass is None or atomic_mass == "": + missing_elements.add(element_symbol) + continue + total_weight += float(atomic_mass) + + if len(missing_elements) > 0: + logger.warning( + "分子量计算失败, 缺少元素原子量: SMILES=%s, 元素=%s", + smiles_text, + sorted(missing_elements), + ) + return None + + return total_weight + + @staticmethod + def parse_experiment_range(range_str: str) -> List[int]: + """ + 功能: + 解析 "适用实验" 字段, 支持单个编号/范围/逗号分隔/混合格式. + "all" 或空字符串返回空列表, 表示适用于所有实验. + 参数: + range_str: 适用实验字符串, 如 "1-6,9" 或 "all". + 返回: + List[int]: 实验编号列表, 空列表表示全部适用. + """ + text = str(range_str).strip().lower() + if text == "" or text == "all": + return [] + + result = [] + for part in text.split(","): + part = part.strip() + if not part: + continue + if "-" in part: + bounds = part.split("-", 1) + try: + start = int(bounds[0].strip()) + end = int(bounds[1].strip()) + result.extend(range(start, end + 1)) + except ValueError: + logger.warning("无法解析实验范围: '%s'", part) + else: + try: + result.append(int(part)) + except ValueError: + logger.warning("无法解析实验编号: '%s'", part) + return sorted(set(result)) + + @staticmethod + def _extract_experiment_number(sample_name: str) -> Optional[int]: + """ + 功能: + 从样品名提取实验编号, 如 "729-3" → 3. + 参数: + sample_name: 样品名字符串. + 返回: + Optional[int]: 实验编号, 提取失败返回 None. + """ + match = re.search(r"-(\d+)$", sample_name.strip()) + if match is not None: + return int(match.group(1)) + return None + + # ------------------------------------------------------------------ + # 内标摩尔量推算 + # ------------------------------------------------------------------ + + def _calculate_is_moles( + self, is_name: str, is_amount: float, chemical_list_path: Path + ) -> float: + """ + 功能: + 从 chemical_list.xlsx 查询内标的物化属性, + 根据 physical_state / physical_form / active_content 推算实际加入摩尔量. + 参数: + is_name: 内标种类名称 (含括号描述). + is_amount: 内标用量(μL 或 mg). + chemical_list_path: chemical_list.xlsx 文件路径. + 返回: + float: 内标摩尔量(mol). + """ + # 读取化学品库, 多工作表时按必需列优先选表. + chem_sheets = pd.read_excel(chemical_list_path, sheet_name=None) + chem_df = None + required_columns = {"substance", "molecular_weight", "physical_state"} + for sheet_name, sheet_df in chem_sheets.items(): + normalized_columns = {str(col).strip().lower() for col in sheet_df.columns} + if required_columns.issubset(normalized_columns): + chem_df = sheet_df + logger.info("化学品库解析使用工作表: %s", sheet_name) + break + + if chem_df is None: + first_sheet_name = next(iter(chem_sheets.keys())) + chem_df = chem_sheets[first_sheet_name] + logger.warning( + "chemical_list.xlsx 未命中必需列%s, 回退到第一张工作表: %s", + sorted(required_columns), + first_sheet_name, + ) + 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 + + # 在化学品库中精确匹配内标名称 + chem_info = None + 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 name == is_name: + chem_info = { + "molecular_weight": _pick(row, "molecular_weight", "mw"), + "physical_state": str(_pick(row, "physical_state", "state", default="") or "").strip().lower(), + "density": _pick(row, "density (g/ml)", "density(g/ml)", "density_g_ml", "density", default=None), + "physical_form": str(_pick(row, "physical_form", default="") or "").strip().lower(), + "active_content": _pick( + row, "active_content", + "active_content(mmol/ml or wt%)", + "active_content(mol/l or wt%)", + default="" + ), + } + break + + if chem_info is None: + logger.error("在 chemical_list.xlsx 中未找到内标: '%s' (精确匹配)", is_name) + raise ValueError(f"在 chemical_list.xlsx 中未找到内标: '{is_name}' (需精确匹配)") + + mw = chem_info["molecular_weight"] + state = chem_info["physical_state"] + form = chem_info["physical_form"] + density = chem_info["density"] + active_content = chem_info["active_content"] + + if mw is None or float(mw) <= 0: + raise ValueError(f"内标 '{is_name}' 缺少有效分子量") + + mw = float(mw) + + # 纯物质 (neat) + if form == "neat" or form == "": + if state == "liquid": + # 用量单位为 μL, 需要密度 + if density is None or float(density) <= 0: + raise ValueError(f"液体内标 '{is_name}' 缺少密度") + mass_g = is_amount / 1000.0 * float(density) + n_mol = mass_g / mw + else: + # 固体, 用量单位为 mg + n_mol = is_amount / 1000.0 / mw + logger.info("内标(纯物质) '%s': %.4f mol", is_name, n_mol) + return n_mol + + # 溶液 (solution) + if form == "solution": + content_type, content_value = self._parse_active_content(active_content, form) + if content_type == "mmol_per_ml": + # content_value 为 mmol/mL, is_amount 为 μL + n_mol = content_value * (is_amount / 1000.0) / 1000.0 + elif content_type == "wt_percent": + if density is None or float(density) <= 0: + raise ValueError(f"溶液内标 '{is_name}' 按 wt% 换算需要密度") + mass_total_g = is_amount / 1000.0 * float(density) + mass_active_g = mass_total_g * content_value / 100.0 + n_mol = mass_active_g / mw + else: + raise ValueError(f"溶液内标 '{is_name}' 的 active_content 无法解析") + logger.info("内标(溶液) '%s': %.6f mol", is_name, n_mol) + return n_mol + + # 负载型 (beads) + if form == "beads": + content_type, content_value = self._parse_active_content(active_content, form) + if content_type == "wt_percent": + # is_amount 为 mg + mass_active_mg = is_amount * content_value / 100.0 + n_mol = mass_active_mg / 1000.0 / mw + else: + raise ValueError(f"负载型内标 '{is_name}' 的 active_content 应为 wt%") + logger.info("内标(负载型) '%s': %.6f mol", is_name, n_mol) + return n_mol + + raise ValueError(f"内标 '{is_name}' 的 physical_form '{form}' 未支持") + + @staticmethod + def _parse_active_content(value: Any, physical_form: str) -> Tuple[str, float]: + """ + 功能: + 解析 active_content 字段, 结合 physical_form 区分 mmol/mL 与 wt%. + 逻辑与 station_controller._parse_active_content 一致. + 参数: + value: active_content 原始值. + physical_form: 物理形态 (solution/beads 等). + 返回: + Tuple[str, float]: (类型标记, 数值), 无法解析返回 ("", 0.0). + """ + form = (physical_form or "").lower().strip() + if value is None: + return "", 0.0 + text_raw = str(value).strip() + if text_raw == "": + return "", 0.0 + + try: + num_val = float(value) + except (ValueError, TypeError): + text_norm = text_raw.lower() + numbers = re.findall(r"[0-9]+(?:\.[0-9]+)?", text_norm) + num_val = float(numbers[0]) if len(numbers) > 0 else 0.0 + + # 按 physical_form 优先判断 + if form == "solution": + return "mmol_per_ml", num_val + if form == "beads": + return "wt_percent", num_val + + # 按文本关键词判断 + text = text_raw.lower() + if "mmol/ml" in text or "mmol per ml" in text or "mmolml" in text: + return "mmol_per_ml", num_val + if "wt%" in text or "wt percent" in text or "wt" in text: + return "wt_percent", num_val + if num_val > 0: + return "mmol_per_ml", num_val + return "", 0.0 + + # ------------------------------------------------------------------ + # 配置解析 + # ------------------------------------------------------------------ + + def parse_yield_config_legacy( + self, plan_path: Path, chemical_list_path: Path + ) -> YieldCalcConfig: + """ + 功能: + 从实验方案 xlsx 解析产率计算配置: + 1. 读原参数 Sheet: 反应规模(mmol), 内标种类, 内标用量(μL/mg). + 2. 读 "GC产率计算" Sheet: 内标SMILES, 目标产物列表, 计算方法. + 3. 查 chemical_list.xlsx, 推算内标摩尔量. + 4. 自动计算各化合物的分子式和 ECN. + 参数: + plan_path: 实验方案 xlsx 文件路径. + chemical_list_path: chemical_list.xlsx 文件路径. + 返回: + YieldCalcConfig: 产率计算配置. + """ + wb = openpyxl.load_workbook(str(plan_path), data_only=True) + + # ---------- 1. 从原参数 Sheet 读取已有字段 ---------- + ws_main = wb.worksheets[0] + params = self._read_kv_params(ws_main) + + reaction_scale = self._parse_float(params.get("反应规模(mmol)", 0)) + is_name = str(params.get("内标种类", "")).strip() + is_amount = self._parse_float(params.get("内标用量(μL/mg)", 0)) + # 兼容可能的其他格式 + if is_amount == 0: + is_amount = self._parse_float(params.get("内标用量(ul/mg)", 0)) + if is_amount == 0: + is_amount = self._parse_float(params.get("内标用量", 0)) + + # ---------- 2. 读 "GC产率计算" Sheet ---------- + if YIELD_CONFIG_SHEET_NAME not in wb.sheetnames: + raise ValueError(f"实验方案中未找到 '{YIELD_CONFIG_SHEET_NAME}' Sheet") + ws_yield = wb[YIELD_CONFIG_SHEET_NAME] + + yield_params = self._read_kv_params(ws_yield) + + is_smiles = str(yield_params.get("内标SMILES", "")).strip() + is_expected_rt = self._parse_opt_float(yield_params.get("内标预期RT(min)")) + calc_method = str(yield_params.get("产率计算方法", "ECN")).strip() + curve_slope = self._parse_opt_float(yield_params.get("标准曲线斜率")) + curve_intercept = self._parse_opt_float(yield_params.get("标准曲线截距")) + response_factor = self._parse_opt_float(yield_params.get("响应因子")) + + if not is_smiles: + raise ValueError(f"{YIELD_CONFIG_SHEET_NAME} Sheet 中未填写 '内标SMILES'") + + # ---------- 3. 读目标产物列表 ---------- + products = self._read_product_table(ws_yield) + + if len(products) == 0: + raise ValueError(f"{YIELD_CONFIG_SHEET_NAME} Sheet 中未找到目标产物列表") + + # ---------- 4. 计算分子式和 ECN ---------- + is_formula = self.smiles_to_formula(is_smiles) + is_molecular_weight = self.smiles_to_molecular_weight(is_smiles) + is_ecn = self.calculate_ecn(is_smiles) + logger.info( + "内标 '%s': 分子式=%s, 分子量=%s, ECN=%.2f", + is_name, + is_formula, + round(is_molecular_weight, 4) if is_molecular_weight is not None else "未知", + is_ecn, + ) + + for p in products: + p.formula = self.smiles_to_formula(p.smiles) + p.molecular_weight = self.smiles_to_molecular_weight(p.smiles) + p.ecn = self.calculate_ecn(p.smiles) + logger.info( + "产物 '%s': 分子式=%s, 分子量=%s, ECN=%.2f", + p.name, + p.formula, + round(p.molecular_weight, 4) if p.molecular_weight is not None else "未知", + p.ecn, + ) + + # ---------- 5. 推算内标摩尔量 ---------- + is_moles = self._calculate_is_moles(is_name, is_amount, chemical_list_path) + + wb.close() + + return YieldCalcConfig( + is_name=is_name, + is_smiles=is_smiles, + is_formula=is_formula, + is_molecular_weight=is_molecular_weight, + is_ecn=is_ecn, + is_expected_rt=is_expected_rt, + is_amount=is_amount, + is_moles=is_moles, + reaction_scale_mmol=reaction_scale, + calc_method=calc_method, + curve_slope=curve_slope, + curve_intercept=curve_intercept, + response_factor=response_factor, + products=products, + ) + + @staticmethod + def _normalize_sheet_text(value: Any) -> str: + """ + 功能: + 规范化工作表文本, 用于关键字匹配. + 参数: + value: 任意类型单元格值. + 返回: + str, 去空白并转小写后的文本. + """ + text = "" if value is None else str(value) + return ( + text.replace(" ", "") + .replace("\n", "") + .replace("\r", "") + .replace("\t", "") + .strip() + .lower() + ) + + def _score_main_config_sheet(self, worksheet: Any) -> int: + """ + 功能: + 计算工作表作为“实验方案主配置表”的匹配分数. + 参数: + worksheet: openpyxl Worksheet. + 返回: + int, 命中关键字段数量. + """ + keyword_flags = { + "反应规模(mmol)": False, + "内标种类": False, + "内标用量": False, + } + normalized_keywords = { + key: self._normalize_sheet_text(key) for key in keyword_flags.keys() + } + + scan_rows = min(worksheet.max_row, 120) + for row_index in range(1, scan_rows + 1): + cell_text = self._normalize_sheet_text(worksheet.cell(row_index, 1).value) + if cell_text == "": + continue + for key, normalized_key in normalized_keywords.items(): + if normalized_key in cell_text: + keyword_flags[key] = True + + return sum(1 for matched in keyword_flags.values() if matched) + + def _select_main_config_sheet(self, workbook: Any) -> Any: + """ + 功能: + 选择用于读取主参数的工作表. + 选择顺序: 实验方案设定 > 当前 active > 其它工作表. + 命中规则: 反应规模/内标种类/内标用量 关键字段分数最高. + 参数: + workbook: openpyxl Workbook 对象. + 返回: + Any, 选中的工作表对象. + """ + candidate_sheet_names: List[str] = [] + preferred_sheet_name = "实验方案设定" + if preferred_sheet_name in workbook.sheetnames: + candidate_sheet_names.append(preferred_sheet_name) + + active_sheet_name = workbook.active.title + if active_sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(active_sheet_name) + + for sheet_name in workbook.sheetnames: + if sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(sheet_name) + + best_candidates: List[Any] = [] + best_score = -1 + for sheet_name in candidate_sheet_names: + worksheet = workbook[sheet_name] + score = self._score_main_config_sheet(worksheet) + if score > best_score: + best_score = score + best_candidates = [worksheet] + continue + if score == best_score: + best_candidates.append(worksheet) + + if len(best_candidates) > 0 and best_score > 0: + selected_sheet = best_candidates[0] + if len(best_candidates) > 1: + candidate_names = [sheet.title for sheet in best_candidates] + logger.warning( + "产率配置命中多个主配置候选工作表, 将按优先顺序使用 [%s], 其余候选: %s", + selected_sheet.title, + candidate_names[1:], + ) + return selected_sheet + + # 兼容历史文件: 若未命中关键字段, 回退到第一张表并记录告警. + fallback_sheet = workbook.worksheets[0] + logger.warning( + "未命中主配置关键字段, 回退到第一张工作表: %s, 可用工作表: %s", + fallback_sheet.title, + workbook.sheetnames, + ) + return fallback_sheet + + def parse_yield_config( + self, plan_path: Path, chemical_list_path: Path + ) -> YieldCalcConfig: + """ + 功能: + 从实验方案 xlsx 解析产率计算配置: + 1. 读主参数 Sheet: 反应规模(mmol), 内标种类, 内标用量(μL/mg). + 2. 读 "GC产率计算" Sheet: 内标SMILES, 目标产物列表, 计算方法. + 3. 查 chemical_list.xlsx, 推算内标摩尔量. + 4. 自动计算各化合物的分子式和 ECN. + 参数: + plan_path: 实验方案 xlsx 文件路径. + chemical_list_path: chemical_list.xlsx 文件路径. + 返回: + YieldCalcConfig: 产率计算配置. + """ + wb = openpyxl.load_workbook(str(plan_path), data_only=True) + try: + # ---------- 1. 从主参数 Sheet 读取已有字段 ---------- + ws_main = self._select_main_config_sheet(wb) + if ws_main.title != wb.active.title: + logger.info( + "产率配置解析使用工作表: %s, 当前活动工作表: %s", + ws_main.title, + wb.active.title, + ) + params = self._read_kv_params(ws_main) + + reaction_scale = self._parse_float(params.get("反应规模(mmol)", 0)) + is_name = str(params.get("内标种类", "")).strip() + is_amount = self._parse_float(params.get("内标用量(μL/mg)", 0)) + # 兼容可能的其它格式. + if is_amount == 0: + is_amount = self._parse_float(params.get("内标用量(ul/mg)", 0)) + if is_amount == 0: + is_amount = self._parse_float(params.get("内标用量", 0)) + + # ---------- 2. 读 "GC产率计算" Sheet ---------- + if YIELD_CONFIG_SHEET_NAME not in wb.sheetnames: + raise ValueError(f"实验方案中未找到 '{YIELD_CONFIG_SHEET_NAME}' Sheet") + ws_yield = wb[YIELD_CONFIG_SHEET_NAME] + + yield_params = self._read_kv_params(ws_yield) + + is_smiles = str(yield_params.get("内标SMILES", "")).strip() + is_expected_rt = self._parse_opt_float(yield_params.get("内标预期RT(min)")) + calc_method = str(yield_params.get("产率计算方法", "ECN")).strip() + curve_slope = self._parse_opt_float(yield_params.get("标准曲线斜率")) + curve_intercept = self._parse_opt_float(yield_params.get("标准曲线截距")) + response_factor = self._parse_opt_float(yield_params.get("响应因子")) + + if not is_smiles: + raise ValueError(f"{YIELD_CONFIG_SHEET_NAME} Sheet 中未填写 '内标SMILES'") + + # ---------- 3. 读目标产物列表 ---------- + products = self._read_product_table(ws_yield) + + if len(products) == 0: + raise ValueError(f"{YIELD_CONFIG_SHEET_NAME} Sheet 中未找到目标产物列表") + + # ---------- 4. 计算分子式和 ECN ---------- + is_formula = self.smiles_to_formula(is_smiles) + is_molecular_weight = self.smiles_to_molecular_weight(is_smiles) + is_ecn = self.calculate_ecn(is_smiles) + logger.info( + "内标 '%s': 分子式=%s, 分子量=%s, ECN=%.2f", + is_name, + is_formula, + round(is_molecular_weight, 4) if is_molecular_weight is not None else "未知", + is_ecn, + ) + + for product in products: + product.formula = self.smiles_to_formula(product.smiles) + product.molecular_weight = self.smiles_to_molecular_weight(product.smiles) + product.ecn = self.calculate_ecn(product.smiles) + logger.info( + "产物 '%s': 分子式=%s, 分子量=%s, ECN=%.2f", + product.name, + product.formula, + round(product.molecular_weight, 4) if product.molecular_weight is not None else "未知", + product.ecn, + ) + + # ---------- 5. 推算内标摩尔量 ---------- + is_moles = self._calculate_is_moles(is_name, is_amount, chemical_list_path) + + return YieldCalcConfig( + is_name=is_name, + is_smiles=is_smiles, + is_formula=is_formula, + is_molecular_weight=is_molecular_weight, + is_ecn=is_ecn, + is_expected_rt=is_expected_rt, + is_amount=is_amount, + is_moles=is_moles, + reaction_scale_mmol=reaction_scale, + calc_method=calc_method, + curve_slope=curve_slope, + curve_intercept=curve_intercept, + response_factor=response_factor, + products=products, + ) + finally: + wb.close() + + def _read_kv_params(self, ws) -> Dict[str, Any]: + """ + 功能: + 从 worksheet 的 A/B 列读取 key-value 参数. + 遇到空行或表格表头行时停止 (表头行特征: A 列为 "适用实验" 等). + 参数: + ws: openpyxl Worksheet. + 返回: + Dict[str, Any]: 参数字典. + """ + params: Dict[str, Any] = {} + # 已知的产物表表头关键词, 遇到则停止 KV 读取 + table_header_keywords = {"适用实验", "目标产物名称", "目标产物"} + for row in ws.iter_rows(min_row=1, max_col=2, values_only=True): + key = str(row[0] or "").strip() + if not key: + continue + if key in table_header_keywords: + break + value = row[1] + if value is not None: + params[key] = value + return params + + def _read_product_table(self, ws) -> List[TargetProduct]: + """ + 功能: + 从 "GC产率计算" Sheet 中读取目标产物列表. + 查找表头行 (含 "适用实验" / "目标产物名称"), 然后逐行读取数据. + 参数: + ws: openpyxl Worksheet ("GC产率计算" Sheet). + 返回: + List[TargetProduct]: 目标产物列表. + """ + products = [] + header_row = None + col_map: Dict[str, int] = {} + + # 定位表头行 + for row_idx, row in enumerate(ws.iter_rows(min_row=1, values_only=False), start=1): + values = [str(cell.value or "").strip() for cell in row] + # 检测是否为产物表头行 + lower_values = [v.lower() for v in values] + if any("适用实验" in v for v in values) or any("目标产物" in v for v in values): + header_row = row_idx + for col_idx, val in enumerate(values): + if val: + col_map[val] = col_idx + break + + if header_row is None: + return products + + # 从表头行下一行开始读取数据 + for row in ws.iter_rows(min_row=header_row + 1, values_only=True): + values = [v for v in row] + # 跳过空行 + if all(v is None or str(v).strip() == "" for v in values): + continue + + def _get(header_name: str, default=""): + for name, idx in col_map.items(): + if header_name in name: + if idx < len(values) and values[idx] is not None: + return values[idx] + return default + + product_name = str(_get("目标产物名称", _get("目标产物"))).strip() + smiles = str(_get("SMILES")).strip() + rt_val = _get("预期RT", _get("RT")) + range_str = str(_get("适用实验")).strip() + eq_val = _get("当量", _get("eq", 1.0)) + + if not product_name or not smiles: + continue + + expected_rt = self._parse_opt_float(rt_val) + applicable = self.parse_experiment_range(range_str) + equivalent = self._parse_float(eq_val, default=1.0) + + products.append(TargetProduct( + name=product_name, + smiles=smiles, + expected_rt=expected_rt, + applicable_experiments=applicable, + equivalent=equivalent, + )) + + return products + + # ------------------------------------------------------------------ + # 数据加载 + # ------------------------------------------------------------------ + + def load_alignment_data(self, report_path: Path) -> Dict[str, List[Dict]]: + """ + 功能: + 从积分报告的 "TIC-FID对照表" Sheet 读取对齐数据, 按样品名分组. + 参数: + report_path: 积分报告 xlsx 文件路径. + 返回: + Dict[str, List[Dict]]: 样品名 → 对齐行字典列表. + """ + wb = openpyxl.load_workbook(str(report_path), data_only=True) + if "TIC-FID对照表" not in wb.sheetnames: + wb.close() + raise ValueError("积分报告中未找到 'TIC-FID对照表' Sheet") + + ws = wb["TIC-FID对照表"] + + # 读表头 + headers = [] + for cell in ws[1]: + headers.append(str(cell.value or "").strip()) + + # 按行读取数据 + data: Dict[str, List[Dict]] = {} + for row in ws.iter_rows(min_row=2, values_only=True): + row_dict: Dict[str, Any] = {} + for i, val in enumerate(row): + if i < len(headers): + row_dict[headers[i]] = val + + sample_name = str(row_dict.get("样品名", "")).strip() + if not sample_name: + continue + + if sample_name not in data: + data[sample_name] = [] + data[sample_name].append(row_dict) + + wb.close() + return data + + # ------------------------------------------------------------------ + # 峰匹配 + # ------------------------------------------------------------------ + + @staticmethod + def _normalize_smiles(smiles: str) -> str: + """ + 功能: + 规范化 SMILES 字符串, 仅做空白清理用于库查询键. + 参数: + smiles: 原始 SMILES 字符串. + 返回: + str, 规范化后的 SMILES. + """ + return re.sub(r"\s+", "", str(smiles).strip()) + + @staticmethod + def _normalize_inchikey(inchikey: str) -> str: + """ + 功能: + 规范化 InChIKey 字符串, 统一去空白并转为大写. + 参数: + inchikey: 原始 InChIKey 字符串. + 返回: + str, 规范化后的 InChIKey. + """ + return re.sub(r"\s+", "", str(inchikey).strip()).upper() + + def _smiles_to_inchikey(self, smiles: str) -> str: + """ + 功能: + 根据 SMILES 生成 InChIKey, 失败时返回空字符串. + 参数: + smiles: 目标 SMILES 字符串. + 返回: + str, 生成成功时返回规范化 InChIKey, 失败返回空字符串. + """ + smiles_text = self._normalize_smiles(smiles) + if smiles_text == "": + return "" + + if Chem is None or RDKitInchi is None: + logger.warning("RDKit 不可用, 无法生成 InChIKey: %s", smiles_text) + return "" + + try: + molecule = Chem.MolFromSmiles(smiles_text) + if molecule is None: + logger.warning("SMILES 解析失败, 无法生成 InChIKey: %s", smiles_text) + return "" + inchikey = RDKitInchi.MolToInchiKey(molecule) + except Exception as exc: + logger.warning("生成 InChIKey 失败: SMILES=%s, 错误=%s", smiles_text, exc) + return "" + + normalized_inchikey = self._normalize_inchikey(inchikey) + if normalized_inchikey == "": + logger.warning("InChIKey 生成结果为空: %s", smiles_text) + return normalized_inchikey + + @staticmethod + def _normalize_name(name: str) -> str: + """ + 功能: + 规范化化合物名称, 用于松弛匹配. + 参数: + name: 原始名称. + 返回: + str, 去符号后的低噪声名称键. + """ + return re.sub(r"[^0-9a-zA-Z\u4e00-\u9fff]+", "", str(name).strip().lower()) + + def _add_nist_entry_to_index(self, entry: Dict[str, Any]) -> None: + """ + 功能: + 将单条 NIST MSP 记录写入运行时索引. + 参数: + entry: 单条 MSP 记录字典. + 返回: + 无. + """ + name_text = str(entry.get("name", "")).strip() + if name_text == "": + return + + inchikey_text = self._normalize_inchikey(str(entry.get("inchikey", ""))) + smiles_text = self._normalize_smiles(str(entry.get("smiles", ""))) + formula_text = str(entry.get("formula", "")).strip() + + if inchikey_text != "": + if inchikey_text not in self._nist_index_by_inchikey: + self._nist_index_by_inchikey[inchikey_text] = [] + self._nist_index_by_inchikey[inchikey_text].append(entry.copy()) + self._nist_inchikey_field_detected = True + + if smiles_text != "": + if smiles_text not in self._nist_index_by_smiles: + self._nist_index_by_smiles[smiles_text] = [] + self._nist_index_by_smiles[smiles_text].append(entry.copy()) + self._nist_smiles_field_detected = True + + if formula_text != "": + if formula_text not in self._nist_index_by_formula: + self._nist_index_by_formula[formula_text] = [] + self._nist_index_by_formula[formula_text].append(entry.copy()) + + def _ensure_nist_library_index(self) -> None: + """ + 功能: + 懒加载 NIST MSP 索引, 支持按 InChIKey/SMILES/Formula+MW 查询收录状态. + 参数: + 无. + 返回: + 无. + """ + if self._nist_index_loaded is True: + return + + self._nist_index_loaded = True + self._nist_index_by_inchikey.clear() + self._nist_index_by_smiles.clear() + self._nist_index_by_formula.clear() + self._nist_inchikey_field_detected = False + self._nist_smiles_field_detected = False + + if self._nist_mainlib_msp_path is None: + logger.warning("未配置 NIST MSP 路径, 跳过 NIST 收录查询") + return + + if self._nist_mainlib_msp_path.exists() is not True: + logger.warning("NIST MSP 文件不存在: %s", self._nist_mainlib_msp_path) + return + + logger.info("开始加载 NIST MSP 索引: %s", self._nist_mainlib_msp_path) + current_entry: Dict[str, Any] = {} + record_count = 0 + try: + with self._nist_mainlib_msp_path.open("r", encoding="utf-8", errors="replace") as handle: + for raw_line in handle: + line = raw_line.strip() + if line == "": + if len(current_entry) > 0: + self._add_nist_entry_to_index(current_entry) + record_count += 1 + current_entry = {} + continue + + if ":" not in line: + continue + + key_text, value_text = line.split(":", 1) + key_name = key_text.strip().upper() + value = value_text.strip() + + if key_name == "NAME": + current_entry["name"] = value + continue + if key_name == "INCHIKEY": + current_entry["inchikey"] = value + continue + if key_name == "SMILES": + current_entry["smiles"] = value + continue + if key_name == "FORMULA": + current_entry["formula"] = value + continue + if key_name == "MW": + current_entry["mw"] = self._parse_opt_float(value) + continue + if key_name == "EXACTMASS": + if current_entry.get("mw") is None: + current_entry["mw"] = self._parse_opt_float(value) + continue + + if len(current_entry) > 0: + self._add_nist_entry_to_index(current_entry) + record_count += 1 + + logger.info( + "NIST MSP 索引加载完成: 记录=%d, InChIKey键=%d, SMILES键=%d, FORMULA键=%d, 含InChIKey字段=%s, 含SMILES字段=%s", + record_count, + len(self._nist_index_by_inchikey), + len(self._nist_index_by_smiles), + len(self._nist_index_by_formula), + self._nist_inchikey_field_detected, + self._nist_smiles_field_detected, + ) + except Exception as exc: + logger.error("加载 NIST MSP 索引失败: %s", exc) + self._nist_index_by_inchikey.clear() + self._nist_index_by_smiles.clear() + self._nist_index_by_formula.clear() + self._nist_inchikey_field_detected = False + self._nist_smiles_field_detected = False + + def _query_nist_library_by_smiles( + self, + smiles: str, + formula: str, + target_mw: Optional[float], + ) -> NISTLibraryQueryResult: + """ + 功能: + 查询目标化合物是否在 NIST 库中收录. + 查询顺序: InChIKey 精确命中 -> SMILES 精确命中 -> Formula+MW 回退命中. + 参数: + smiles: 目标 SMILES. + formula: 目标分子式. + target_mw: 目标分子量. + 返回: + NISTLibraryQueryResult, 查询结果对象. + """ + self._ensure_nist_library_index() + normalized_smiles = self._normalize_smiles(smiles) + target_inchikey = self._smiles_to_inchikey(normalized_smiles) + + if target_inchikey != "": + inchikey_entries = self._nist_index_by_inchikey.get(target_inchikey, []) + if len(inchikey_entries) > 0: + names = sorted({str(item.get("name", "")).strip() for item in inchikey_entries if str(item.get("name", "")).strip() != ""}) + formulas = sorted({str(item.get("formula", "")).strip() for item in inchikey_entries if str(item.get("formula", "")).strip() != ""}) + mw_values = sorted({float(item.get("mw")) for item in inchikey_entries if item.get("mw") is not None}) + return NISTLibraryQueryResult( + has_record=True, + query_mode="inchikey_exact", + reference_names=names, + reference_formulas=formulas, + reference_mw=mw_values, + ) + + if normalized_smiles != "": + hit_entries = self._nist_index_by_smiles.get(normalized_smiles, []) + if len(hit_entries) > 0: + names = sorted({str(item.get("name", "")).strip() for item in hit_entries if str(item.get("name", "")).strip() != ""}) + formulas = sorted({str(item.get("formula", "")).strip() for item in hit_entries if str(item.get("formula", "")).strip() != ""}) + mw_values = sorted({float(item.get("mw")) for item in hit_entries if item.get("mw") is not None}) + return NISTLibraryQueryResult( + has_record=True, + query_mode="smiles_exact", + reference_names=names, + reference_formulas=formulas, + reference_mw=mw_values, + ) + + formula_text = str(formula).strip() + if formula_text != "": + fallback_entries = self._nist_index_by_formula.get(formula_text, []) + matched_entries: List[Dict[str, Any]] = [] + for item in fallback_entries: + item_mw = item.get("mw") + if item_mw is None: + continue + if self._is_mass_match(float(item_mw), target_mw) is True: + matched_entries.append(item) + + if len(matched_entries) > 0: + names = sorted({str(item.get("name", "")).strip() for item in matched_entries if str(item.get("name", "")).strip() != ""}) + formulas = sorted({str(item.get("formula", "")).strip() for item in matched_entries if str(item.get("formula", "")).strip() != ""}) + mw_values = sorted({float(item.get("mw")) for item in matched_entries if item.get("mw") is not None}) + return NISTLibraryQueryResult( + has_record=True, + query_mode="formula_mw_fallback", + reference_names=names, + reference_formulas=formulas, + reference_mw=mw_values, + ) + + return NISTLibraryQueryResult(has_record=False, query_mode="not_found") + + def _extract_rt_for_row(self, row: Dict[str, Any]) -> Optional[float]: + """ + 功能: + 从对照表行中提取用于匹配的保留时间. + 优先 FID 保留时间, 缺失时回退 TIC 保留时间. + 参数: + row: 对照表行字典. + 返回: + Optional[float]: 保留时间(min). + """ + fid_rt = self._parse_opt_float(row.get("FID保留时间(min)")) + if fid_rt is not None: + return fid_rt + tic_rt = self._parse_opt_float(row.get("TIC保留时间(min)")) + return tic_rt + + def _extract_nist_hits(self, row: Dict[str, Any]) -> List[NISTHitInfo]: + """ + 功能: + 从对照表行动态提取 NIST 命中信息. + 支持化合物1, 化合物2, 化合物3 等任意数量候选. + 参数: + row: 对照表行字典. + 返回: + List[NISTHitInfo]: 命中信息列表. + """ + hits: List[NISTHitInfo] = [] + candidate_ranks: Set[int] = set() + + for key in row.keys(): + if isinstance(key, str) is False: + continue + key_text = key.strip() + match = re.match(r"^化合物(\d+)\((名称|分子式|分子量)\)$", key_text) + if match is None: + continue + candidate_ranks.add(int(match.group(1))) + + for rank in sorted(candidate_ranks): + name_text = str(row.get(f"化合物{rank}(名称)", "")).strip() + formula_text = str(row.get(f"化合物{rank}(分子式)", "")).strip() + mw_value = self._parse_opt_float(row.get(f"化合物{rank}(分子量)")) + if name_text != "" or formula_text != "" or mw_value is not None: + hits.append( + NISTHitInfo( + rank=rank, + compound_name=name_text, + formula=formula_text, + molecular_weight=mw_value, + ) + ) + return hits + + def _extract_mass_predictions( + self, row: Dict[str, Any] + ) -> Dict[str, Dict[str, Optional[float]]]: + """ + 功能: + 从对照表行中提取已启用算法的分子量预测值与置信度. + 参数: + row: 对照表行字典. + 返回: + Dict[str, Dict[str, Optional[float]]]: + 方法名 -> {"mw": 分子量, "confidence": 置信度}. + """ + predictions: Dict[str, Dict[str, Optional[float]]] = {} + enabled_methods = self._get_enabled_prediction_methods() + for method_name in enabled_methods: + mw_column = self._PREDICTION_MW_COLUMNS[method_name] + conf_column = self._PREDICTION_CONF_COLUMNS[method_name] + mw_value = self._parse_opt_float(row.get(mw_column)) + conf_value = self._parse_opt_float(row.get(conf_column)) + predictions[method_name] = { + "mw": mw_value, + "confidence": conf_value, + } + return predictions + + @staticmethod + def _clip_zero_to_one(value: float) -> float: + """ + 功能: + 将数值截断到 [0, 1] 区间. + 参数: + value: 输入数值. + 返回: + float: 截断后的数值. + """ + if value < 0.0: + return 0.0 + if value > 1.0: + return 1.0 + return value + + def _normalize_prediction_confidence( + self, + method_name: str, + raw_confidence: Optional[float], + mass_matched: bool, + ) -> float: + """ + 功能: + 归一化不同预测方法的置信度到 [0, 1] 区间. + 参数: + method_name: 方法名, PIM/SS-HM/iHS-HM. + raw_confidence: 原始置信度. + mass_matched: 分子量是否命中. + 返回: + float: 归一化置信度. + """ + if mass_matched is False: + return 0.0 + if raw_confidence is None: + return 0.5 + if method_name == "PIM": + return self._clip_zero_to_one(raw_confidence / 2.0) + return self._clip_zero_to_one(raw_confidence) + + def _is_mass_match(self, observed_mw: Optional[float], target_mw: Optional[float]) -> bool: + """ + 功能: + 判断观测分子量与目标分子量是否命中. + 命中规则: + 1. 两者都存在时, 分别四舍五入为整数. + 2. 仅当整数完全相等时判定命中. + 参数: + observed_mw: 观测分子量. + target_mw: 目标分子量. + 返回: + bool: 命中返回 True. + """ + if observed_mw is None: + return False + if target_mw is None: + return False + observed_int = int(round(observed_mw)) + target_int = int(round(target_mw)) + return observed_int == target_int + + def _is_name_matched( + self, + hit_name: str, + target_name: str, + reference_names: List[str], + ) -> bool: + """ + 功能: + 判断 NIST 命中名称是否与目标化合物一致. + 参数: + hit_name: NIST 命中名称. + target_name: 目标化合物名称. + reference_names: 由 NIST 收录查询得到的参考名称集合. + 返回: + bool: 名称一致返回 True. + """ + normalized_hit = self._normalize_name(hit_name) + if normalized_hit == "": + return False + + candidates: Set[str] = set() + normalized_target = self._normalize_name(target_name) + if normalized_target != "": + candidates.add(normalized_target) + for item in reference_names: + normalized_item = self._normalize_name(item) + if normalized_item != "": + candidates.add(normalized_item) + + if len(candidates) == 0: + return False + return normalized_hit in candidates + + def _classify_nist_evidence( + self, + nist_query: NISTLibraryQueryResult, + nist_target_matched: bool, + nist_mw_matched: bool, + ) -> str: + """ + 功能: + 将当前峰的 NIST 证据归类为强支持, 弱支持, 缺失或矛盾. + 参数: + nist_query: NIST 收录查询结果. + nist_target_matched: 当前峰的 NIST 候选是否直接支持目标. + nist_mw_matched: 当前峰的 NIST 候选分子量是否命中目标. + 返回: + str: 证据层级, 可选 nist_strong/nist_weak/nist_none_absent/nist_none_contradict. + """ + if nist_target_matched is True: + return "nist_strong" + if nist_mw_matched is True: + return "nist_weak" + if nist_query.has_record is True: + return "nist_none_contradict" + return "nist_none_absent" + + def _summarize_prediction_evidence( + self, + mass_match_methods: List[str], + prediction_details: Dict[str, Dict[str, Optional[float]]], + ) -> Dict[str, Any]: + """ + 功能: + 汇总各预测方法的命中情况, 归一化置信度和平均预测质量. + 参数: + mass_match_methods: 分子量预测命中的方法列表. + prediction_details: 分子量预测值与置信度明细. + 返回: + Dict[str, Any]: 包含预测层级, 命中方法和逐方法评分明细. + """ + enabled_methods = self._get_enabled_prediction_methods() + matched_method_names: List[str] = [] + matched_confidences: List[float] = [] + prediction_scores: Dict[str, Dict[str, Any]] = {} + + for method_name in enabled_methods: + method_weight = self._PREDICTION_WEIGHTS[method_name] + method_data = prediction_details.get(method_name, {}) + method_mw = method_data.get("mw") + method_confidence = method_data.get("confidence") + method_matched = method_name in mass_match_methods + method_conf_norm = self._normalize_prediction_confidence( + method_name=method_name, + raw_confidence=method_confidence, + mass_matched=method_matched, + ) + if method_matched is True: + matched_method_names.append(method_name) + matched_confidences.append(method_conf_norm) + + # 保留逐方法明细, 便于解释最终评分来源. + prediction_scores[method_name] = { + "weight": method_weight, + "mw": method_mw, + "confidence_raw": method_confidence, + "matched": method_matched, + "confidence_norm": round(method_conf_norm, 4), + "score": round(method_weight * method_conf_norm, 2), + } + + matched_method_count = len(matched_method_names) + if matched_method_count >= 2: + prediction_evidence_level = "multi_match" + elif matched_method_count == 1: + prediction_evidence_level = "single_match" + else: + prediction_evidence_level = "none" + + predictor_quality = 0.0 + if matched_method_count > 0: + predictor_quality = round(sum(matched_confidences) / matched_method_count, 4) + + return { + "enabled_methods": enabled_methods, + "matched_method_names": matched_method_names, + "matched_method_count": matched_method_count, + "prediction_evidence_level": prediction_evidence_level, + "predictor_quality": predictor_quality, + "prediction_scores": prediction_scores, + } + + def _resolve_confidence_band( + self, + nist_evidence_level: str, + prediction_evidence_level: str, + nist_mw_matched: bool, + ) -> Tuple[str, float, float, float]: + """ + 功能: + 根据 NIST 与预测证据层级选出固定评分分段和倍率. + 参数: + nist_evidence_level: NIST 证据层级. + prediction_evidence_level: 预测证据层级. + nist_mw_matched: 当前峰的 NIST 分子量是否命中目标. + 返回: + Tuple[str, float, float, float]: + (评分层级, 分段下界, 分段上界, 预测质量倍率). + """ + if nist_evidence_level == "nist_strong": + if nist_mw_matched is True: + scoring_tier = "nist_strong_with_mw" + else: + scoring_tier = "nist_strong_without_mw" + elif nist_evidence_level == "nist_weak": + scoring_tier = "nist_weak" + else: + scoring_tier = f"{nist_evidence_level}_{prediction_evidence_level}" + + band_min, band_max, quality_multiplier = self._CONFIDENCE_BAND_RULES[scoring_tier] + return scoring_tier, band_min, band_max, quality_multiplier + + @staticmethod + def _format_score_band_value(value: float) -> str: + """ + 功能: + 将评分分段端点格式化为紧凑文本. + 参数: + value: 分段端点值. + 返回: + str: 适合展示的分值文本. + """ + if float(value).is_integer(): + return str(int(value)) + return f"{value:.2f}" + + def _describe_nist_evidence( + self, + nist_evidence_level: str, + nist_mw_matched: bool, + ) -> str: + """ + 功能: + 生成 NIST 证据层级的人类可读描述. + 参数: + nist_evidence_level: NIST 证据层级. + nist_mw_matched: 当前峰的 NIST 分子量是否命中目标. + 返回: + str: NIST 证据描述. + """ + if nist_evidence_level == "nist_strong": + if nist_mw_matched is True: + return "NIST命中目标化合物, 且分子量一致" + return "NIST命中目标化合物, 但分子量未一致" + if nist_evidence_level == "nist_weak": + return "NIST仅支持分子量一致" + if nist_evidence_level == "nist_none_absent": + return "NIST无目标记录, 当前峰未获得NIST支持" + return "NIST有目标记录, 但当前峰候选未支持目标" + + def _describe_prediction_evidence( + self, + enabled_methods: List[str], + matched_method_names: List[str], + ) -> str: + """ + 功能: + 生成预测证据层级的人类可读描述. + 参数: + enabled_methods: 当前启用的预测方法列表. + matched_method_names: 分子量命中的预测方法列表. + 返回: + str: 预测证据描述. + """ + if len(enabled_methods) == 0: + return "未启用预测方法" + if len(matched_method_names) == 0: + return "预测未命中" + return f"预测命中{len(matched_method_names)}项({','.join(matched_method_names)})" + + def _describe_current_peak_nist_support( + self, + nist_target_matched: bool, + nist_formula_matched: bool, + nist_mw_matched: bool, + ) -> str: + """ + 功能: + 生成“当前峰是否被 NIST 支持”的文本描述. + 参数: + nist_target_matched: 当前峰的 NIST 候选是否直接支持目标. + nist_formula_matched: 当前峰的 NIST 候选分子式是否命中目标. + nist_mw_matched: 当前峰的 NIST 候选分子量是否命中目标. + 返回: + str: 支持说明文本. + """ + if nist_target_matched is True and nist_formula_matched is True and nist_mw_matched is True: + return "是(目标+分子式+分子量)" + if nist_target_matched is True and nist_formula_matched is True: + return "是(目标+分子式)" + if nist_target_matched is True and nist_mw_matched is True: + return "是(目标+分子量)" + if nist_target_matched is True: + return "是(目标)" + if nist_mw_matched is True: + return "是(分子量)" + return "否" + + @staticmethod + def _is_full_nist_match( + nist_target_matched: bool, + nist_mw_matched: bool, + ) -> bool: + """ + 功能: + 判断当前峰是否满足“完整 NIST 命中”. + 参数: + nist_target_matched: 当前峰的 NIST 候选是否支持目标. + nist_mw_matched: 当前峰的 NIST 候选分子量是否命中目标. + 返回: + bool: 目标与分子量同时命中时返回 True. + """ + return nist_target_matched is True and nist_mw_matched is True + + def _resolve_match_method_label( + self, + base_match_method: str, + nist_target_matched: bool, + nist_mw_matched: bool, + ) -> str: + """ + 功能: + 将内部匹配路径转换为结果表使用的匹配方式文本. + 参数: + base_match_method: 内部匹配路径标记. + nist_target_matched: 当前峰的 NIST 候选是否支持目标. + nist_mw_matched: 当前峰的 NIST 候选分子量是否命中目标. + 返回: + str: 匹配方式文本. + """ + if self._is_full_nist_match( + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + ) is True: + return "NIST命中" + if base_match_method == "rt": + return "RT命中" + return "分子量命中" + + def _build_confidence( + self, + nist_query: NISTLibraryQueryResult, + nist_target_matched: bool, + nist_mw_matched: bool, + nist_formula_matched: bool, + mass_match_methods: List[str], + prediction_details: Dict[str, Dict[str, Optional[float]]], + ) -> Tuple[float, str, Dict[str, Any]]: + """ + 功能: + 根据 NIST 与分子量预测证据输出 0-100 置信度分数与说明. + 分数使用“证据分层 + 固定分段”规则, 不再按满分归一化. + 参数: + nist_query: NIST 收录查询结果. + nist_target_matched: NIST 命中是否为目标化合物. + nist_mw_matched: NIST 命中分子量是否匹配目标. + nist_formula_matched: NIST 命中分子式是否匹配目标. + mass_match_methods: 分子量预测命中的方法列表. + prediction_details: 分子量预测值与置信度明细. + 返回: + Tuple[float, str, Dict[str, Any]]: + (置信度分数, 置信说明, 评分明细字典). + """ + nist_evidence_level = self._classify_nist_evidence( + nist_query=nist_query, + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + ) + prediction_summary = self._summarize_prediction_evidence( + mass_match_methods=mass_match_methods, + prediction_details=prediction_details, + ) + scoring_tier, band_min, band_max, quality_multiplier = self._resolve_confidence_band( + nist_evidence_level=nist_evidence_level, + prediction_evidence_level=prediction_summary["prediction_evidence_level"], + nist_mw_matched=nist_mw_matched, + ) + + predictor_quality = float(prediction_summary["predictor_quality"]) + confidence_score = round(band_min + quality_multiplier * predictor_quality, 2) + if confidence_score < band_min: + confidence_score = band_min + if confidence_score > band_max: + confidence_score = band_max + + band_min_text = self._format_score_band_value(band_min) + band_max_text = self._format_score_band_value(band_max) + reason_parts = [ + self._describe_nist_evidence( + nist_evidence_level=nist_evidence_level, + nist_mw_matched=nist_mw_matched, + ), + self._describe_prediction_evidence( + enabled_methods=prediction_summary["enabled_methods"], + matched_method_names=prediction_summary["matched_method_names"], + ), + f"平均预测置信度={predictor_quality:.4f}", + f"评分分段={band_min_text}-{band_max_text}", + f"综合得分={int(round(confidence_score))}", + ] + detail = { + "scoring_tier": scoring_tier, + "nist_evidence_level": nist_evidence_level, + "prediction_evidence_level": prediction_summary["prediction_evidence_level"], + "score_band_min": band_min, + "score_band_max": band_max, + "predictor_quality": predictor_quality, + "matched_method_count": prediction_summary["matched_method_count"], + "matched_method_names": prediction_summary["matched_method_names"], + "prediction_scores": prediction_summary["prediction_scores"], + } + return confidence_score, "; ".join(reason_parts), detail + + def _build_hit_summary( + self, + row: Dict[str, Any], + expected_rt: Optional[float], + nist_query: NISTLibraryQueryResult, + nist_target_matched: bool, + nist_mw_matched: bool, + nist_formula_matched: bool, + confidence_score: float, + confidence_detail: Dict[str, Any], + ) -> str: + """ + 功能: + 生成单峰命中详情文本, 便于审计所有证据来源. + 参数: + row: 峰行数据. + expected_rt: 目标预期 RT. + nist_query: NIST 收录查询结果. + nist_target_matched: NIST 名称/分子式命中目标. + nist_mw_matched: NIST 分子量命中目标. + nist_formula_matched: NIST 分子式命中目标. + confidence_score: 综合分数. + confidence_detail: 评分明细. + 返回: + str: 命中详情摘要. + """ + summary_parts: List[str] = [] + row_rt = self._extract_rt_for_row(row) + if expected_rt is not None and row_rt is not None: + rt_diff = abs(row_rt - expected_rt) + summary_parts.append( + f"RT={row_rt:.3f}, 预期={expected_rt:.3f}, 偏差={rt_diff:.3f}, 容差内={'是' if rt_diff <= self._rt_tolerance else '否'}" + ) + elif row_rt is not None: + summary_parts.append(f"RT={row_rt:.3f}, 无预期RT") + else: + summary_parts.append("RT缺失") + + summary_parts.append( + "NIST收录=" + + ("是" if nist_query.has_record is True else "否") + + f", 查询模式={nist_query.query_mode}, 名称/目标命中={'是' if nist_target_matched is True else '否'}" + + f", 分子式命中={'是' if nist_formula_matched is True else '否'}" + + f", 分子量命中={'是' if nist_mw_matched is True else '否'}" + ) + + prediction_scores = confidence_detail.get("prediction_scores", {}) + enabled_methods = self._get_enabled_prediction_methods() + for method_name in enabled_methods: + method_data = prediction_scores.get(method_name, {}) + method_mw = method_data.get("mw") + method_conf_raw = method_data.get("confidence_raw") + method_matched = method_data.get("matched") + method_score = method_data.get("score") + method_conf_norm = method_data.get("confidence_norm") + if method_mw is None: + mw_text = "无" + else: + mw_text = f"{float(method_mw):.4f}" + if method_conf_raw is None: + conf_text = "无" + else: + conf_text = f"{float(method_conf_raw):.4f}" + if method_score is None: + score_text = "0.00" + else: + score_text = f"{float(method_score):.2f}" + if method_conf_norm is None: + conf_norm_text = "0.0000" + else: + conf_norm_text = f"{float(method_conf_norm):.4f}" + summary_parts.append( + f"{method_name}(MW={mw_text}, 置信度={conf_text}, 命中={'是' if method_matched is True else '否'}, 归一化={conf_norm_text}, 参考分={score_text})" + ) + + band_min = float(confidence_detail.get("score_band_min", 0.0)) + band_max = float(confidence_detail.get("score_band_max", 0.0)) + predictor_quality = float(confidence_detail.get("predictor_quality", 0.0)) + scoring_tier = str(confidence_detail.get("scoring_tier", "")) + summary_parts.append( + f"评分层级={scoring_tier}, 分段={self._format_score_band_value(band_min)}-{self._format_score_band_value(band_max)}, 平均预测置信度={predictor_quality:.4f}, score={int(round(confidence_score))}" + ) + return "; ".join(summary_parts) + + def _build_remark( + self, + nist_query: NISTLibraryQueryResult, + nist_target_matched: bool, + nist_mw_matched: bool, + nist_formula_matched: bool, + mass_match_methods: List[str], + prediction_details: Dict[str, Dict[str, Optional[float]]], + nist_hit: Optional[NISTHitInfo], + ) -> str: + """ + 功能: + 生成精简的备注文本, 明确区分目标收录状态与当前峰证据状态. + 参数: + nist_query: NIST 收录查询结果. + nist_target_matched: NIST 命中是否为目标化合物. + nist_mw_matched: NIST 命中分子量是否匹配目标. + nist_formula_matched: NIST 命中分子式是否匹配目标. + mass_match_methods: 分子量预测命中的方法列表. + prediction_details: 分子量预测值与置信度明细. + nist_hit: 当前峰选中的最佳 NIST 候选. + 返回: + str: 备注文本. + """ + parts: List[str] = [] + if self._is_full_nist_match( + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + ) is True: + parts.append(f"NIST目标记录=是({nist_query.query_mode})") + else: + parts.append("NIST目标记录=否") + parts.append( + "当前峰NIST支持=" + + self._describe_current_peak_nist_support( + nist_target_matched=nist_target_matched, + nist_formula_matched=nist_formula_matched, + nist_mw_matched=nist_mw_matched, + ) + ) + if nist_hit is None or nist_hit.molecular_weight is None: + parts.append("当前峰NIST最佳候选MW=无") + else: + parts.append(f"当前峰NIST最佳候选MW={int(round(float(nist_hit.molecular_weight)))}") + + # 各预测方法的分子量和置信度 (MW 保留整数) + enabled_methods = self._get_enabled_prediction_methods() + for method_name in enabled_methods: + method_data = prediction_details.get(method_name, {}) + mw = method_data.get("mw") + conf = method_data.get("confidence") + mw_text = str(int(round(float(mw)))) if mw is not None else "无" + conf_text = f"{float(conf):.4f}" if conf is not None else "无" + method_matched = "命中" if method_name in mass_match_methods else "未命中" + parts.append(f"{method_name}: MW={mw_text}, {method_matched}, 置信度={conf_text}") + + return "; ".join(parts) + + @staticmethod + def _confidence_rank(level: str) -> int: + """ + 功能: + 将置信度文本映射为排序分值. + 优先支持 0-100 分数字符串, 兼容旧的高/中/低文本. + 参数: + level: 置信度文本. + 返回: + int: 排序分值. + """ + level_text = str(level).strip() + try: + numeric_score = float(level_text) + return int(round(numeric_score)) + except ValueError: + pass + + if level == "高": + return 3 + if level == "中": + return 2 + if level == "低": + return 1 + return 0 + + def _build_peak_decision( + self, + row: Dict[str, Any], + match_method: str, + target_name: str, + target_formula: str, + target_mw: Optional[float], + nist_query: NISTLibraryQueryResult, + expected_rt: Optional[float] = None, + ) -> PeakDecision: + """ + 功能: + 对单行峰数据生成结构化判定结果. + 参数: + row: 对照表行. + match_method: 匹配路径标记. + target_name: 目标化合物名称. + target_formula: 目标分子式. + target_mw: 目标分子量. + nist_query: NIST 收录查询结果. + 返回: + PeakDecision: 候选峰判定对象. + """ + hits = self._extract_nist_hits(row) + predictions = self._extract_mass_predictions(row) + matched_methods: List[str] = [] + for method_name, method_data in predictions.items(): + method_mw = method_data.get("mw") + if self._is_mass_match(method_mw, target_mw) is True: + matched_methods.append(method_name) + + best_hit: Optional[NISTHitInfo] = None + nist_target_matched = False + nist_mw_matched = False + nist_formula_matched = False + nist_reference_names = nist_query.reference_names + best_hit_priority = -1 + best_hit_rank = 99 + + for hit in hits: + name_matched = self._is_name_matched(hit.compound_name, target_name, nist_reference_names) + formula_matched = False + if target_formula != "" and hit.formula != "": + formula_matched = (hit.formula == target_formula) + mw_matched = self._is_mass_match(hit.molecular_weight, target_mw) + + if name_matched is True or formula_matched is True: + nist_target_matched = True + if formula_matched is True: + nist_formula_matched = True + if mw_matched is True: + nist_mw_matched = True + + if name_matched is True or formula_matched is True: + current_priority = 4 + elif mw_matched is True and formula_matched is True: + current_priority = 3 + elif mw_matched is True: + current_priority = 2 + else: + current_priority = 1 + + if current_priority > best_hit_priority: + best_hit_priority = current_priority + best_hit_rank = hit.rank + best_hit = hit + elif current_priority == best_hit_priority and hit.rank < best_hit_rank: + best_hit_rank = hit.rank + best_hit = hit + + confidence_score, confidence_reason, confidence_detail = self._build_confidence( + nist_query=nist_query, + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + nist_formula_matched=nist_formula_matched, + mass_match_methods=matched_methods, + prediction_details=predictions, + ) + hit_summary = self._build_hit_summary( + row=row, + expected_rt=expected_rt, + nist_query=nist_query, + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + nist_formula_matched=nist_formula_matched, + confidence_score=confidence_score, + confidence_detail=confidence_detail, + ) + remark = self._build_remark( + nist_query=nist_query, + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + nist_formula_matched=nist_formula_matched, + mass_match_methods=matched_methods, + prediction_details=predictions, + nist_hit=best_hit, + ) + + return PeakDecision( + row=row, + match_method=self._resolve_match_method_label( + base_match_method=match_method, + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + ), + fid_rt=self._parse_opt_float(row.get("FID保留时间(min)")), + fid_area=self._parse_opt_float(row.get("FID峰面积")), + nist_hit=best_hit, + nist_target_matched=nist_target_matched, + nist_mw_matched=nist_mw_matched, + nist_formula_matched=nist_formula_matched, + pim_mw=predictions.get("PIM", {}).get("mw"), + sshm_mw=predictions.get("SS-HM", {}).get("mw"), + ihshm_mw=predictions.get("iHS-HM", {}).get("mw"), + mass_match_methods=matched_methods, + confidence_level=str(int(round(confidence_score))), + confidence_score=confidence_score, + confidence_reason=confidence_reason, + hit_summary=hit_summary, + remark=remark, + ) + + def _sort_peak_decisions( + self, + decisions: List[PeakDecision], + expected_rt: Optional[float] = None, + ) -> List[PeakDecision]: + """ + 功能: + 按综合分数、NIST命中、峰面积与 RT 偏差排序候选峰. + 参数: + decisions: 候选峰列表. + expected_rt: 预期 RT, RT 模式下用于偏差排序. + 返回: + List[PeakDecision]: 排序后的候选峰列表. + """ + def _rt_bias(item: PeakDecision) -> float: + if expected_rt is None: + return 0.0 + if item.fid_rt is None: + return float("-inf") + return -abs(item.fid_rt - expected_rt) + + return sorted( + decisions, + key=lambda item: ( + item.confidence_score, + 1 if item.nist_target_matched is True else 0, + item.fid_area if item.fid_area is not None else -1.0, + _rt_bias(item), + ), + reverse=True, + ) + + def _match_rows_by_rt( + self, + sample_rows: List[Dict], + expected_rt: float, + ) -> List[Dict[str, Any]]: + """ + 功能: + 按保留时间过滤, 返回 RT 容差范围内的全部峰行. + 参数: + sample_rows: 对照表数据行列表. + expected_rt: 预期保留时间(min). + 返回: + List[Dict[str, Any]]: 容差范围内候选峰行列表. + """ + matched_rows: List[Dict[str, Any]] = [] + for row in sample_rows: + row_rt = self._extract_rt_for_row(row) + if row_rt is None: + continue + diff = abs(row_rt - expected_rt) + if diff <= self._rt_tolerance: + matched_rows.append(row) + return matched_rows + + def identify_peaks( + self, + sample_rows: List[Dict], + expected_rt: Optional[float], + target_name: str, + target_formula: str, + target_mw: Optional[float], + nist_query: NISTLibraryQueryResult, + allow_multiple: bool = False, + ) -> List[PeakDecision]: + """ + 功能: + 根据 RT/NIST/分子量预测综合规则识别目标峰. + 规则: + 1. 有 expected_rt 时, 以 RT 匹配为主并输出单峰. + 2. 无 expected_rt 且 NIST 收录时, NIST 命中或分子量预测任一命中即保留. + 3. 无 expected_rt 且 NIST 未收录时, 以分子量预测为主. + 参数: + sample_rows: 样品对照表行列表. + expected_rt: 目标预期保留时间. + target_name: 目标化合物名称. + target_formula: 目标分子式. + target_mw: 目标分子量. + nist_query: NIST 收录查询结果. + allow_multiple: 是否允许返回多个候选峰. + 返回: + List[PeakDecision]: 候选峰判定列表. + """ + decisions: List[PeakDecision] = [] + + if expected_rt is not None: + rt_rows = self._match_rows_by_rt(sample_rows, expected_rt) + if len(rt_rows) == 0: + return decisions + for row in rt_rows: + decision = self._build_peak_decision( + row=row, + match_method="rt", + target_name=target_name, + target_formula=target_formula, + target_mw=target_mw, + nist_query=nist_query, + expected_rt=expected_rt, + ) + decisions.append(decision) + decisions = self._sort_peak_decisions(decisions, expected_rt=expected_rt) + if allow_multiple is False and len(decisions) > 1: + return [decisions[0]] + return decisions + + for row in sample_rows: + decision = self._build_peak_decision( + row=row, + match_method="mass", + target_name=target_name, + target_formula=target_formula, + target_mw=target_mw, + nist_query=nist_query, + expected_rt=None, + ) + + if nist_query.has_record is True: + has_nist_evidence = ( + decision.nist_target_matched is True + or decision.nist_mw_matched is True + ) + else: + has_nist_evidence = ( + decision.nist_mw_matched is True + and decision.nist_formula_matched is True + ) + + has_mass_evidence = (len(decision.mass_match_methods) > 0) + if has_nist_evidence is False and has_mass_evidence is False: + continue + + decisions.append(decision) + + decisions = self._sort_peak_decisions(decisions, expected_rt=None) + if allow_multiple is False and len(decisions) > 1: + return [decisions[0]] + return decisions + + # ------------------------------------------------------------------ + # 产率计算 + # ------------------------------------------------------------------ + + def _calculate_yield( + self, + fid_area_product: float, + fid_area_is: float, + ecn_product: float, + ecn_is: float, + config: YieldCalcConfig, + product_equivalent: float = 1.0, + ) -> Tuple[Optional[float], Optional[float], Optional[float], Optional[float]]: + """ + 功能: + 根据计算方法分发到 ECN/标准曲线/响应因子法, 计算产率. + 参数: + fid_area_product: 产物 FID 峰面积. + fid_area_is: 内标 FID 峰面积. + ecn_product: 产物 ECN. + ecn_is: 内标 ECN. + config: 产率计算配置. + product_equivalent: 目标产物当量(eq), 理论产物量 = 反应规模 * equivalent. + 返回: + Tuple: (area_ratio, molar_ratio, n_product_mol, yield_percent). + 任何环节失败返回 None. + """ + if fid_area_is <= 0: + logger.warning("内标 FID 面积 <= 0, 无法计算面积比") + return (None, None, None, None) + + ratio = fid_area_product / fid_area_is + + method = config.calc_method.strip().upper() + + # ECN 法 + if method == "ECN": + if ecn_product <= 0: + logger.warning("产物 ECN <= 0, 无法计算摩尔比") + return (ratio, None, None, None) + molar_ratio = ratio * (ecn_is / ecn_product) + + # 标准曲线法 + elif method in ("标准曲线", "CALIBRATION", "CURVE"): + if config.curve_slope is None or config.curve_slope == 0: + logger.warning("标准曲线斜率无效, 无法计算") + return (ratio, None, None, None) + intercept = config.curve_intercept if config.curve_intercept is not None else 0.0 + molar_ratio = (ratio - intercept) / config.curve_slope + + # 响应因子法 + elif method in ("响应因子", "RF", "RESPONSE_FACTOR"): + if config.response_factor is None or config.response_factor == 0: + logger.warning("响应因子无效, 无法计算") + return (ratio, None, None, None) + molar_ratio = ratio * config.response_factor + + else: + logger.warning("未知计算方法: '%s'", config.calc_method) + return (ratio, None, None, None) + + # 计算产物物质的量 + if config.is_moles <= 0: + logger.warning("内标摩尔量 <= 0, 无法计算产物物质的量") + return (ratio, molar_ratio, None, None) + + n_product = molar_ratio * config.is_moles + + # 计算产率 + if config.reaction_scale_mmol <= 0: + logger.warning("反应规模 <= 0, 无法计算产率") + return (ratio, molar_ratio, n_product, None) + + n_theoretical = (config.reaction_scale_mmol * product_equivalent) / 1000.0 # mmol * eq → mol + yield_percent = (n_product / n_theoretical) * 100.0 + + return (ratio, molar_ratio, n_product, yield_percent) + + # ------------------------------------------------------------------ + # 主流程 + # ------------------------------------------------------------------ + + def process_task( + self, + plan_path: Path, + report_path: Path, + chemical_list_path: Path, + ) -> Tuple[YieldCalcConfig, List[SampleYieldResult]]: + """ + 功能: + 产率计算完整流程: + 1. 从实验方案和 chemical_list 解析配置. + 2. 从积分报告加载 TIC-FID 对照表数据. + 3. 逐 (样品 x 产物) 计算产率. + 参数: + plan_path: 实验方案 xlsx 路径. + report_path: 积分报告 xlsx 路径. + chemical_list_path: chemical_list.xlsx 路径. + 返回: + Tuple[YieldCalcConfig, List[SampleYieldResult]]: + 配置和所有样品的计算结果列表. + """ + # 1. 解析配置 + config = self.parse_yield_config(plan_path, chemical_list_path) + logger.info( + "产率计算配置: 方法=%s, 内标=%s(%.6f mol), 反应规模=%.2f mmol, 产物数=%d", + config.calc_method, config.is_name, config.is_moles, + config.reaction_scale_mmol, len(config.products), + ) + + # 2. 加载对齐数据 + alignment_data = self.load_alignment_data(report_path) + logger.info("加载了 %d 个样品的 TIC-FID 对照数据", len(alignment_data)) + + # 3. 预先查询 NIST 收录状态 + is_nist_query = self._query_nist_library_by_smiles( + smiles=config.is_smiles, + formula=config.is_formula, + target_mw=config.is_molecular_weight, + ) + config.is_nist_has_record = is_nist_query.has_record + config.is_nist_query_mode = is_nist_query.query_mode + config.is_nist_reference_names = is_nist_query.reference_names + + product_nist_query_map: Dict[int, NISTLibraryQueryResult] = {} + for product in config.products: + product_nist_query = self._query_nist_library_by_smiles( + smiles=product.smiles, + formula=product.formula, + target_mw=product.molecular_weight, + ) + product.nist_has_record = product_nist_query.has_record + product.nist_query_mode = product_nist_query.query_mode + product.nist_reference_names = product_nist_query.reference_names + product_nist_query_map[id(product)] = product_nist_query + logger.info( + "产物 '%s' NIST收录=%s, 查询模式=%s", + product.name, + "是" if product.nist_has_record is True else "否", + product.nist_query_mode, + ) + + # 4. 逐样品计算 + results: List[SampleYieldResult] = [] + for sample_name in sorted(alignment_data.keys()): + sample_rows = alignment_data[sample_name] + exp_num = self._extract_experiment_number(sample_name) + + # 先识别内标峰, 内标始终只取最佳单峰. + is_decisions = self.identify_peaks( + sample_rows=sample_rows, + expected_rt=config.is_expected_rt, + target_name=config.is_name, + target_formula=config.is_formula, + target_mw=config.is_molecular_weight, + nist_query=is_nist_query, + allow_multiple=False, + ) + is_decision = is_decisions[0] if len(is_decisions) > 0 else None + + is_fid_rt: Optional[float] = None + is_fid_area: Optional[float] = None + is_match_compound = "" + if is_decision is not None: + is_fid_rt = is_decision.fid_rt + if is_fid_rt is None: + is_fid_rt = self._extract_rt_for_row(is_decision.row) + is_fid_area = is_decision.fid_area + if is_decision.nist_hit is not None: + is_match_compound = is_decision.nist_hit.compound_name + + # 筛选适用于该实验的目标产物. + for product in config.products: + if ( + len(product.applicable_experiments) > 0 + and exp_num is not None + and exp_num not in product.applicable_experiments + ): + continue + + base_result = SampleYieldResult( + sample_name=sample_name, + product_name=product.name, + is_fid_rt=is_fid_rt, + is_fid_area=is_fid_area, + is_match_compound=is_match_compound, + ecn_product=product.ecn, + ecn_is=config.is_ecn, + ) + + if is_decision is None: + base_result.warnings.append("未检测到内标") + results.append(base_result) + continue + + if is_fid_area is None or is_fid_area <= 0: + base_result.warnings.append("内标FID面积无效") + results.append(base_result) + continue + + product_nist_query = product_nist_query_map.get( + id(product), + NISTLibraryQueryResult(), + ) + prod_decisions = self.identify_peaks( + sample_rows=sample_rows, + expected_rt=product.expected_rt, + target_name=product.name, + target_formula=product.formula, + target_mw=product.molecular_weight, + nist_query=product_nist_query, + allow_multiple=True, + ) + + if len(prod_decisions) == 0: + base_result.remark = "没有目标产物" + if product.expected_rt is not None: + base_result.warnings.append("未检测到目标产物(RT匹配失败)") + elif product_nist_query.has_record is True: + base_result.warnings.append("未检测到满足NIST或分子量条件的目标峰") + else: + base_result.warnings.append("未检测到分子量预测命中的目标峰") + results.append(base_result) + continue + + for prod_decision in prod_decisions: + result = SampleYieldResult( + sample_name=sample_name, + product_name=product.name, + is_fid_rt=is_fid_rt, + is_fid_area=is_fid_area, + is_match_compound=is_match_compound, + ecn_product=product.ecn, + ecn_is=config.is_ecn, + ) + + result.product_fid_rt = prod_decision.fid_rt + if result.product_fid_rt is None: + result.product_fid_rt = self._extract_rt_for_row(prod_decision.row) + result.product_fid_area = prod_decision.fid_area + if prod_decision.nist_hit is not None: + result.product_match_compound = prod_decision.nist_hit.compound_name + result.nist_matched_mw = prod_decision.nist_hit.molecular_weight + result.match_method = prod_decision.match_method + result.confidence_level = prod_decision.confidence_level + result.confidence_score = prod_decision.confidence_score + result.confidence_reason = prod_decision.confidence_reason + result.hit_summary = prod_decision.hit_summary + result.remark = prod_decision.remark + result.pim_predicted_mw = prod_decision.pim_mw + result.sshm_predicted_mw = prod_decision.sshm_mw + result.ihshm_predicted_mw = prod_decision.ihshm_mw + + if result.product_fid_area is None or result.product_fid_area <= 0: + result.warnings.append("产物FID面积无效") + results.append(result) + continue + + ratio, molar_ratio, n_product, yield_pct = self._calculate_yield( + result.product_fid_area, + is_fid_area, + product.ecn, + config.is_ecn, + config, + product_equivalent=product.equivalent, + ) + result.area_ratio = ratio + result.molar_ratio = molar_ratio + result.n_product_mol = n_product + result.yield_percent = yield_pct + results.append(result) + + logger.info("产率计算完成, 共 %d 条结果", len(results)) + return config, results + + # ------------------------------------------------------------------ + # 报告生成 + # ------------------------------------------------------------------ + + def generate_yield_report( + self, + task_id: str, + config: YieldCalcConfig, + results: List[SampleYieldResult], + output_dir: Path, + ) -> Path: + """ + 功能: + 生成产率报告 Excel 文件, 包含 "产率计算结果" 和 "计算参数" 两个 Sheet. + 参数: + task_id: 任务 ID. + config: 产率计算配置. + results: 各样品计算结果列表. + output_dir: 输出目录. + 返回: + Path: 生成的 xlsx 文件路径. + """ + output_dir.mkdir(parents=True, exist_ok=True) + output_path = output_dir / f"{task_id}_yield_report.xlsx" + + wb = openpyxl.Workbook() + + # Sheet 1: 产率计算结果 + ws_result = wb.active + ws_result.title = "产率计算结果" + self._write_yield_sheet(ws_result, results) + + # Sheet 2: 计算参数 + ws_params = wb.create_sheet("计算参数") + self._write_params_sheet(ws_params, config) + + wb.save(str(output_path)) + logger.info("产率报告已保存: %s", output_path) + return output_path + + def _write_yield_sheet( + self, ws, results: List[SampleYieldResult] + ) -> None: + """写入产率计算结果 Sheet.""" + headers = self._build_yield_headers() + col_map = {name: idx for idx, name in enumerate(headers, start=1)} + self._write_header(ws, headers) + + for row_idx, r in enumerate(results, start=2): + ws.cell(row=row_idx, column=col_map["样品名"], value=r.sample_name) + ws.cell(row=row_idx, column=col_map["目标产物"], value=r.product_name) + ws.cell(row=row_idx, column=col_map["产物保留时间(min)"], + value=round(r.product_fid_rt, 3) if r.product_fid_rt is not None else "") + ws.cell(row=row_idx, column=col_map["产物FID面积"], + value=round(r.product_fid_area, 6) if r.product_fid_area is not None else "") + ws.cell(row=row_idx, column=col_map["产物匹配化合物"], value=r.product_match_compound) + ws.cell(row=row_idx, column=col_map["内标保留时间(min)"], + value=round(r.is_fid_rt, 3) if r.is_fid_rt is not None else "") + ws.cell(row=row_idx, column=col_map["内标FID面积"], + value=round(r.is_fid_area, 6) if r.is_fid_area is not None else "") + ws.cell(row=row_idx, column=col_map["内标匹配化合物"], value=r.is_match_compound) + ws.cell(row=row_idx, column=col_map["Ratio"], + value=round(r.area_ratio, 4) if r.area_ratio is not None else "") + ws.cell(row=row_idx, column=col_map["产物ECN"], + value=round(r.ecn_product, 2) if r.ecn_product is not None else "") + ws.cell(row=row_idx, column=col_map["内标ECN"], + value=round(r.ecn_is, 2) if r.ecn_is is not None else "") + if r.yield_percent is not None: + yield_display = "<1" if r.yield_percent < 1 else round(r.yield_percent) + else: + yield_display = "" + ws.cell(row=row_idx, column=col_map["产率(%)"], value=yield_display) + ws.cell(row=row_idx, column=col_map["匹配方式"], value=r.match_method) + ws.cell(row=row_idx, column=col_map["置信度"], value=r.confidence_level) + ws.cell( + row=row_idx, + column=col_map["NIST匹配分子量(Da)"], + value=round(r.nist_matched_mw, 4) if r.nist_matched_mw is not None else "", + ) + + if "PIM预测分子量(Da)" in col_map: + ws.cell( + row=row_idx, + column=col_map["PIM预测分子量(Da)"], + value=round(r.pim_predicted_mw, 4) if r.pim_predicted_mw is not None else "", + ) + if "SS-HM预测分子量(Da)" in col_map: + ws.cell( + row=row_idx, + column=col_map["SS-HM预测分子量(Da)"], + value=round(r.sshm_predicted_mw, 4) if r.sshm_predicted_mw is not None else "", + ) + if "iHS-HM预测分子量(Da)" in col_map: + ws.cell( + row=row_idx, + column=col_map["iHS-HM预测分子量(Da)"], + value=round(r.ihshm_predicted_mw, 4) if r.ihshm_predicted_mw is not None else "", + ) + + # 组装最终备注: remark + 额外警告 + remark_text = r.remark + if len(r.warnings) > 0: + extra = "; ".join(r.warnings) + if remark_text != "": + remark_text = remark_text + "; " + extra + else: + remark_text = extra + ws.cell(row=row_idx, column=col_map["备注"], value=remark_text) + + self._auto_column_width(ws) + + def _write_params_sheet(self, ws, config: YieldCalcConfig) -> None: + """写入计算参数 Sheet, 记录本次计算的完整配置.""" + self._write_header(ws, ["参数", "值"]) + + rows = [ + ("产率计算方法", config.calc_method), + ("反应规模(mmol)", config.reaction_scale_mmol), + ("", ""), + ("── 内标信息 ──", ""), + ("内标名称", config.is_name), + ("内标SMILES", config.is_smiles), + ("内标分子式", config.is_formula), + ( + "内标分子量(Da)", + round(config.is_molecular_weight, 4) if config.is_molecular_weight is not None else "", + ), + ("内标ECN", round(config.is_ecn, 4)), + ("内标NIST收录", "是" if config.is_nist_has_record is True else "否"), + ("内标NIST查询模式", config.is_nist_query_mode), + ("内标用量(μL/mg)", config.is_amount), + ("内标摩尔量(mmol)", round(config.is_moles * 1000, 6)), + ("内标预期RT(min)", config.is_expected_rt if config.is_expected_rt is not None else ""), + ("", ""), + ] + + # 标准曲线/响应因子参数 + if config.curve_slope is not None: + rows.append(("标准曲线斜率", config.curve_slope)) + if config.curve_intercept is not None: + rows.append(("标准曲线截距", config.curve_intercept)) + if config.response_factor is not None: + rows.append(("响应因子", config.response_factor)) + + rows.append(("", "")) + rows.append(("── 目标产物 ──", "")) + + for i, p in enumerate(config.products, start=1): + exp_str = ( + ",".join(str(e) for e in p.applicable_experiments) + if p.applicable_experiments else "all" + ) + rows.append((f"产物{i} 名称", p.name)) + rows.append((f"产物{i} SMILES", p.smiles)) + rows.append((f"产物{i} 分子式", p.formula)) + rows.append((f"产物{i} 分子量(Da)", round(p.molecular_weight, 4) if p.molecular_weight is not None else "")) + rows.append((f"产物{i} ECN", round(p.ecn, 4))) + rows.append((f"产物{i} NIST收录", "是" if p.nist_has_record is True else "否")) + rows.append((f"产物{i} NIST查询模式", p.nist_query_mode)) + rows.append((f"产物{i} 预期RT(min)", p.expected_rt if p.expected_rt is not None else "")) + rows.append((f"产物{i} 适用实验", exp_str)) + rows.append((f"产物{i} 当量(eq)", p.equivalent)) + rows.append(("", "")) + + left_align = Alignment(horizontal="left", vertical="center") + center_align = Alignment(horizontal="center", vertical="center") + for row_idx, (key, value) in enumerate(rows, start=2): + cell_key = ws.cell(row=row_idx, column=1, value=key) + cell_val = ws.cell(row=row_idx, column=2, value=value) + cell_key.alignment = left_align + cell_val.alignment = center_align + + self._auto_column_width(ws) + + # ------------------------------------------------------------------ + # 工具方法 + # ------------------------------------------------------------------ + + def _write_header(self, ws, headers: List[str]) -> None: + """写入表头行并设置样式.""" + for col_idx, header in enumerate(headers, start=1): + cell = ws.cell(row=1, column=col_idx, value=header) + cell.font = self._HEADER_FONT + cell.fill = self._HEADER_FILL + cell.alignment = self._HEADER_ALIGN + + @staticmethod + def _auto_column_width(ws) -> None: + """根据内容自动调整列宽, 所有单元格居中对齐, 备注列列宽加大.""" + center_align = Alignment(horizontal="center", vertical="center") + for col_cells in ws.columns: + max_length = 0 + col_letter = get_column_letter(col_cells[0].column) + header_value = str(col_cells[0].value) if col_cells[0].value is not None else "" + for cell in col_cells: + if cell.value is not None: + val_str = str(cell.value) + char_len = sum(2 if ord(c) > 127 else 1 for c in val_str) + max_length = max(max_length, char_len) + # 居中对齐 (表头行保留原样式, 数据行设置居中) + if cell.row > 1: + cell.alignment = center_align + # 备注列使用更大的列宽上限, 确保容纳完整文本 + if header_value == "备注": + ws.column_dimensions[col_letter].width = min(max_length + 3, 100) + else: + ws.column_dimensions[col_letter].width = min(max_length + 3, 30) + + @staticmethod + def _parse_float(value: Any, default: float = 0.0) -> float: + """安全解析浮点数.""" + if value is None: + return default + try: + return float(value) + except (ValueError, TypeError): + text = str(value).strip() + numbers = re.findall(r"[0-9]+(?:\.[0-9]+)?", text) + if numbers: + return float(numbers[0]) + return default + + @staticmethod + def _parse_opt_float(value: Any) -> Optional[float]: + """安全解析可选浮点数, 无效返回 None.""" + if value is None: + return None + try: + result = float(value) + return result + except (ValueError, TypeError): + text = str(value).strip() + if text == "": + return None + numbers = re.findall(r"[0-9]+(?:\.[0-9]+)?", text) + if numbers: + return float(numbers[0]) + return None + diff --git a/unilabos/devices/eit_analysis_station/scripts/__init__.py b/unilabos/devices/eit_analysis_station/scripts/__init__.py new file mode 100644 index 00000000..b075a837 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/scripts/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""分析站脚本模块.""" diff --git a/unilabos/devices/eit_analysis_station/scripts/build_nist_structure_index.py b/unilabos/devices/eit_analysis_station/scripts/build_nist_structure_index.py new file mode 100644 index 00000000..0e6d8fb0 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/scripts/build_nist_structure_index.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 该脚本已废弃. + 结构映射改为运行时从 MSP/MOL 自动构建, 不再支持提前生成 index.json. +参数: + 无. +返回: + 无. +""" + +import logging +import sys + +logger = logging.getLogger(__name__) + + +def main() -> None: + """ + 功能: + 输出废弃提示并退出. + 参数: + 无. + 返回: + 无. + """ + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(name)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + logger.error("该脚本已废弃, 请直接运行分析流程触发运行时结构映射构建.") + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_analysis_station/scripts/regression_peak_integration.py b/unilabos/devices/eit_analysis_station/scripts/regression_peak_integration.py new file mode 100644 index 00000000..49b4579c --- /dev/null +++ b/unilabos/devices/eit_analysis_station/scripts/regression_peak_integration.py @@ -0,0 +1,933 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +功能: + 对 robust_v3 积分方案执行快速回归检查. + 覆盖合成肩峰场景, 合成真实双峰场景, 以及本地 725/760 样本的 TIC/FID 场景. +参数: + --data-root: 样本根目录, 默认 eit_analysis_station/data. +返回: + 进程退出码, 0 表示通过, 1 表示失败. +""" + +import argparse +import logging +import sys +from pathlib import Path +from typing import Callable, List, Optional + +import numpy as np + +PROJECT_ROOT = Path(__file__).resolve().parents[2] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + +from unilabos.devices.eit_analysis_station.processor.data_reader import GCMSDataReader +from unilabos.devices.eit_analysis_station.processor.peak_integrator import PeakIntegrator, PeakResult + +logger = logging.getLogger(__name__) + + +def _build_integrator( + prominence: float, + min_distance: int, + *, + integration_mode: str = "robust_v3", + shoulder_filter_enable: bool = True, + tail_artifact_filter_enable: bool = True, + tail_monotonic_filter_enable: bool = True, + tail_monotonic_ratio_max: float = 0.25, + max_peak_width_min: float = 0.5, + leading_edge_filter_enable: bool = True, + leading_edge_relative_prominence_max: float = 0.25, + leading_edge_monotonic_ratio_min: float = 0.65, +) -> PeakIntegrator: + """ + 功能: + 构造指定模式的积分器. + 参数: + prominence: 峰检测 prominence. + min_distance: 峰最小间距. + integration_mode: 积分模式. + shoulder_filter_enable: 是否启用肩峰过滤. + tail_artifact_filter_enable: 是否启用三条件拖尾假峰过滤. + tail_monotonic_filter_enable: 是否启用平滑信号单调下降拖尾过滤. + tail_monotonic_ratio_max: 单调下降判定上升步占比上限. + max_peak_width_min: 峰最大边界宽度(min), 超出视为基线抬升假峰, 设0关闭. + leading_edge_filter_enable: 是否启用前沿假峰过滤. + leading_edge_relative_prominence_max: 前沿假峰相对后峰显著性上限. + leading_edge_monotonic_ratio_min: 前沿假峰判定上升步占比下限. + 返回: + PeakIntegrator. + """ + return PeakIntegrator( + smoothing_window=11, + prominence=prominence, + min_distance=min_distance, + integration_mode=integration_mode, + baseline_method="rolling_quantile", + baseline_quantile=20.0, + baseline_window_min=0.9, + boundary_sigma_factor=3.0, + boundary_edge_ratio=0.005, + boundary_expand_factor=6.0, + boundary_min_span_min=0.08, + boundary_max_span_min=2.0, + shoulder_filter_enable=shoulder_filter_enable, + shoulder_filter_width_max_min=0.035, + shoulder_filter_gap_max_min=0.09, + shoulder_filter_relative_prominence_max=0.15, + tail_artifact_filter_enable=tail_artifact_filter_enable, + tail_artifact_gap_max_min=0.20, + tail_artifact_relative_prominence_max=0.15, + tail_artifact_half_width_asymmetry_min=2.0, + tail_monotonic_filter_enable=tail_monotonic_filter_enable, + tail_monotonic_ratio_max=tail_monotonic_ratio_max, + max_peak_width_min=max_peak_width_min, + leading_edge_filter_enable=leading_edge_filter_enable, + leading_edge_relative_prominence_max=leading_edge_relative_prominence_max, + leading_edge_monotonic_ratio_min=leading_edge_monotonic_ratio_min, + ) + + +def _gaussian(times: np.ndarray, center: float, sigma: float, amplitude: float) -> np.ndarray: + """ + 功能: + 生成高斯峰信号. + 参数: + times: 时间数组. + center: 中心时间. + sigma: 峰宽参数. + amplitude: 峰高. + 返回: + np.ndarray. + """ + return amplitude * np.exp(-0.5 * ((times - center) / sigma) ** 2) + + +def _has_peak(peaks: List[PeakResult], target_rt: float, tolerance: float) -> bool: + """ + 功能: + 判断峰列表中是否存在命中目标保留时间的峰. + 参数: + peaks: 峰列表. + target_rt: 目标保留时间. + tolerance: 容差(min). + 返回: + bool, True 表示命中. + """ + for peak in peaks: + if abs(peak.retention_time - target_rt) <= tolerance: + return True + return False + + +def _has_peak_in_window(peaks: List[PeakResult], rt_min: float, rt_max: float) -> bool: + """ + 功能: + 判断峰列表中是否存在落入窗口范围的峰. + 参数: + peaks: 峰列表. + rt_min: 保留时间下限. + rt_max: 保留时间上限. + 返回: + bool, True 表示存在峰. + """ + for peak in peaks: + if rt_min <= peak.retention_time <= rt_max: + return True + return False + + +def _rounded_rts(peaks: List[PeakResult]) -> List[float]: + """ + 功能: + 提取峰列表的保留时间并做固定精度归一化. + 参数: + peaks: 峰列表. + 返回: + List[float], 四舍五入后的保留时间列表. + """ + return [round(item.retention_time, 4) for item in peaks] + + +def _closest_peak(peaks: List[PeakResult], target_rt: float) -> PeakResult: + """ + 功能: + 找到最接近目标保留时间的峰. + 参数: + peaks: 峰列表. + target_rt: 目标保留时间. + 返回: + PeakResult. + """ + return min(peaks, key=lambda item: abs(item.retention_time - target_rt)) + + +def run_synthetic_doublet_regression() -> bool: + """ + 功能: + 验证 robust_v3 不会误伤真实双峰. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(77) + times = np.arange(4.0, 10.0, 0.01) + baseline = ( + 2.8e4 + + 4.2e3 * np.sin((times - 4.0) * 0.9) + + 1.5e4 * np.exp(-0.5 * ((times - 7.0) / 1.2) ** 2) + ) + first_peak = _gaussian(times, center=6.85, sigma=0.006, amplitude=2.8e6) + second_peak = _gaussian(times, center=6.98, sigma=0.012, amplitude=1.5e5) + signal = baseline + first_peak + second_peak + rng.normal(0.0, 1200.0, size=len(times)) + + integrator = _build_integrator(prominence=50000.0, min_distance=5) + peaks = integrator.integrate(times, signal) + if _has_peak(peaks, 6.85, 0.03) is False: + logger.error("合成真双峰回归失败: 未保留 6.85 min 主峰.") + return False + if _has_peak(peaks, 6.98, 0.04) is False: + logger.error("合成真双峰回归失败: 未保留 6.98 min 近邻峰.") + return False + + logger.info("合成真双峰回归通过: peaks=%s", _rounded_rts(peaks)) + return True + + +def run_synthetic_shoulder_regression() -> bool: + """ + 功能: + 验证 robust_v3 可以过滤拖尾肩峰, 且关闭肩峰过滤时退化为 robust_v2. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(123) + times = np.arange(8.0, 9.2, 0.01) + baseline = 2.2e4 + 3.5e3 * np.sin((times - 8.0) * 1.7) + first_peak = _gaussian(times, center=8.66, sigma=0.012, amplitude=3.2e7) + shoulder_peak = _gaussian(times, center=8.74, sigma=0.004, amplitude=4.0e5) + second_peak = _gaussian(times, center=8.83, sigma=0.011, amplitude=5.5e6) + signal = baseline + first_peak + shoulder_peak + second_peak + rng.normal(0.0, 2500.0, size=len(times)) + + peaks_v2 = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v2", + shoulder_filter_enable=False, + ).integrate(times, signal) + peaks_v3 = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + shoulder_filter_enable=True, + ).integrate(times, signal) + peaks_v3_disabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + shoulder_filter_enable=False, + tail_artifact_filter_enable=False, + tail_monotonic_filter_enable=False, + leading_edge_filter_enable=False, + max_peak_width_min=0.0, + ).integrate(times, signal) + + if _has_peak_in_window(peaks_v2, 8.72, 8.77) is False: + logger.error("合成肩峰回归失败: robust_v2 未检出预期肩峰, 当前样本构造失效.") + return False + if _has_peak_in_window(peaks_v3, 8.72, 8.77) is True: + logger.error("合成肩峰回归失败: robust_v3 仍保留 8.72-8.77 min 肩峰.") + return False + if _has_peak(peaks_v3, 8.66, 0.03) is False or _has_peak(peaks_v3, 8.83, 0.03) is False: + logger.error("合成肩峰回归失败: robust_v3 误删了主峰.") + return False + merged_peak = _closest_peak(peaks_v3, 8.66) + if merged_peak.end_time < 8.74: + logger.error( + "合成肩峰回归失败: robust_v3 未将肩峰面积并入前峰. 前峰结束时间=%.4f", + merged_peak.end_time, + ) + return False + if _rounded_rts(peaks_v2) != _rounded_rts(peaks_v3_disabled): + logger.error( + "合成肩峰回归失败: robust_v3 关闭肩峰过滤后未退化为 robust_v2. v2=%s, v3_disabled=%s", + _rounded_rts(peaks_v2), + _rounded_rts(peaks_v3_disabled), + ) + return False + + logger.info( + "合成肩峰回归通过: robust_v2=%s, robust_v3=%s", + _rounded_rts(peaks_v2), + _rounded_rts(peaks_v3), + ) + return True + + +def run_synthetic_tail_artifact_regression() -> bool: + """ + 功能: + 验证 robust_v3 可以过滤强峰拖尾后的单侧假峰. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(7) + times = np.arange(8.0, 9.2, 0.01) + baseline = 1.8e4 + 2.5e3 * np.sin((times - 8.0) * 1.1) + main_peak = _gaussian(times, center=8.67, sigma=0.006, amplitude=1.8e7) + tail_signal = np.where(times > 8.67, 2.5e5 * np.exp(-(times - 8.67) / 0.06), 0.0) + signal = baseline + main_peak + tail_signal + rng.normal(0.0, 1800.0, size=len(times)) + + peaks_disabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + shoulder_filter_enable=True, + tail_artifact_filter_enable=False, + ).integrate(times, signal) + peaks_enabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + shoulder_filter_enable=True, + tail_artifact_filter_enable=True, + ).integrate(times, signal) + + if len(peaks_disabled) < 2 or _has_peak_in_window(peaks_disabled, 8.72, 8.78) is False: + logger.error( + "合成拖尾假峰回归失败: 关闭拖尾假峰过滤时, 未保留预期尾部伪峰. peaks=%s", + _rounded_rts(peaks_disabled), + ) + return False + if len(peaks_enabled) != 1 or _has_peak(peaks_enabled, 8.67, 0.03) is False: + logger.error( + "合成拖尾假峰回归失败: 启用拖尾假峰过滤后, 主峰保留结果异常. peaks=%s", + _rounded_rts(peaks_enabled), + ) + return False + + merged_peak = _closest_peak(peaks_enabled, 8.67) + if merged_peak.end_time < 8.74: + logger.error( + "合成拖尾假峰回归失败: 启用拖尾假峰过滤后, 主峰未吸收尾部面积. end_time=%.4f", + merged_peak.end_time, + ) + return False + + logger.info( + "合成拖尾假峰回归通过: disabled=%s, enabled=%s", + _rounded_rts(peaks_disabled), + _rounded_rts(peaks_enabled), + ) + return True + + +def run_synthetic_monotonic_tail_regression() -> bool: + """ + 功能: + 验证 robust_v3 可以过滤强峰长距离拖尾产生的基线校正假峰. + 模拟 771-5 场景: 大峰 RT 6.10, 单调指数衰减尾部在 ~6.19 产生假峰. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(771) + times = np.arange(5.5, 7.0, 0.01) + # 缓变背景基线 + baseline = 1.5e4 + 2.0e3 * np.sin((times - 5.5) * 0.8) + # 主峰: 强且窄, 中心 6.10 + main_peak = _gaussian(times, center=6.10, sigma=0.008, amplitude=2.3e6) + # 长指数拖尾: 从 6.10 开始衰减, 时间常数 0.08 min + tail_signal = np.where( + times > 6.10, + 3.5e5 * np.exp(-(times - 6.10) / 0.08), + 0.0, + ) + signal = baseline + main_peak + tail_signal + rng.normal(0.0, 1500.0, size=len(times)) + + # 启用全部过滤: 拖尾假峰应被合并 + peaks_enabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + shoulder_filter_enable=True, + tail_artifact_filter_enable=True, + tail_monotonic_filter_enable=True, + ).integrate(times, signal) + + # 关闭全部过滤: 验证假峰确实产生(确认测试构造有效) + peaks_disabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + shoulder_filter_enable=True, + tail_artifact_filter_enable=False, + tail_monotonic_filter_enable=False, + ).integrate(times, signal) + + if _has_peak_in_window(peaks_disabled, 6.15, 6.25) is False: + logger.warning( + "合成单调拖尾回归: 关闭过滤时未检出拖尾区域假峰, 测试构造可能需调整. peaks=%s", + _rounded_rts(peaks_disabled), + ) + + if _has_peak_in_window(peaks_enabled, 6.15, 6.25) is True: + logger.error( + "合成单调拖尾回归失败: 启用过滤后, 拖尾区域仍保留假峰. peaks=%s", + _rounded_rts(peaks_enabled), + ) + return False + + if _has_peak(peaks_enabled, 6.10, 0.03) is False: + logger.error( + "合成单调拖尾回归失败: 主峰 RT=6.10 未保留. peaks=%s", + _rounded_rts(peaks_enabled), + ) + return False + + logger.info( + "合成单调拖尾回归通过: enabled=%s, disabled=%s", + _rounded_rts(peaks_enabled), + _rounded_rts(peaks_disabled), + ) + return True + + +def _load_detector_signal( + reader: GCMSDataReader, + d_dir: Path, + detector: str, +) -> Optional[tuple]: + """ + 功能: + 读取指定检测器的色谱信号. + 参数: + reader: 数据读取器. + d_dir: 样本目录. + detector: 检测器类型, 仅支持 tic/fid. + 返回: + Optional[tuple], 成功时返回(times, intensities), 失败时返回 None. + """ + try: + if detector == "tic": + return reader.read_tic(d_dir) + if detector == "fid": + return reader.read_fid(d_dir) + except Exception as exc: + logger.error("样本 %s %s 读取失败: %s", d_dir.name, detector.upper(), exc) + return None + + logger.error("未知检测器类型: %s", detector) + return None + + +def _run_real_case( + reader: GCMSDataReader, + d_dir: Path, + detector: str, + prominence: float, + min_distance: int, + required_rts: List[float], + forbidden_window: Optional[tuple] = None, +) -> bool: + """ + 功能: + 执行单个真实样本回归检查. + 参数: + reader: 数据读取器. + d_dir: 样本目录. + detector: 检测器类型. + prominence: 峰检测 prominence. + min_distance: 峰最小间距. + required_rts: 必须保留的 RT 列表. + forbidden_window: 禁止出现峰的 RT 窗口. + 返回: + bool, True 表示通过. + """ + signal = _load_detector_signal(reader, d_dir, detector) + if signal is None: + return False + + times, intensities = signal + peaks = _build_integrator( + prominence=prominence, + min_distance=min_distance, + integration_mode="robust_v3", + shoulder_filter_enable=True, + ).integrate(times, intensities) + peaks = [item for item in peaks if 4.0 <= item.retention_time <= 10.0] + if len(peaks) == 0: + logger.error("样本 %s %s 回归失败: 4-10 min 未检出峰.", d_dir.name, detector.upper()) + return False + + for target_rt in required_rts: + if _has_peak(peaks, target_rt, 0.03) is False: + logger.error( + "样本 %s %s 回归失败: 未保留目标峰 RT=%.3f. peaks=%s", + d_dir.name, + detector.upper(), + target_rt, + _rounded_rts(peaks), + ) + return False + + if detector == "tic" and d_dir.name == "760-2.D": + merged_peak = _closest_peak(peaks, 8.6666) + if merged_peak.end_time < 8.76: + logger.error( + "样本 %s %s 回归失败: 8.6666 min 前峰未吸收肩峰面积. end_time=%.4f, peaks=%s", + d_dir.name, + detector.upper(), + merged_peak.end_time, + _rounded_rts(peaks), + ) + return False + + if forbidden_window is not None: + window_min, window_max = forbidden_window + if _has_peak_in_window(peaks, window_min, window_max) is True: + logger.error( + "样本 %s %s 回归失败: 禁止窗口 %.3f-%.3f min 仍存在峰. peaks=%s", + d_dir.name, + detector.upper(), + window_min, + window_max, + _rounded_rts(peaks), + ) + return False + + logger.info("真实样本回归通过: sample=%s, detector=%s, peaks=%s", d_dir.name, detector.upper(), _rounded_rts(peaks)) + return True + + +def run_real_sample_regression(data_root: Path) -> bool: + """ + 功能: + 对 725/760 真实样本执行 TIC 和 FID 回归检查. + 参数: + data_root: 样本根目录. + 返回: + bool, True 表示通过. + """ + reader = GCMSDataReader() + cases = [ + { + "d_dir": data_root / "760" / "760-2.D", + "detector": "tic", + "prominence": 10000.0, + "min_distance": 5, + "required_rts": [8.6666, 8.8273], + "forbidden_window": (8.72, 8.77), + }, + { + "d_dir": data_root / "725" / "725-1.D", + "detector": "tic", + "prominence": 50000.0, + "min_distance": 5, + "required_rts": [6.8492, 6.9797], + "forbidden_window": None, + }, + { + "d_dir": data_root / "760" / "760-2.D", + "detector": "fid", + "prominence": 0.5, + "min_distance": 50, + "required_rts": [6.8403, 6.9683, 8.6597, 8.8140], + "forbidden_window": None, + }, + { + "d_dir": data_root / "725" / "725-1.D", + "detector": "fid", + "prominence": 0.5, + "min_distance": 50, + "required_rts": [6.8427, 6.9723], + "forbidden_window": None, + }, + ] + + all_ok = True + for case in cases: + d_dir = case["d_dir"] + if d_dir.is_dir() is False: + logger.warning("样本目录不存在, 跳过真实样本回归: %s", d_dir) + continue + case_ok = _run_real_case( + reader=reader, + d_dir=d_dir, + detector=case["detector"], + prominence=case["prominence"], + min_distance=case["min_distance"], + required_rts=case["required_rts"], + forbidden_window=case["forbidden_window"], + ) + if case_ok is False: + all_ok = False + + return all_ok + + +def run_robust_v2_compat_smoke(data_root: Path) -> bool: + """ + 功能: + 验证显式选择 robust_v2, 或在 robust_v3 中关闭肩峰过滤时, 行为保持一致. + 参数: + data_root: 样本根目录. + 返回: + bool, True 表示通过. + """ + reader = GCMSDataReader() + cases = [ + (data_root / "760" / "760-2.D", "tic", 10000.0, 5), + (data_root / "760" / "760-2.D", "fid", 0.5, 50), + ] + + all_ok = True + for d_dir, detector, prominence, min_distance in cases: + if d_dir.is_dir() is False: + logger.warning("样本目录不存在, 跳过 robust_v2 兼容烟雾测试: %s", d_dir) + continue + + signal = _load_detector_signal(reader, d_dir, detector) + if signal is None: + all_ok = False + continue + + times, intensities = signal + peaks_v2 = _build_integrator( + prominence=prominence, + min_distance=min_distance, + integration_mode="robust_v2", + shoulder_filter_enable=False, + max_peak_width_min=0.0, + ).integrate(times, intensities) + peaks_v3_disabled = _build_integrator( + prominence=prominence, + min_distance=min_distance, + integration_mode="robust_v3", + shoulder_filter_enable=False, + tail_artifact_filter_enable=False, + tail_monotonic_filter_enable=False, + leading_edge_filter_enable=False, + max_peak_width_min=0.0, + ).integrate(times, intensities) + + if _rounded_rts(peaks_v2) != _rounded_rts(peaks_v3_disabled): + logger.error( + "robust_v2 兼容烟雾测试失败: sample=%s, detector=%s, v2=%s, v3_disabled=%s", + d_dir.name, + detector.upper(), + _rounded_rts(peaks_v2), + _rounded_rts(peaks_v3_disabled), + ) + all_ok = False + continue + + logger.info( + "robust_v2 兼容烟雾测试通过: sample=%s, detector=%s, peaks=%s", + d_dir.name, + detector.upper(), + _rounded_rts(peaks_v2), + ) + + return all_ok + + +def run_synthetic_leading_edge_regression() -> bool: + """ + 功能: + 验证 robust_v3 可以丢弃强峰上升沿(前沿)上的假峰. + 模拟 771-7 场景: 大峰 RT 11.00 的上升沿在 ~10.89 产生假峰. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(7717) + times = np.arange(10.0, 12.0, 0.01) + # 缓变背景基线 + baseline = 2.0e4 + 3.0e3 * np.sin((times - 10.0) * 1.2) + # 主峰: 强且窄, 中心 11.00 + main_peak = _gaussian(times, center=11.00, sigma=0.010, amplitude=2.0e6) + # 前沿小扰动: 在上升沿 10.89 处叠加一个微弱的窄高斯 + leading_bump = _gaussian(times, center=10.89, sigma=0.005, amplitude=6.0e4) + signal = baseline + main_peak + leading_bump + rng.normal(0.0, 1500.0, size=len(times)) + + # 启用前沿过滤: 假峰应被丢弃 + peaks_enabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + leading_edge_filter_enable=True, + ).integrate(times, signal) + + # 关闭前沿过滤: 验证假峰确实被检出(确认测试构造有效) + peaks_disabled = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + leading_edge_filter_enable=False, + ).integrate(times, signal) + + if _has_peak_in_window(peaks_disabled, 10.85, 10.93) is False: + logger.warning( + "合成前沿假峰回归: 关闭过滤时未检出前沿区域假峰, 测试构造可能需调整. peaks=%s", + _rounded_rts(peaks_disabled), + ) + + # 验证启用过滤后前沿假峰被丢弃 + if _has_peak_in_window(peaks_enabled, 10.85, 10.93) is True: + logger.error( + "合成前沿假峰回归失败: 启用过滤后, 前沿区域仍保留假峰. peaks=%s", + _rounded_rts(peaks_enabled), + ) + return False + + # 验证主峰保留 + if _has_peak(peaks_enabled, 11.00, 0.03) is False: + logger.error( + "合成前沿假峰回归失败: 主峰 RT=11.00 未保留. peaks=%s", + _rounded_rts(peaks_enabled), + ) + return False + + logger.info( + "合成前沿假峰回归通过: enabled=%s, disabled=%s", + _rounded_rts(peaks_enabled), + _rounded_rts(peaks_disabled), + ) + return True + + +def run_synthetic_leading_edge_doublet_safety_regression() -> bool: + """ + 功能: + 验证前沿过滤不会误伤真实近邻双峰(两峰 prominence 接近的情况). + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(8888) + times = np.arange(10.0, 12.0, 0.01) + baseline = 2.0e4 + 2.0e3 * np.sin((times - 10.0) * 0.8) + # 两个 prominence 接近的真实峰 + peak_a = _gaussian(times, center=10.80, sigma=0.010, amplitude=1.0e6) + peak_b = _gaussian(times, center=11.00, sigma=0.012, amplitude=1.5e6) + signal = baseline + peak_a + peak_b + rng.normal(0.0, 1500.0, size=len(times)) + + peaks = _build_integrator( + prominence=10000.0, + min_distance=5, + integration_mode="robust_v3", + leading_edge_filter_enable=True, + ).integrate(times, signal) + + if _has_peak(peaks, 10.80, 0.03) is False: + logger.error( + "前沿过滤安全回归失败: RT=10.80 的真实峰被误删. peaks=%s", + _rounded_rts(peaks), + ) + return False + + if _has_peak(peaks, 11.00, 0.03) is False: + logger.error( + "前沿过滤安全回归失败: RT=11.00 的真实峰被误删. peaks=%s", + _rounded_rts(peaks), + ) + return False + + logger.info( + "前沿过滤安全回归通过: peaks=%s", + _rounded_rts(peaks), + ) + return True + + +def run_synthetic_wide_baseline_peak_regression() -> bool: + """ + 功能: + 验证 robust_v3 可以过滤由柱流失基线抬升产生的超宽假峰. + 模拟 771 系列场景: 渐增基线上的宽驼峰被误检为峰. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(7715) + times = np.arange(4.0, 15.0, 0.01) + + # 缓变背景基线 + 后段渐增模拟柱流失 + baseline = 2.0e4 + 5.0e4 * np.exp(0.15 * (times - 4.0)) + # 宽驼峰: 模拟基线抬升残差在 RT 12 附近形成超宽"峰" + wide_hump = 8.0e4 * np.exp(-0.5 * ((times - 12.0) / 0.8) ** 2) + + # 真实窄峰 + peak_612 = _gaussian(times, center=6.12, sigma=0.008, amplitude=5.0e6) + peak_685 = _gaussian(times, center=6.85, sigma=0.007, amplitude=8.0e6) + peak_774 = _gaussian(times, center=7.74, sigma=0.006, amplitude=1.6e7) + + signal = ( + baseline + wide_hump + peak_612 + peak_685 + peak_774 + + rng.normal(0.0, 2000.0, size=len(times)) + ) + + # 启用超宽峰过滤 + peaks_enabled = _build_integrator( + prominence=20000.0, + min_distance=5, + integration_mode="robust_v3", + max_peak_width_min=0.5, + ).integrate(times, signal) + + # 关闭超宽峰过滤 + peaks_disabled = _build_integrator( + prominence=20000.0, + min_distance=5, + integration_mode="robust_v3", + max_peak_width_min=0.0, + ).integrate(times, signal) + + # 验证关闭时超宽峰存在(确认测试构造有效) + wide_peaks_disabled = [p for p in peaks_disabled if p.width > 0.5] + if len(wide_peaks_disabled) == 0: + logger.warning( + "合成超宽基线峰回归: 关闭过滤时未检出超宽峰, 测试构造可能需调整. peaks=%s", + _rounded_rts(peaks_disabled), + ) + + # 验证启用时超宽峰被过滤 + wide_peaks_enabled = [p for p in peaks_enabled if p.width > 0.5] + if len(wide_peaks_enabled) > 0: + logger.error( + "合成超宽基线峰回归失败: 启用过滤后仍存在超宽峰. peaks=%s, widths=%s", + _rounded_rts(peaks_enabled), + [round(p.width, 4) for p in peaks_enabled], + ) + return False + + # 验证真实窄峰保留 + for target_rt in [6.12, 6.85, 7.74]: + if _has_peak(peaks_enabled, target_rt, 0.04) is False: + logger.error( + "合成超宽基线峰回归失败: 真实峰 RT=%.2f 被误删. peaks=%s", + target_rt, _rounded_rts(peaks_enabled), + ) + return False + + logger.info( + "合成超宽基线峰回归通过: enabled=%s, disabled=%s", + _rounded_rts(peaks_enabled), + _rounded_rts(peaks_disabled), + ) + return True + + +def run_synthetic_real_peak_on_elevated_baseline_regression() -> bool: + """ + 功能: + 验证 robust_v3 不会误删位于抬升基线上的真实窄峰. + 模拟 771-8 RT 11.00 场景: 高基线上存在真实的尖锐色谱峰. + 参数: + 无. + 返回: + bool, True 表示通过. + """ + rng = np.random.default_rng(9999) + times = np.arange(8.0, 14.0, 0.01) + + # 渐增基线模拟柱流失 + baseline = 3.0e4 + 5.0e4 * np.exp(0.12 * (times - 8.0)) + + # 真实窄峰: 虽在高基线上但峰本身是尖锐的 + real_peak_1 = _gaussian(times, center=10.0, sigma=0.015, amplitude=8.0e5) + real_peak_2 = _gaussian(times, center=12.0, sigma=0.020, amplitude=1.5e6) + + signal = baseline + real_peak_1 + real_peak_2 + rng.normal(0.0, 2000.0, size=len(times)) + + peaks = _build_integrator( + prominence=20000.0, + min_distance=5, + integration_mode="robust_v3", + max_peak_width_min=0.5, + ).integrate(times, signal) + + if _has_peak(peaks, 10.0, 0.05) is False: + logger.error( + "合成高基线真实峰回归失败: RT=10.0 真实峰被误删. peaks=%s", + _rounded_rts(peaks), + ) + return False + + if _has_peak(peaks, 12.0, 0.05) is False: + logger.error( + "合成高基线真实峰回归失败: RT=12.0 真实峰被误删. peaks=%s", + _rounded_rts(peaks), + ) + return False + + logger.info( + "合成高基线真实峰回归通过: peaks=%s", + _rounded_rts(peaks), + ) + return True + + +def main() -> int: + """ + 功能: + 程序入口. + 参数: + 无. + 返回: + int, 进程退出码. + """ + parser = argparse.ArgumentParser(description="robust_v3 积分回归检查") + parser.add_argument( + "--data-root", + type=Path, + default=PROJECT_ROOT / "eit_analysis_station" / "data", + help="样本根目录路径", + ) + args = parser.parse_args() + + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)s %(name)s - %(message)s", + ) + + checks: List[Callable[[], bool]] = [ + run_synthetic_doublet_regression, + run_synthetic_shoulder_regression, + run_synthetic_tail_artifact_regression, + run_synthetic_monotonic_tail_regression, + run_synthetic_leading_edge_regression, + run_synthetic_leading_edge_doublet_safety_regression, + run_synthetic_wide_baseline_peak_regression, + run_synthetic_real_peak_on_elevated_baseline_regression, + lambda: run_real_sample_regression(args.data_root), + lambda: run_robust_v2_compat_smoke(args.data_root), + ] + + all_ok = True + for check in checks: + if check() is False: + all_ok = False + + if all_ok is True: + logger.info("回归检查全部通过.") + return 0 + + logger.error("回归检查失败.") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/unilabos/devices/eit_analysis_station/sheet/gc_ms.csv b/unilabos/devices/eit_analysis_station/sheet/gc_ms.csv new file mode 100644 index 00000000..5e6452e9 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/sheet/gc_ms.csv @@ -0,0 +1,2 @@ +SampleName,AcqMethod,RackCode,VialPos,SmplInjVol,OutputFile +Sample001,/ChromeleonLocal/�豸�IJļ���������/20250604/20231113.seq/20250604-test,Rack 1,1,1,/ChromeleonLocal/�豸�IJļ���������/20250604 diff --git a/unilabos/devices/eit_analysis_station/sheet/hplc.csv b/unilabos/devices/eit_analysis_station/sheet/hplc.csv new file mode 100644 index 00000000..fcfb5e43 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/sheet/hplc.csv @@ -0,0 +1 @@ +SampleName,AcqMethod,RackCode,VialPos,SmplInjVol,OutputFile diff --git a/unilabos/devices/eit_analysis_station/sheet/uplc_qtof.csv b/unilabos/devices/eit_analysis_station/sheet/uplc_qtof.csv new file mode 100644 index 00000000..fcfb5e43 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/sheet/uplc_qtof.csv @@ -0,0 +1 @@ +SampleName,AcqMethod,RackCode,VialPos,SmplInjVol,OutputFile diff --git a/unilabos/devices/eit_analysis_station/uplc_qtof.csv b/unilabos/devices/eit_analysis_station/uplc_qtof.csv new file mode 100644 index 00000000..04355877 --- /dev/null +++ b/unilabos/devices/eit_analysis_station/uplc_qtof.csv @@ -0,0 +1,2 @@ +SampleName,AcqMethod,RackCode,VialPos,SmplInjVol,OutputFile +test-20260228-3,Generic_15min,Rack 6,1,2,test-20260228-3 \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/.gitignore b/unilabos/devices/eit_synthesis_station/chem_tools/.gitignore new file mode 100644 index 00000000..996f9d52 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/.gitignore @@ -0,0 +1,4 @@ +data/chemicalbook_cache/ +data/chemicalbook_raw/ +data/chemicalbook_records/ +__pycache__/ diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/__init__.py b/unilabos/devices/eit_synthesis_station/chem_tools/__init__.py new file mode 100644 index 00000000..e6191b0b --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" +功能: + 化合物查询与 ChemicalBook 抓取子包. + 统一承载化合物追加链路的查询, 抓取, 缓存与 sidecar 落盘能力. +参数: + 无. +返回: + 无. +""" + +__all__ = [ + "chemical_append_utils", + "chemical_lookup", + "chemicalbook_scraper", + "fetch_chemicalbook", + "storage", +] diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/chemical_append_utils.py b/unilabos/devices/eit_synthesis_station/chem_tools/chemical_append_utils.py new file mode 100644 index 00000000..fec61411 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/chemical_append_utils.py @@ -0,0 +1,550 @@ +# -*- coding: utf-8 -*- +""" +功能: + 提供化学品查询结果追加入库所需的轻量工具函数. + 包括 ChemicalBook 结构化结果适配, 物态推断, 重复检查规则生成, + 以及全量 sidecar JSON 落盘. +参数: + 无. +返回: + 无. +""" + +import json +import logging +from pathlib import Path +from typing import Any, Dict, List, Optional, Sequence, Tuple + +from . import storage + + +logger = logging.getLogger("ChemicalAppendUtils") + + +def _first_non_empty(*values: Any) -> str: + """ + 功能: + 从多个候选值中返回第一个非空字符串. + 参数: + values: 任意数量的候选值. + 返回: + str, 第一个非空字符串. 若都为空则返回空字符串. + """ + for value in values: + value_text = str(value or "").strip() + if value_text != "": + return value_text + return "" + + +def _first_non_none(*values: Any) -> Any: + """ + 功能: + 从多个候选值中返回第一个非 None 的值. + 参数: + values: 任意数量的候选值. + 返回: + Any, 第一个非 None 的值. 若都为空则返回 None. + """ + for value in values: + if value is not None: + return value + return None + + +def get_measurement_value(normalized: Optional[Dict[str, Any]], field_name: str) -> Optional[float]: + """ + 功能: + 从 ChemicalBook normalized 结果中提取数值型测量字段. + 参数: + normalized: Optional[Dict[str, Any]], ChemicalBook normalized 字段. + field_name: str, 目标字段名, 如 density 或 boiling_point. + 返回: + Optional[float], 成功解析出的数值. 不存在时返回 None. + """ + if isinstance(normalized, dict) is False: + return None + + field_value = normalized.get(field_name) + if isinstance(field_value, dict) is False: + return None + + numeric_value = field_value.get("value") + if numeric_value is None: + return None + + try: + return float(numeric_value) + except (TypeError, ValueError): + logger.warning("ChemicalBook 字段无法转换为浮点数: field=%s, value=%s", field_name, numeric_value) + return None + + +def infer_physical_state( + existing_state: Optional[str], + melting_point: Optional[float], + boiling_point: Optional[float], +) -> str: + """ + 功能: + 根据现有状态和温度数据推断 25 摄氏度下的物态. + 参数: + existing_state: Optional[str], 已有物态值. + melting_point: Optional[float], 熔点, 单位为摄氏度. + boiling_point: Optional[float], 沸点, 单位为摄氏度. + 返回: + str, solid, liquid, gas 或空字符串. + """ + normalized_state = str(existing_state or "").strip().lower() + if normalized_state != "": + return normalized_state + + if melting_point is not None and melting_point > 25.0: + return "solid" + if boiling_point is not None and boiling_point <= 25.0: + return "gas" + if melting_point is not None or boiling_point is not None: + return "liquid" + return "" + + +def join_aliases(alias_values: Optional[Sequence[Any]]) -> str: + """ + 功能: + 将别名列表去重后拼接为适合写入 Excel 的字符串. + 参数: + alias_values: Optional[Sequence[Any]], ChemicalBook 别名列表. + 返回: + str, 以 ; 分隔的别名文本. 无有效别名时返回空字符串. + """ + if isinstance(alias_values, Sequence) is False or isinstance(alias_values, (str, bytes)): + return "" + + deduplicated_aliases: List[str] = [] + seen_aliases = set() + for alias_value in alias_values: + alias_text = str(alias_value or "").strip() + if alias_text == "": + continue + if alias_text in seen_aliases: + continue + seen_aliases.add(alias_text) + deduplicated_aliases.append(alias_text) + + return "; ".join(deduplicated_aliases) + + +def _format_numeric_text(value: Any) -> str: + """ + 功能: + 将数值格式化为适合写入展示名的紧凑文本. + 参数: + value: Any, 原始数值. + 返回: + str, 去除多余尾零后的文本. + """ + try: + numeric_value = float(value) + except (TypeError, ValueError): + return str(value or "").strip() + return format(numeric_value, "g") + + +def build_prepared_display_name( + base_row_data: Dict[str, Any], + prepared_form: str, + active_content: Any, + *, + solvent_name: str = "", +) -> str: + """ + 功能: + 根据母体化合物信息构造溶液或 beads 条目的展示名称. + 参数: + base_row_data: Dict[str, Any], 母体化合物行数据. + prepared_form: str, 派生形态, 支持 solution 或 beads. + active_content: Any, solution 时表示 mol/L, beads 时表示 wt%. + solvent_name: str, solution 使用的溶剂名称. + 返回: + str, 派生条目展示名称. + 异常: + ValueError: 形态非法, 或缺少母体名称, 或 solution 缺少溶剂名时抛出. + """ + normalized_form = str(prepared_form or "").strip().lower() + base_name = _first_non_empty(base_row_data.get("base_substance")) + if base_name == "": + substance_text = _first_non_empty( + base_row_data.get("substance"), + base_row_data.get("substance_chinese_name"), + ) + if " (" in substance_text: + substance_text = substance_text.split(" (", 1)[0].strip() + base_name = _first_non_empty( + substance_text, + base_row_data.get("substance_english_name"), + ) + if base_name == "": + raise ValueError("母体化合物缺少名称, 无法生成派生条目名称") + + active_text = _format_numeric_text(active_content) + if normalized_form == "solution": + normalized_solvent_name = str(solvent_name or "").strip() + if normalized_solvent_name == "": + raise ValueError("溶液条目缺少溶剂名称") + return f"{base_name} (溶液, {active_text} M in {normalized_solvent_name})" + if normalized_form == "beads": + return f"{base_name} (beads, {active_text}%)" + + raise ValueError(f"不支持的派生形态: {prepared_form}") + + +def build_prepared_chemical_row_data( + base_row_data: Dict[str, Any], + prepared_form: str, + active_content: Any, + *, + solvent_name: str = "", +) -> Dict[str, Any]: + """ + 功能: + 基于母体化合物行数据构造溶液或 beads 的追加入库字段. + 参数: + base_row_data: Dict[str, Any], 母体化合物行数据. + prepared_form: str, 派生形态, 支持 solution 或 beads. + active_content: Any, solution 时表示 mol/L, beads 时表示 wt%. + solvent_name: str, solution 使用的溶剂名称. + 返回: + Dict[str, Any], 可直接用于 Excel 追加的派生条目字段. + 异常: + ValueError: 形态非法, active_content 无法转浮点, 或必要字段缺失时抛出. + """ + normalized_form = str(prepared_form or "").strip().lower() + if normalized_form not in {"solution", "beads"}: + raise ValueError(f"不支持的派生形态: {prepared_form}") + + try: + numeric_active_content = float(active_content) + except (TypeError, ValueError) as exc: + raise ValueError("active_content 必须为数值") from exc + + display_name = build_prepared_display_name( + base_row_data=base_row_data, + prepared_form=normalized_form, + active_content=numeric_active_content, + solvent_name=solvent_name, + ) + + derived_density = base_row_data.get("density (g/mL)") + if normalized_form == "beads": + derived_density = "" + + return { + "cas_number": str(base_row_data.get("cas_number") or "").strip(), + "chemical_id": "", + "substance_english_name": str(base_row_data.get("substance_english_name") or "").strip(), + "substance": display_name, + "substance_chinese_name": display_name, + "other_name": "", + "brand": "", + "package_size": "", + "storage_location": "", + "molecular_weight": base_row_data.get("molecular_weight"), + "density (g/mL)": derived_density, + "physical_state": "liquid" if normalized_form == "solution" else "solid", + "physical_form": normalized_form, + "active_content(mol/L or wt%)": numeric_active_content, + } + + +def build_append_row_data( + query: str, + lookup_info: Optional[Any], + chemicalbook_record: Optional[Dict[str, Any]], + legacy_chemicalbook_info: Optional[Any] = None, +) -> Dict[str, Any]: + """ + 功能: + 按约定优先级合并多源查询结果, 生成 Excel 追加行所需字段. + Excel 主表仅保留核心字段, ChemicalBook 的全量结构化结果单独落盘 sidecar. + 参数: + query: str, 用户输入的 CAS 或名称. + lookup_info: Optional[Any], lookup_chemical 返回对象. + chemicalbook_record: Optional[Dict[str, Any]], fetch_chemicalbook_by_cas 返回结果. + 返回: + Dict[str, Any], 追加入库所需字段映射. + """ + return _build_append_row_data( + query=query, + lookup_info=lookup_info, + chemicalbook_record=chemicalbook_record, + legacy_chemicalbook_info=legacy_chemicalbook_info, + allow_query_as_cas_fallback=True, + ) + + +def build_append_row_data_for_smiles( + lookup_info: Optional[Any], + chemicalbook_record: Optional[Dict[str, Any]], + legacy_chemicalbook_info: Optional[Any] = None, +) -> Dict[str, Any]: + """ + 功能: + 为 SMILES 在线查询流程构建 Excel 追加行字段. + 与通用构造逻辑的区别是: 不允许将原始查询文本回填到 cas_number. + 参数: + lookup_info: Optional[Any], lookup_chemical_by_smiles 返回对象. + chemicalbook_record: Optional[Dict[str, Any]], fetch_chemicalbook_by_cas 返回结果. + 返回: + Dict[str, Any], 追加入库所需字段映射. + """ + return _build_append_row_data( + query="", + lookup_info=lookup_info, + chemicalbook_record=chemicalbook_record, + legacy_chemicalbook_info=legacy_chemicalbook_info, + allow_query_as_cas_fallback=False, + ) + + +def _build_append_row_data( + query: str, + lookup_info: Optional[Any], + chemicalbook_record: Optional[Dict[str, Any]], + legacy_chemicalbook_info: Optional[Any], + allow_query_as_cas_fallback: bool, +) -> Dict[str, Any]: + """ + 功能: + 合并多源查询结果并生成 Excel 追加行字段. + 可按调用场景决定是否允许将原始查询值作为 CAS 兜底写入. + 参数: + query: str, 原始查询文本. + lookup_info: Optional[Any], 查询结果对象. + chemicalbook_record: Optional[Dict[str, Any]], ChemicalBook 结构化结果. + allow_query_as_cas_fallback: bool, 是否允许用 query 回填 cas_number. + 返回: + Dict[str, Any], 追加入库所需字段映射. + """ + normalized = {} + if isinstance(chemicalbook_record, dict) is True and isinstance(chemicalbook_record.get("normalized"), dict) is True: + normalized = chemicalbook_record["normalized"] + + use_legacy_fallback = needs_legacy_chemicalbook_fallback(chemicalbook_record) + legacy_cn_name = None + legacy_en_name = None + legacy_density = None + legacy_melting_point = None + if use_legacy_fallback is True and legacy_chemicalbook_info is not None: + legacy_cn_name = getattr(legacy_chemicalbook_info, "substance", None) + legacy_en_name = getattr(legacy_chemicalbook_info, "substance_english_name", None) + legacy_density = getattr(legacy_chemicalbook_info, "density", None) + legacy_melting_point = getattr(legacy_chemicalbook_info, "melting_point", None) + + lookup_cas = getattr(lookup_info, "cas_number", None) + lookup_en_name = getattr(lookup_info, "substance_english_name", None) + lookup_cn_name = getattr(lookup_info, "substance", None) + lookup_molecular_weight = getattr(lookup_info, "molecular_weight", None) + lookup_density = getattr(lookup_info, "density", None) + lookup_melting_point = getattr(lookup_info, "melting_point", None) + lookup_state = getattr(lookup_info, "physical_state", None) + + chemicalbook_cas = "" + if isinstance(chemicalbook_record, dict) is True: + chemicalbook_cas = str(chemicalbook_record.get("cas") or "").strip() + + cas_values = [lookup_cas, chemicalbook_cas] + if allow_query_as_cas_fallback is True: + cas_values.append(query) + cas_number = _first_non_empty(*cas_values) + substance_english_name = _first_non_empty( + lookup_en_name, + normalized.get("en_name"), + legacy_en_name, + ) + substance_chinese_name = _first_non_empty( + lookup_cn_name, + normalized.get("cn_name"), + legacy_cn_name, + ) + molecular_weight = _first_non_none( + lookup_molecular_weight, + normalized.get("molecular_weight"), + ) + density_value = _first_non_none( + lookup_density, + get_measurement_value(normalized, "density"), + legacy_density, + ) + melting_point_value = _first_non_none( + lookup_melting_point, + get_measurement_value(normalized, "melting_point"), + legacy_melting_point, + ) + boiling_point_value = get_measurement_value(normalized, "boiling_point") + physical_state = infer_physical_state( + existing_state=lookup_state, + melting_point=melting_point_value, + boiling_point=boiling_point_value, + ) + # 主表保持精简, 别名不直接回填到 other_name. + other_name = "" + + return { + "cas_number": cas_number, + "chemical_id": "", + "substance_english_name": substance_english_name, + "substance": substance_chinese_name, + "substance_chinese_name": substance_chinese_name, + "other_name": other_name, + "brand": "", + "package_size": "", + "storage_location": "", + "molecular_weight": molecular_weight, + "density (g/mL)": density_value, + "physical_state": physical_state, + "physical_form": "neat", + "active_content(mol/L or wt%)": "", + } + + +def needs_legacy_chemicalbook_fallback(record: Optional[Dict[str, Any]]) -> bool: + """ + 功能: + 判断当前 ChemicalBook 结构化结果是否缺少追加主表所需的核心字段. + 当前仅检查中文名, 密度和熔点, 缺任一项即认为需要旧兜底结果. + 参数: + record: Optional[Dict[str, Any]], ChemicalBook 结构化结果. + 返回: + bool, True 表示需要旧 ChemicalBook 兜底. + """ + if isinstance(record, dict) is False: + return True + + normalized = record.get("normalized") + if isinstance(normalized, dict) is False: + return True + + cn_name = str(normalized.get("cn_name") or "").strip() + density_value = get_measurement_value(normalized, "density") + melting_point_value = get_measurement_value(normalized, "melting_point") + + return any([ + cn_name == "", + density_value is None, + melting_point_value is None, + ]) + + +def collect_missing_append_headers(header_map: Dict[str, int]) -> List[str]: + """ + 功能: + 检查化学品追加流程依赖的表头是否存在. + 中文名列兼容 substance 与 substance_chinese_name 两种命名. + 参数: + header_map: Dict[str, int], Excel 表头映射. + 返回: + List[str], 缺失的字段名列表. + """ + missing_headers: List[str] = [] + required_headers = [ + "cas_number", + "substance_english_name", + "molecular_weight", + "density (g/mL)", + "physical_state", + "physical_form", + ] + + for header_name in required_headers: + if header_name not in header_map: + missing_headers.append(header_name) + + if "substance" not in header_map and "substance_chinese_name" not in header_map: + missing_headers.append("substance/substance_chinese_name") + + return missing_headers + + +def build_duplicate_check_specs(row_data: Dict[str, Any]) -> List[Tuple[Tuple[str, ...], str, str]]: + """ + 功能: + 生成按优先级排列的重复检查规则. + neat 条目按 CAS > 英文名 > 中文名检查. + solution 与 beads 仅按完整展示名检查, 允许与母体共享 CAS 与英文名. + 参数: + row_data: Dict[str, Any], 准备写入 Excel 的行数据. + 返回: + List[Tuple[Tuple[str, ...], str, str]], 每项包含候选列名集合, 目标值, 日志标签. + """ + duplicate_specs: List[Tuple[Tuple[str, ...], str, str]] = [] + physical_form = str(row_data.get("physical_form") or "").strip().lower() + is_prepared_form = physical_form in {"solution", "beads"} + + if is_prepared_form is False: + cas_number = str(row_data.get("cas_number") or "").strip() + if cas_number != "": + duplicate_specs.append((("cas_number",), cas_number, "CAS")) + + substance_english_name = str(row_data.get("substance_english_name") or "").strip() + if substance_english_name != "": + duplicate_specs.append((("substance_english_name",), substance_english_name, "英文名")) + + substance_chinese_name = str( + row_data.get("substance") + or row_data.get("substance_chinese_name") + or "" + ).strip() + if substance_chinese_name != "": + duplicate_specs.append((("substance", "substance_chinese_name"), substance_chinese_name, "中文名")) + + return duplicate_specs + + +def get_excel_write_value(row_data: Dict[str, Any], column_name: str) -> Any: + """ + 功能: + 根据目标表头名称, 返回适合写入 Excel 的字段值. + 该函数负责 substance 与 substance_chinese_name 两种列名的兼容. + 参数: + row_data: Dict[str, Any], 追加行字段映射. + column_name: str, Excel 表头列名. + 返回: + Any, 对应列应写入的值. + """ + if column_name == "substance": + return row_data.get("substance") or row_data.get("substance_chinese_name") + if column_name == "substance_chinese_name": + return row_data.get("substance_chinese_name") or row_data.get("substance") + return row_data.get(column_name) + + +def save_chemicalbook_record( + record: Optional[Dict[str, Any]], + output_root: Optional[Path] = None, +) -> str: + """ + 功能: + 将 ChemicalBook 全量结构化结果保存为 sidecar JSON 文件. + 参数: + record: Optional[Dict[str, Any]], fetch_chemicalbook_by_cas 返回结果. + output_root: Optional[Path], 自定义输出目录. None 时使用默认目录. + 返回: + str, 保存后的 JSON 文件路径. 无法保存时返回空字符串. + """ + if isinstance(record, dict) is False: + return "" + + cas_number = str(record.get("cas") or "").strip() + if cas_number == "": + return "" + + if output_root is None: + storage.ensure_chemicalbook_data_layout() + target_root = output_root or storage.CHEMICALBOOK_RECORD_ROOT + target_root.mkdir(parents=True, exist_ok=True) + output_path = target_root / f"{cas_number}.json" + output_path.write_text( + json.dumps(record, ensure_ascii=False, indent=2), + encoding="utf-8", + ) + return str(output_path) diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/chemical_lookup.py b/unilabos/devices/eit_synthesis_station/chem_tools/chemical_lookup.py new file mode 100644 index 00000000..e49a92d4 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/chemical_lookup.py @@ -0,0 +1,961 @@ +# -*- coding: utf-8 -*- +""" +化合物在线查询模块 + +功能: + 根据 CAS 号或中英文名称, 从 PubChem 和 Common Chemistry 查询化合物信息, + 包括 CAS 号, 英文名, 分子量, 密度, 熔点和物态. + 中文名由外部 chemicalbook_scraper 模块独立获取. +""" + +import re +import logging +import json +from dataclasses import dataclass, asdict +from typing import Dict, List, Optional, Tuple +from urllib.parse import quote + +import requests + +from . import chemicalbook_scraper +from .chemical_append_utils import get_measurement_value + +logger = logging.getLogger("ChemicalLookup") + +# ===================== 数据模型 ===================== + +@dataclass +class ChemicalInfo: + """ + 功能: + 化合物在线查询结果的统一数据容器. + 各数据源写入对应字段, 后续按优先级合并. + """ + cas_number: Optional[str] = None + substance_english_name: Optional[str] = None + substance: Optional[str] = None # 中文名 + molecular_weight: Optional[float] = None + density: Optional[float] = None # g/mL + melting_point: Optional[float] = None # celsius, 用于推断 physical_state + physical_state: Optional[str] = None # solid / liquid + + def to_dict(self) -> Dict: + """ + 功能: + 转换为字典, 过滤 None 和 melting_point(中间字段). + 返回: + Dict, 仅包含非 None 的字段(不含 melting_point). + """ + result = {} + for key, val in asdict(self).items(): + if key == "melting_point": + continue # 内部字段, 不对外暴露 + if val is not None: + result[key] = val + return result + + +# ===================== 常量 ===================== + +CAS_PATTERN = re.compile(r"^\d{2,7}-\d{2}-\d$") + +# PubChem API 基地址 +_PUBCHEM_BASE = "https://pubchem.ncbi.nlm.nih.gov/rest/pug" +_PUBCHEM_VIEW_BASE = "https://pubchem.ncbi.nlm.nih.gov/rest/pug_view" + +# Common Chemistry API 基地址 +_COMMON_CHEM_BASE = "https://commonchemistry.cas.org/api" + +# 通用浏览器请求头, 含 Client Hints 和 Sec-Fetch 头以绕过反爬 +_BROWSER_HEADERS = { + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" + ), + "Accept": ( + "text/html,application/xhtml+xml,application/xml;q=0.9," + "image/avif,image/webp,image/apng,*/*;q=0.8" + ), + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Sec-Ch-Ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', + "Sec-Ch-Ua-Mobile": "?0", + "Sec-Ch-Ua-Platform": '"Windows"', + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", +} + +# 密度解析正则 +_DENSITY_WITH_UNIT_RE = re.compile( + r"(\d+\.?\d*)\s*g\s*/\s*(?:mL|cm[³3]|cu\s*cm)", + re.IGNORECASE, +) +_DENSITY_LEADING_FLOAT_RE = re.compile(r"^(\d+\.?\d*)") +_DENSITY_RELATIVE_RE = re.compile( + r"(?:relative\s+density|specific\s+gravity)[^:]*:\s*(\d+\.?\d*)", + re.IGNORECASE, +) +# 比重范围, 如 "0.80872~0.81601" +_DENSITY_RANGE_RE = re.compile( + r"(\d+\.?\d*)\s*[~~–\-]\s*(\d+\.?\d*)", +) + +# 熔点解析正则 +_MP_CELSIUS_RE = re.compile(r"(-?\d+\.?\d*)\s*°?\s*C(?:\b|[^a-zA-Z])", re.IGNORECASE) +_MP_FAHRENHEIT_RE = re.compile(r"(-?\d+\.?\d*)\s*°?\s*F(?:\b|[^a-zA-Z])", re.IGNORECASE) +# 范围格式: "138-140", "101-104 °C", "134-136°C" +_MP_RANGE_RE = re.compile( + r"(-?\d+\.?\d*)\s*[~~–\-]\s*(-?\d+\.?\d*)\s*°?\s*C?", + re.IGNORECASE, +) + +# 密度过滤关键词 (出现则跳过该条目) +_DENSITY_SKIP_KEYWORDS = ["enthalpy", "latent heat"] + + +# ===================== 工具函数 ===================== + +def is_cas_number(query: str) -> bool: + """ + 功能: + 判断输入字符串是否为合法的 CAS 号格式. + 参数: + query: str, 用户输入的查询字符串. + 返回: + bool, True 表示匹配 CAS 号格式. + """ + return CAS_PATTERN.match(query.strip()) is not None + + +def _fahrenheit_to_celsius(f: float) -> float: + """华氏度转摄氏度""" + return round((f - 32) * 5 / 9, 2) + + +def _extract_cas_from_synonyms(synonyms: List[str]) -> Optional[str]: + """ + 功能: + 从 PubChem 同义词列表中提取 CAS 号. + CAS 号格式: 2~7位数字-2位数字-1位数字. + 参数: + synonyms: List[str], PubChem 返回的同义词列表. + 返回: + Optional[str], 找到的第一个 CAS 号, 未找到返回 None. + """ + for name in synonyms: + name = name.strip() + if CAS_PATTERN.match(name): + return name + return None + + +# ===================== PubChem 查询 ===================== + +def _pubchem_get_cid(query: str, timeout: float = 15.0) -> Optional[int]: + """ + 功能: + 通过名称或 CAS 号查询 PubChem 获取化合物 CID. + 参数: + query: str, CAS 号或化合物名称. + timeout: float, 请求超时秒数. + 返回: + Optional[int], 化合物 CID, 查询失败返回 None. + """ + url = f"{_PUBCHEM_BASE}/compound/name/{requests.utils.quote(query)}/cids/JSON" + try: + resp = requests.get(url, timeout=timeout) + if resp.status_code != 200: + logger.debug("PubChem CID 查询失败: status=%s, query=%s", resp.status_code, query) + return None + data = resp.json() + cids = data.get("IdentifierList", {}).get("CID", []) + if cids: + return cids[0] + except (requests.RequestException, json.JSONDecodeError, ValueError) as exc: + logger.warning("PubChem CID 查询异常: %s", exc) + return None + + +def _pubchem_get_cid_by_smiles(smiles: str, timeout: float = 15.0) -> Optional[int]: + """ + 功能: + 通过 SMILES 查询 PubChem 获取化合物 CID. + SMILES 作为 URL 路径参数时需强制编码特殊字符, 以兼容立体化学斜杠等符号. + 参数: + smiles: str, 单个完整 SMILES 结构式. + timeout: float, 请求超时秒数. + 返回: + Optional[int], 化合物 CID, 查询失败或返回无效 CID 时返回 None. + """ + normalized_smiles = str(smiles or "").strip() + if normalized_smiles == "": + return None + + encoded_smiles = quote(normalized_smiles, safe="") + url = f"{_PUBCHEM_BASE}/compound/smiles/{encoded_smiles}/cids/JSON" + try: + resp = requests.get(url, timeout=timeout) + if resp.status_code != 200: + logger.debug("PubChem SMILES CID 查询失败: status=%s, smiles=%s", resp.status_code, normalized_smiles) + return None + + data = resp.json() + cids = data.get("IdentifierList", {}).get("CID", []) + if len(cids) == 0: + return None + + cid = cids[0] + if isinstance(cid, int) is True and cid > 0: + return cid + + logger.info("PubChem SMILES 查询返回无效 CID: smiles=%s, cid=%s", normalized_smiles, cid) + except (requests.RequestException, json.JSONDecodeError, ValueError) as exc: + logger.warning("PubChem SMILES CID 查询异常: %s", exc) + return None + + +def _pubchem_get_properties(cid: int, timeout: float = 15.0) -> Tuple[Optional[str], Optional[float]]: + """ + 功能: + 根据 CID 获取化合物的 IUPAC 名称和分子量. + 参数: + cid: int, PubChem CID. + timeout: float, 请求超时秒数. + 返回: + (iupac_name, molecular_weight), 各为 Optional. + """ + url = f"{_PUBCHEM_BASE}/compound/cid/{cid}/property/MolecularWeight,IUPACName/JSON" + try: + resp = requests.get(url, timeout=timeout) + if resp.status_code != 200: + return None, None + data = resp.json() + props = data.get("PropertyTable", {}).get("Properties", [{}])[0] + iupac = props.get("IUPACName") + mw_str = props.get("MolecularWeight") + mw = float(mw_str) if mw_str is not None else None + return iupac, mw + except (requests.RequestException, json.JSONDecodeError, ValueError, IndexError) as exc: + logger.warning("PubChem 属性查询异常: %s", exc) + return None, None + + +def _pubchem_get_synonyms(cid: int, timeout: float = 15.0) -> List[str]: + """ + 功能: + 根据 CID 获取化合物的所有同义词列表 (含 CAS 号). + 参数: + cid: int, PubChem CID. + timeout: float, 请求超时秒数. + 返回: + List[str], 同义词列表, 查询失败返回空列表. + """ + url = f"{_PUBCHEM_BASE}/compound/cid/{cid}/synonyms/JSON" + try: + resp = requests.get(url, timeout=timeout) + if resp.status_code != 200: + return [] + data = resp.json() + tables = data.get("InformationList", {}).get("Information", []) + if tables: + return tables[0].get("Synonym", []) + except (requests.RequestException, json.JSONDecodeError, ValueError) as exc: + logger.warning("PubChem 同义词查询异常: %s", exc) + return [] + + +def _find_sections(data: dict, heading: str) -> list: + """ + 功能: + 递归搜索 PUG-View JSON 中匹配指定 heading 的 Section 节点. + 参数: + data: dict, 当前层级的 JSON 数据. + heading: str, 目标 Section 标题. + 返回: + list, 匹配的 Section 列表. + """ + results = [] + sections = data.get("Section", []) + for sec in sections: + if sec.get("TOCHeading", "") == heading: + results.append(sec) + results.extend(_find_sections(sec, heading)) + return results + + +def _extract_density_from_info_list(info_list: list) -> Optional[float]: + """ + 功能: + 从 PubChem Density Information 列表中提取密度值 (g/mL). + 按优先级: 结构化 Number > 带单位字符串 > 开头浮点数 > 相对密度. + 参数: + info_list: list, PubChem PUG-View Density 的 Information 数组. + 返回: + Optional[float], 密度值, 未提取到返回 None. + """ + # 第一遍: 检查结构化 Number+Unit 格式 + for item in info_list: + value = item.get("Value", {}) + if "Number" in value: + nums = value["Number"] + if isinstance(nums, list) and len(nums) > 0: + val = nums[0] + if 0.3 <= val <= 25.0: + return round(val, 4) + + # 第二遍: 匹配带 g/mL 或 g/cm³ 单位的字符串 + for item in info_list: + value = item.get("Value", {}) + for swm in value.get("StringWithMarkup", []): + text = swm.get("String", "") + text_lower = text.lower() + # 跳过混合描述 + if any(kw in text_lower for kw in _DENSITY_SKIP_KEYWORDS): + continue + # 跳过不等式 + if text.strip().startswith("<") or text.strip().startswith(">"): + continue + match = _DENSITY_WITH_UNIT_RE.search(text) + if match is not None: + return round(float(match.group(1)), 4) + + # 第三遍: 匹配字符串开头的浮点数 (范围 0.3-25.0) + for item in info_list: + value = item.get("Value", {}) + for swm in value.get("StringWithMarkup", []): + text = swm.get("String", "") + text_lower = text.lower() + if any(kw in text_lower for kw in _DENSITY_SKIP_KEYWORDS): + continue + if text.strip().startswith("<") or text.strip().startswith(">"): + continue + # 跳过 relative density / specific gravity (留到第四遍) + if "relative density" in text_lower or "specific gravity" in text_lower: + continue + match = _DENSITY_LEADING_FLOAT_RE.match(text.strip()) + if match is not None: + val = float(match.group(1)) + if 0.3 <= val <= 25.0: + return round(val, 4) + + # 第四遍: 回退 - 从 "Relative density" / "Specific Gravity" 条目提取 + for item in info_list: + value = item.get("Value", {}) + for swm in value.get("StringWithMarkup", []): + text = swm.get("String", "") + text_lower = text.lower() + if "relative density" in text_lower or "specific gravity" in text_lower: + # 尝试匹配范围 (如 "0.80872~0.81601") + range_match = _DENSITY_RANGE_RE.search(text) + if range_match is not None: + low = float(range_match.group(1)) + high = float(range_match.group(2)) + return round((low + high) / 2, 4) + # 尝试匹配单个数值 (如 "Relative density (water = 1): 0.79") + rel_match = _DENSITY_RELATIVE_RE.search(text) + if rel_match is not None: + return round(float(rel_match.group(1)), 4) + # 最后尝试提取最后一个浮点数 + all_floats = re.findall(r"(\d+\.?\d*)", text) + if all_floats: + val = float(all_floats[-1]) + if 0.3 <= val <= 25.0: + return round(val, 4) + + return None + + +def _extract_melting_point_from_info_list(info_list: list) -> Optional[float]: + """ + 功能: + 从 PubChem Melting Point Information 列表中提取熔点 (celsius). + 按优先级: 结构化 Number > °C 字符串 > °F 字符串(转换) > 无单位范围. + 参数: + info_list: list, PubChem PUG-View Melting Point 的 Information 数组. + 返回: + Optional[float], 熔点值(celsius), 未提取到返回 None. + """ + # 第一遍: 检查结构化 Number+Unit 格式 + for item in info_list: + value = item.get("Value", {}) + if "Number" in value and "Unit" in value: + nums = value["Number"] + unit = value.get("Unit", "") + if isinstance(nums, list) and len(nums) > 0: + temp = nums[0] + if "F" in unit and "C" not in unit: + temp = _fahrenheit_to_celsius(temp) + return round(temp, 2) + + # 收集各类匹配结果 + celsius_values = [] + fahrenheit_values = [] + range_values = [] + + for item in info_list: + value = item.get("Value", {}) + for swm in value.get("StringWithMarkup", []): + text = swm.get("String", "") + + # 尝试匹配范围 + °C (如 "134-136°C", "101-104 °C(lit.)") + range_c_match = re.search( + r"(-?\d+\.?\d*)\s*[~~–\-]\s*(-?\d+\.?\d*)\s*°?\s*C", + text, re.IGNORECASE, + ) + if range_c_match is not None: + low = float(range_c_match.group(1)) + high = float(range_c_match.group(2)) + celsius_values.append(round((low + high) / 2, 2)) + continue + + # 尝试匹配单个 °C 值 (如 "-114.14 °C", "135 °C") + c_match = re.search(r"(-?\d+\.?\d*)\s*°\s*C", text) + if c_match is not None: + celsius_values.append(float(c_match.group(1))) + continue + + # 尝试匹配 °F 值 (如 "-173.4 °F") + f_match = re.search(r"(-?\d+\.?\d*)\s*°\s*F", text) + if f_match is not None: + f_val = float(f_match.group(1)) + fahrenheit_values.append(_fahrenheit_to_celsius(f_val)) + continue + + # 尝试匹配无单位范围 (如 "138-140"), 假设为 °C + range_match = re.match( + r"\s*(-?\d+\.?\d*)\s*[~~–\-]\s*(-?\d+\.?\d*)\s*$", + text.strip(), + ) + if range_match is not None: + low = float(range_match.group(1)) + high = float(range_match.group(2)) + range_values.append(round((low + high) / 2, 2)) + + # 按优先级返回: °C > °F > 无单位范围 + if celsius_values: + return celsius_values[0] + if fahrenheit_values: + return fahrenheit_values[0] + if range_values: + return range_values[0] + return None + + +def _pubchem_get_experimental(cid: int, timeout: float = 15.0) -> Tuple[Optional[float], Optional[float]]: + """ + 功能: + 通过 PUG-View API 获取化合物的实验密度和熔点. + 参数: + cid: int, PubChem CID. + timeout: float, 请求超时秒数. + 返回: + (density, melting_point), 各为 Optional[float]. + """ + url = ( + f"{_PUBCHEM_VIEW_BASE}/data/compound/{cid}/JSON" + f"?heading=Experimental+Properties" + ) + try: + resp = requests.get(url, timeout=timeout) + if resp.status_code != 200: + logger.debug("PubChem PUG-View 查询失败: status=%s, cid=%s", resp.status_code, cid) + return None, None + data = resp.json() + record = data.get("Record", {}) + + density = None + melting_point = None + + # 在返回的嵌套 Section 中搜索 Density 和 Melting Point + density_sections = _find_sections(record, "Density") + for sec in density_sections: + info_list = sec.get("Information", []) + density = _extract_density_from_info_list(info_list) + if density is not None: + break + + mp_sections = _find_sections(record, "Melting Point") + for sec in mp_sections: + info_list = sec.get("Information", []) + melting_point = _extract_melting_point_from_info_list(info_list) + if melting_point is not None: + break + + return density, melting_point + + except (requests.RequestException, json.JSONDecodeError, ValueError) as exc: + logger.warning("PubChem PUG-View 查询异常: %s", exc) + return None, None + + +def _query_pubchem(query: str, timeout: float = 15.0) -> Optional[ChemicalInfo]: + """ + 功能: + 通过 PubChem 查询化合物完整信息. + 依次获取 CID, 属性, 同义词(含 CAS), 实验数据(密度/熔点). + 参数: + query: str, CAS 号或化合物名称. + timeout: float, 单次请求超时秒数. + 返回: + Optional[ChemicalInfo], 查询成功返回填充后的对象, 失败返回 None. + """ + logger.info("开始 PubChem 查询: %s", query) + + cid = _pubchem_get_cid(query, timeout) + if cid is None: + logger.info("PubChem 未找到化合物: %s", query) + return None + + return _query_pubchem_by_cid(cid=cid, timeout=timeout) + + +def _query_pubchem_by_cid(cid: int, timeout: float = 15.0) -> Optional[ChemicalInfo]: + """ + 功能: + 根据已解析的 PubChem CID 获取完整化合物信息. + 依次获取属性, 同义词(含 CAS), 实验数据(密度/熔点). + 参数: + cid: int, PubChem CID. + timeout: float, 单次请求超时秒数. + 返回: + Optional[ChemicalInfo], 查询成功返回填充后的对象, 失败返回 None. + """ + if isinstance(cid, int) is False or cid <= 0: + logger.warning("PubChem CID 无效, 无法继续查询: %s", cid) + return None + + logger.debug("PubChem 找到 CID=%s, 开始获取详细信息", cid) + info = ChemicalInfo() + + # 先取基础属性, 便于后续入库字段复用. + iupac, mw = _pubchem_get_properties(cid, timeout) + info.substance_english_name = iupac + info.molecular_weight = mw + + # PubChem 同义词里常含 CAS, 后续可用于补 Common Chemistry 与 ChemicalBook. + synonyms = _pubchem_get_synonyms(cid, timeout) + info.cas_number = _extract_cas_from_synonyms(synonyms) + + # 实验物性沿用现有 PUG-View 解析逻辑. + density, mp = _pubchem_get_experimental(cid, timeout) + info.density = density + info.melting_point = mp + + logger.info( + "PubChem 查询完成: CID=%s, CAS=%s, 名称=%s, MW=%s, 密度=%s, 熔点=%s", + cid, + info.cas_number, + info.substance_english_name, + info.molecular_weight, + info.density, + info.melting_point, + ) + return info + + +# ===================== Common Chemistry 查询 ===================== + +def _query_common_chemistry(query: str, timeout: float = 10.0) -> Optional[ChemicalInfo]: + """ + 功能: + 通过 CAS Common Chemistry API 查询化合物基础信息. + 先搜索获取 CAS RN, 再获取详情 (名称, 分子量). + 参数: + query: str, CAS 号或化合物名称. + timeout: float, 请求超时秒数. + 返回: + Optional[ChemicalInfo], 仅包含 cas_number, substance_english_name, molecular_weight. + """ + logger.info("开始 Common Chemistry 查询: %s", query) + headers = {**_BROWSER_HEADERS, "Accept": "application/json"} + + # 第一步: 搜索获取 CAS RN + search_url = f"{_COMMON_CHEM_BASE}/search?q={requests.utils.quote(query)}" + try: + resp = requests.get(search_url, headers=headers, timeout=timeout) + if resp.status_code != 200: + logger.debug("Common Chemistry 搜索失败: status=%s", resp.status_code) + return None + data = resp.json() + results = data.get("results", []) + if not results: + logger.info("Common Chemistry 未找到化合物: %s", query) + return None + cas_rn = results[0].get("rn", "") + if not cas_rn: + return None + except (requests.RequestException, json.JSONDecodeError, ValueError) as exc: + logger.warning("Common Chemistry 搜索异常: %s", exc) + return None + + # 第二步: 获取详细信息 + detail_url = f"{_COMMON_CHEM_BASE}/detail?cas_rn={cas_rn}" + try: + resp = requests.get(detail_url, headers=headers, timeout=timeout) + if resp.status_code != 200: + # 至少返回搜索到的 CAS 号 + info = ChemicalInfo(cas_number=cas_rn) + return info + data = resp.json() + + info = ChemicalInfo() + info.cas_number = data.get("rn", cas_rn) + info.substance_english_name = data.get("name") + mw_str = data.get("molecularMass") + if mw_str is not None: + try: + info.molecular_weight = float(mw_str) + except (ValueError, TypeError): + pass + + logger.info( + "Common Chemistry 查询完成: CAS=%s, 名称=%s, MW=%s", + info.cas_number, info.substance_english_name, info.molecular_weight, + ) + return info + + except (requests.RequestException, json.JSONDecodeError, ValueError) as exc: + logger.warning("Common Chemistry 详情查询异常: %s", exc) + # 至少返回搜索到的 CAS 号 + return ChemicalInfo(cas_number=cas_rn) + + + +# ===================== 合并与物态判断 ===================== + +def _merge_results( + pubchem: Optional[ChemicalInfo], + common_chem: Optional[ChemicalInfo], +) -> ChemicalInfo: + """ + 功能: + 按优先级合并 PubChem 和 Common Chemistry 查询结果. + 每个字段取第一个非 None 值. + 参数: + pubchem: Optional[ChemicalInfo], PubChem 查询结果. + common_chem: Optional[ChemicalInfo], Common Chemistry 查询结果. + 返回: + ChemicalInfo, 合并后的结果. + """ + merged = ChemicalInfo() + + # cas_number: Common Chemistry > PubChem (CAS 号以 CAS 官方为准) + sources_for_cas = [common_chem, pubchem] + # substance_english_name: PubChem > Common Chemistry + sources_for_en_name = [pubchem, common_chem] + # molecular_weight: PubChem > Common Chemistry + sources_for_mw = [pubchem, common_chem] + # density / melting_point: 仅 PubChem 提供 + sources_for_density = [pubchem] + sources_for_mp = [pubchem] + + for src in sources_for_cas: + if src is not None and src.cas_number is not None and merged.cas_number is None: + merged.cas_number = src.cas_number + + for src in sources_for_en_name: + if src is not None and src.substance_english_name is not None and merged.substance_english_name is None: + merged.substance_english_name = src.substance_english_name + + for src in sources_for_mw: + if src is not None and src.molecular_weight is not None and merged.molecular_weight is None: + merged.molecular_weight = src.molecular_weight + + for src in sources_for_density: + if src is not None and src.density is not None and merged.density is None: + merged.density = src.density + + for src in sources_for_mp: + if src is not None and src.melting_point is not None and merged.melting_point is None: + merged.melting_point = src.melting_point + + return merged + + +def _merge_chemicalbook_result( + merged: ChemicalInfo, + chemicalbook: Optional[ChemicalInfo], +) -> ChemicalInfo: + """ + 功能: + 将 ChemicalBook 结果按补缺优先原则并入已有查询结果. + ChemicalBook 主要补中文名和缺失物性, 不覆盖已存在的 PubChem 核心字段. + 参数: + merged: ChemicalInfo, 已合并的主结果. + chemicalbook: Optional[ChemicalInfo], ChemicalBook 适配结果. + 返回: + ChemicalInfo, 合并后的结果对象. + """ + if chemicalbook is None: + return merged + + if merged.cas_number is None and chemicalbook.cas_number is not None: + merged.cas_number = chemicalbook.cas_number + if merged.substance is None and chemicalbook.substance is not None: + merged.substance = chemicalbook.substance + if merged.substance_english_name is None and chemicalbook.substance_english_name is not None: + merged.substance_english_name = chemicalbook.substance_english_name + if merged.molecular_weight is None and chemicalbook.molecular_weight is not None: + merged.molecular_weight = chemicalbook.molecular_weight + if merged.density is None and chemicalbook.density is not None: + merged.density = chemicalbook.density + if merged.melting_point is None and chemicalbook.melting_point is not None: + merged.melting_point = chemicalbook.melting_point + if merged.physical_state is None and chemicalbook.physical_state is not None: + merged.physical_state = chemicalbook.physical_state + return merged + + +def _determine_physical_state(melting_point: Optional[float]) -> str: + """ + 功能: + 根据熔点推断物质的物态. + 参数: + melting_point: Optional[float], 熔点 (celsius). + 返回: + str, "solid" / "liquid" / "". + """ + if melting_point is None: + return "" + if melting_point > 25.0: + return "solid" + return "liquid" + + +def _query_chemicalbook(cas: str) -> Optional[ChemicalInfo]: + """ + 功能: + 根据 CAS 调用 ChemicalBook 抓取器, 并转换为 ChemicalInfo 对象. + 参数: + cas: str, CAS 号. + 返回: + Optional[ChemicalInfo], 适配后的 ChemicalBook 结果, 失败时返回 None. + """ + normalized_cas = str(cas or "").strip() + if normalized_cas == "": + return None + + record = chemicalbook_scraper.fetch_chemicalbook_by_cas(normalized_cas) + if isinstance(record, dict) is False: + return None + + normalized = record.get("normalized") + if isinstance(normalized, dict) is False: + return None + + chemical_info = ChemicalInfo( + cas_number=str(record.get("cas") or normalized_cas).strip() or normalized_cas, + substance_english_name=str(normalized.get("en_name") or "").strip() or None, + substance=str(normalized.get("cn_name") or "").strip() or None, + molecular_weight=normalized.get("molecular_weight"), + density=get_measurement_value(normalized, "density"), + melting_point=get_measurement_value(normalized, "melting_point"), + ) + chemical_info.physical_state = _determine_physical_state(chemical_info.melting_point) + return chemical_info + + +# ===================== 对外统一入口 ===================== + +def lookup_chemical(query: str) -> Optional[ChemicalInfo]: + """ + 功能: + 对外统一入口. 根据输入判断 CAS 号或名称, 依次查询 PubChem 和 + Common Chemistry, 按优先级合并结果并推断物态. + ChemicalBook 中文名/密度/熔点由外部 chemicalbook_scraper 单独处理. + 参数: + query: str, CAS 号或化合物中英文名称. + 返回: + Optional[ChemicalInfo], 合并后的化合物信息, 所有源都查询失败则返回 None. + """ + query = query.strip() + if not query: + logger.warning("查询字符串为空") + return None + + is_cas = is_cas_number(query) + logger.info("开始化合物查询: query=%s, 类型=%s", query, "CAS号" if is_cas else "名称") + + # 查询各数据源 (各自独立, 互不影响) + pubchem_result = None + common_chem_result = None + + # 源 1: PubChem + try: + pubchem_result = _query_pubchem(query) + except Exception as exc: + logger.warning("PubChem 查询意外异常: %s", exc) + + # 源 2: Common Chemistry + try: + common_chem_result = _query_common_chemistry(query) + except Exception as exc: + logger.warning("Common Chemistry 查询意外异常: %s", exc) + + # 检查是否所有源都失败 + if pubchem_result is None and common_chem_result is None: + logger.warning("所有数据源均未查询到化合物: %s", query) + return None + + # 合并结果 + merged = _merge_results(pubchem_result, common_chem_result) + + # 推断物态 + merged.physical_state = _determine_physical_state(merged.melting_point) + + logger.info( + "化合物查询完成: CAS=%s, 英文名=%s, MW=%s, 密度=%s, 熔点=%s, 物态=%s", + merged.cas_number, merged.substance_english_name, + merged.molecular_weight, merged.density, merged.melting_point, + merged.physical_state, + ) + return merged + + +def lookup_chemical_bundle(query: str) -> Dict[str, Optional[object]]: + """ + 功能: + 执行多源化学查询并返回核心结果与 ChemicalBook 上下文. + 返回值用于上层决定是否保存 sidecar 或继续补充其他字段. + 参数: + query: str, CAS 号或化合物中英文名称. + 返回: + Dict[str, Optional[object]], 包含 info, resolved_cas, chemicalbook_record, chemicalbook_status. + """ + normalized_query = str(query or "").strip() + if normalized_query == "": + logger.warning("化合物 bundle 查询参数为空") + return { + "info": None, + "resolved_cas": "", + "chemicalbook_record": None, + "chemicalbook_status": "", + } + + pubchem_result = None + common_chem_result = None + try: + pubchem_result = _query_pubchem(normalized_query) + except Exception as exc: + logger.warning("bundle PubChem 查询异常: %s", exc) + + try: + common_chem_result = _query_common_chemistry(normalized_query) + except Exception as exc: + logger.warning("bundle Common Chemistry 查询异常: %s", exc) + + merged = None + if pubchem_result is not None or common_chem_result is not None: + merged = _merge_results(pubchem_result, common_chem_result) + + resolved_cas = "" + if merged is not None and str(merged.cas_number or "").strip() != "": + resolved_cas = str(merged.cas_number).strip() + elif is_cas_number(normalized_query) is True: + resolved_cas = normalized_query + + chemicalbook_record = None + chemicalbook_status = "" + chemicalbook_info = None + if resolved_cas != "": + try: + chemicalbook_record = chemicalbook_scraper.fetch_chemicalbook_by_cas(resolved_cas) + if isinstance(chemicalbook_record, dict) is True: + chemicalbook_status = str(chemicalbook_record.get("status") or "") + normalized = chemicalbook_record.get("normalized") + if isinstance(normalized, dict) is True: + chemicalbook_info = ChemicalInfo( + cas_number=str(chemicalbook_record.get("cas") or resolved_cas).strip() or resolved_cas, + substance_english_name=str(normalized.get("en_name") or "").strip() or None, + substance=str(normalized.get("cn_name") or "").strip() or None, + molecular_weight=normalized.get("molecular_weight"), + density=get_measurement_value(normalized, "density"), + melting_point=get_measurement_value(normalized, "melting_point"), + ) + except Exception as exc: + logger.warning("bundle ChemicalBook 查询异常: %s", exc) + + if chemicalbook_info is not None: + chemicalbook_info.physical_state = _determine_physical_state(chemicalbook_info.melting_point) + + if merged is None: + merged = chemicalbook_info + elif chemicalbook_info is not None: + merged = _merge_chemicalbook_result(merged, chemicalbook_info) + + if merged is not None: + merged.physical_state = _determine_physical_state(merged.melting_point) + + return { + "info": merged, + "resolved_cas": resolved_cas, + "chemicalbook_record": chemicalbook_record, + "chemicalbook_status": chemicalbook_status, + } + + +def lookup_chemical_by_smiles(smiles: str) -> Optional[ChemicalInfo]: + """ + 功能: + 根据单个完整 SMILES 查询化合物信息. + 先通过 PubChem 结构查询获取 CID 与核心属性, 再在拿到 CAS 后补 Common Chemistry. + ChemicalBook 中文名/密度/熔点仍由外部 chemicalbook_scraper 单独处理. + 参数: + smiles: str, 单个完整 SMILES 结构式. + 返回: + Optional[ChemicalInfo], 合并后的化合物信息, 查询失败时返回 None. + """ + normalized_smiles = str(smiles or "").strip() + if normalized_smiles == "": + logger.warning("SMILES 查询字符串为空") + return None + + logger.info("开始 SMILES 化合物查询: smiles=%s", normalized_smiles) + + try: + cid = _pubchem_get_cid_by_smiles(normalized_smiles) + except Exception as exc: + logger.warning("PubChem SMILES CID 查询意外异常: %s", exc) + return None + + if cid is None: + logger.warning("PubChem 未找到 SMILES 对应化合物: %s", normalized_smiles) + return None + + pubchem_result = None + try: + pubchem_result = _query_pubchem_by_cid(cid=cid) + except Exception as exc: + logger.warning("PubChem SMILES 详细查询意外异常: %s", exc) + + if pubchem_result is None: + logger.warning("SMILES 查询失败, 未获取到 PubChem 详情: %s", normalized_smiles) + return None + + common_chem_result = None + resolved_cas = str(pubchem_result.cas_number or "").strip() + if resolved_cas != "": + try: + common_chem_result = _query_common_chemistry(resolved_cas) + except Exception as exc: + logger.warning("SMILES 查询补 Common Chemistry 异常: %s", exc) + + merged = _merge_results(pubchem_result, common_chem_result) + merged.physical_state = _determine_physical_state(merged.melting_point) + + logger.info( + "SMILES 化合物查询完成: SMILES=%s, CAS=%s, 英文名=%s, MW=%s, 密度=%s, 熔点=%s, 物态=%s", + normalized_smiles, + merged.cas_number, + merged.substance_english_name, + merged.molecular_weight, + merged.density, + merged.melting_point, + merged.physical_state, + ) + return merged diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/chemicalbook_scraper.py b/unilabos/devices/eit_synthesis_station/chem_tools/chemicalbook_scraper.py new file mode 100644 index 00000000..2ceb5cd7 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/chemicalbook_scraper.py @@ -0,0 +1,2549 @@ +# -*- coding: utf-8 -*- +""" +功能: + 提供 ChemicalBook 页面抓取, 缓存, 反爬识别, DOM 解析与结构化标准化能力. + 本模块仅处理按 CAS 直达的中文页与英文页, 输出稳定 JSON 结构. +参数: + 无. +返回: + 无. +""" + +import copy +import html as html_lib +import json +import logging +import random +import re +import time +from dataclasses import dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Dict, Iterable, List, Optional, Tuple + +import requests + +from . import storage + +try: + from bs4 import BeautifulSoup + from bs4.element import Tag +except ImportError as exc: # pragma: no cover - 运行环境未安装依赖时走这里 + BeautifulSoup = None + Tag = Any + _BS4_IMPORT_ERROR = exc +else: + _BS4_IMPORT_ERROR = None + + +logger = logging.getLogger("ChemicalBookScraper") + +CAS_PATTERN = re.compile(r"^\d{2,7}-\d{2}-\d$") +FLOAT_RE = re.compile(r"-?\d+(?:\.\d+)?") +CELSIUS_RANGE_RE = re.compile( + r"(-?\d+(?:\.\d+)?)\s*(?:to|~|-|—|–)\s*(-?\d+(?:\.\d+)?)\s*(?:°|º)?\s*C", + re.IGNORECASE, +) +CELSIUS_RE = re.compile(r"(-?\d+(?:\.\d+)?)\s*(?:°|º)?\s*C", re.IGNORECASE) +FAHRENHEIT_RANGE_RE = re.compile( + r"(-?\d+(?:\.\d+)?)\s*(?:to|~|-|—|–)\s*(-?\d+(?:\.\d+)?)\s*(?:°|º)?\s*F", + re.IGNORECASE, +) +FAHRENHEIT_RE = re.compile(r"(-?\d+(?:\.\d+)?)\s*(?:°|º)?\s*F", re.IGNORECASE) +GBK_META_RE = re.compile(r"charset\s*=\s*(gbk|gb2312|gb18030)", re.IGNORECASE) +BLOCK_KEYWORDS = [ + "验证码", + "访问过于频繁", + "安全验证", + "输入验证码", + "captcha", + "robot", + "verify", +] +DEFAULT_TIMEOUT = 15.0 +DEFAULT_CACHE_TTL_S = 7 * 24 * 60 * 60 # 7 天, 中文名等基本信息几乎不变 +DEFAULT_MIN_INTERVAL_S = 2.0 +MAX_RETRIES = 3 + +CHEMICALBOOK_CN_URL = "https://www.chemicalbook.com/CAS_{cas}.htm" +CHEMICALBOOK_EN_URL = "https://www.chemicalbook.com/CASEN_{cas}.htm" + +DEFAULT_SECTIONS = ( + "基本信息", + "物理化学性质", + "安全信息", + "用途与合成方法", + "上下游产品信息", +) + +BROWSER_HEADERS = { + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" + ), + "Accept": ( + "text/html,application/xhtml+xml,application/xml;q=0.9," + "image/avif,image/webp,image/apng,*/*;q=0.8" + ), + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + "Upgrade-Insecure-Requests": "1", + "Sec-Ch-Ua": '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', + "Sec-Ch-Ua-Mobile": "?0", + "Sec-Ch-Ua-Platform": '"Windows"', + "Sec-Fetch-Dest": "document", + "Sec-Fetch-Mode": "navigate", + "Sec-Fetch-Site": "none", + "Sec-Fetch-User": "?1", +} + +# UA 轮换池, 降低重试时的指纹一致性 +_USER_AGENT_POOL = [ + ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" + ), + ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/123.0.0.0 Safari/537.36" + ), + ( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/124.0.0.0 Safari/537.36" + ), + ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) " + "Gecko/20100101 Firefox/124.0" + ), +] + +SECTION_KEYWORDS = { + "基本信息": [ + "基本信息", + "产品名称", + "化学品信息", + "product information", + "basic information", + ], + "物理化学性质": [ + "物理化学性质", + "性质", + "理化性质", + "physical and chemical properties", + "physical properties", + ], + "安全信息": [ + "安全信息", + "危险类别", + "安全术语", + "hazard information", + "safety information", + "safety", + ], + "用途与合成方法": [ + "用途与合成方法", + "用途", + "应用", + "use", + "application", + "description", + "general description", + ], + "上下游产品信息": [ + "上下游产品信息", + "上游产品", + "下游产品", + "upstream products", + "downstream products", + "related products", + ], +} + +FIELD_ALIASES = { + "cn_name": ["中文名称", "中文名", "产品中文名称"], + "en_name": [ + "英文名称", + "英文名", + "name", + "product name", + "chemical name", + "casenname", + ], + "aliases": ["别名", "同义词", "别名或商品名", "中文别名", "英文别名", "synonyms", "alias"], + "molecular_formula": ["分子式", "结构式", "molecular formula", "formula"], + "molecular_weight": ["分子量", "分子量值", "molecular weight", "mol weight"], + "density": ["密度", "density", "relative density", "specific gravity", "比重"], + "melting_point": ["熔点", "熔点范围", "melting point", "mp"], + "boiling_point": ["沸点", "boiling point", "bp"], + "flash_point": ["闪点", "flash point", "fp"], + "appearance": ["性状", "外观", "外观性质", "外观性状", "形态", "appearance", "color and form", "form"], + "solubility": ["溶解度", "溶解性", "水溶解性", "solubility", "water solubility"], + "storage_conditions": ["储存条件", "贮存条件", "存储类别", "storage temp", "storage", "store at"], + "hazard_statements": [ + "危险说明", + "危险性描述", + "危险类别码", + "hazard statements", + "hazard codes", + "ghs hazard statements", + "hazard", + ], + "safety_statements": [ + "安全说明", + "防范说明", + "安全术语", + "safety statements", + "safety description", + "ghs precautionary statements", + ], + "uses": ["用途", "应用", "用途与合成方法", "application", "use", "general description"], + "upstream_products": ["上游产品", "上游原料", "raw materials", "upstream products"], + "downstream_products": ["下游产品", "preparation products", "downstream products"], +} + +FIELD_SECTION_MAP = { + "cn_name": "基本信息", + "en_name": "基本信息", + "aliases": "基本信息", + "molecular_formula": "基本信息", + "molecular_weight": "基本信息", + "density": "物理化学性质", + "melting_point": "物理化学性质", + "boiling_point": "物理化学性质", + "flash_point": "物理化学性质", + "appearance": "物理化学性质", + "solubility": "物理化学性质", + "storage_conditions": "安全信息", + "hazard_statements": "安全信息", + "safety_statements": "安全信息", + "uses": "用途与合成方法", + "upstream_products": "上下游产品信息", + "downstream_products": "上下游产品信息", +} + +PAGE_TYPES = ("cn", "en") +_LAST_REQUEST_AT_BY_CAS: Dict[str, float] = {} + + +@dataclass +class PageFetchResult: + """ + 功能: + 记录单个 ChemicalBook 页面抓取结果, 供抓取层与解析层传递状态. + 参数: + page_type: str, 页面类型, cn 或 en. + url: str, 页面 URL. + status_code: Optional[int], HTTP 状态码. + html: Optional[str], 解码后的 HTML 文本. + blocked: bool, 是否命中反爬页面. + blocked_reason: str, 反爬原因说明. + from_cache: bool, 是否命中本地缓存. + error_message: str, 抓取异常说明. + 返回: + PageFetchResult. + """ + + page_type: str + url: str + status_code: Optional[int] = None + html: Optional[str] = None + blocked: bool = False + blocked_reason: str = "" + from_cache: bool = False + error_message: str = "" + + +def fetch_chemicalbook_by_cas( + cas: str, + timeout: float = DEFAULT_TIMEOUT, + save_raw_html: bool = False, +) -> Dict[str, Any]: + """ + 功能: + 根据 CAS 号抓取 ChemicalBook 中文页与英文页, 并返回标准化 JSON 结构. + 参数: + cas: str, CAS 号, 例如 64-17-5. + timeout: float, 单次请求超时秒数. + save_raw_html: bool, 是否额外保存原始 HTML 文件到本地目录. + 返回: + Dict[str, Any], 包含抓取状态, 标准化字段, 原始 sections 与调试信息. + """ + storage.ensure_chemicalbook_data_layout() + record = _build_empty_record(cas=cas) + normalized_cas = str(cas).strip() + record["cas"] = normalized_cas + + if CAS_PATTERN.match(normalized_cas) is None: + record["status"] = "error" + record["debug"]["blocked_reason"] = "CAS 格式不合法" + logger.warning("ChemicalBook 抓取失败, CAS 格式不合法: %s", normalized_cas) + return record + + if BeautifulSoup is None: + logger.warning("未安装 beautifulsoup4, 将使用降级解析路径") + + session = _build_session() + page_results: List[PageFetchResult] = [] + + need_network = _has_cache_miss(normalized_cas) + if need_network is True: + _apply_cas_rate_limit(normalized_cas) + + for idx, page_type in enumerate(PAGE_TYPES): + # CN 页和 EN 页之间加入随机延迟, 降低连续请求特征 + if idx > 0 and need_network is True: + delay = random.uniform(1.5, 3.5) + logger.debug("ChemicalBook 页面间等待 %.2f 秒", delay) + time.sleep(delay) + + page_result = _fetch_page_with_cache( + session=session, + cas=normalized_cas, + page_type=page_type, + timeout=timeout, + ) + page_results.append(page_result) + + status_key = f"http_status_{page_type}" + cache_key = f"cache_hit_{page_type}" + record["debug"][status_key] = page_result.status_code + record["debug"][cache_key] = page_result.from_cache + + if page_result.blocked is True: + record["debug"]["blocked"] = True + if len(record["debug"]["blocked_reason"]) == 0: + record["debug"]["blocked_reason"] = page_result.blocked_reason + + merged_sections = _build_empty_sections() + all_pairs: List[Tuple[str, str]] = [] + + for page_result in page_results: + if page_result.html is None: + continue + + parsed_page = _parse_chemicalbook_page( + html=page_result.html, + page_type=page_result.page_type, + ) + _merge_sections(merged_sections, parsed_page["sections"]) + all_pairs.extend(parsed_page["pairs"]) + + if save_raw_html is True: + raw_path = _save_raw_html( + cas=normalized_cas, + page_type=page_result.page_type, + html=page_result.html, + ) + record["debug"]["raw_html_paths"][page_result.page_type] = str(raw_path) + + record["sections"] = merged_sections + record["normalized"] = _normalize_from_pairs_and_sections( + cas=normalized_cas, + pairs=all_pairs, + sections=merged_sections, + ) + record = normalize_chemicalbook_record(record) + record["status"] = _determine_record_status(record=record, page_results=page_results) + return record + + +def normalize_chemicalbook_record(record: Dict[str, Any]) -> Dict[str, Any]: + """ + 功能: + 基于已抓取 record 中的 sections 信息, 生成稳定的 normalized 字段. + 该函数不会删除 sections 中的未知字段, 仅补全或覆盖 normalized. + 参数: + record: Dict[str, Any], fetch_chemicalbook_by_cas 生成或兼容的数据结构. + 返回: + Dict[str, Any], 补全 normalized 后的新字典. + """ + normalized_record = copy.deepcopy(record) + cas = str(normalized_record.get("cas", "")).strip() + sections = normalized_record.get("sections", {}) + pairs = _collect_pairs_from_sections(sections) + normalized = _normalize_from_pairs_and_sections( + cas=cas, + pairs=pairs, + sections=sections, + ) + normalized_record["normalized"] = normalized + return normalized_record + + +def _build_session() -> requests.Session: + """ + 功能: + 构建 requests Session, 统一请求头, 并默认忽略系统代理环境变量. + 参数: + 无. + 返回: + requests.Session, 已配置好的会话对象. + """ + session = requests.Session() + session.headers.update(BROWSER_HEADERS) + session.trust_env = False + return session + + +def _build_empty_record(cas: str) -> Dict[str, Any]: + """ + 功能: + 构建标准输出骨架, 便于后续逐步填充抓取结果. + 参数: + cas: str, CAS 号. + 返回: + Dict[str, Any], 默认 JSON 结构. + """ + return { + "cas": cas, + "status": "error", + "source": "chemicalbook", + "source_urls": { + "cn": CHEMICALBOOK_CN_URL.format(cas=cas), + "en": CHEMICALBOOK_EN_URL.format(cas=cas), + }, + "fetched_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "normalized": _build_empty_normalized(cas), + "sections": _build_empty_sections(), + "debug": { + "http_status_cn": None, + "http_status_en": None, + "blocked": False, + "blocked_reason": "", + "cache_hit_cn": False, + "cache_hit_en": False, + "raw_html_paths": {}, + }, + } + + +def _build_empty_sections() -> Dict[str, Dict[str, Any]]: + """ + 功能: + 构建默认 sections 容器. + 参数: + 无. + 返回: + Dict[str, Dict[str, Any]], 默认 section 映射. + """ + sections: Dict[str, Dict[str, Any]] = {} + for section_name in DEFAULT_SECTIONS: + sections[section_name] = {} + return sections + + +def _build_empty_normalized(cas: str) -> Dict[str, Any]: + """ + 功能: + 构建标准化字段骨架. + 参数: + cas: str, CAS 号. + 返回: + Dict[str, Any], normalized 字段初始值. + """ + return { + "cn_name": "", + "en_name": "", + "aliases": [], + "molecular_formula": "", + "molecular_weight": None, + "density": {"raw": "", "value": None, "unit": "g/mL"}, + "melting_point": {"raw": "", "value": None, "unit": "C"}, + "boiling_point": {"raw": "", "value": None, "unit": "C"}, + "flash_point": {"raw": "", "value": None, "unit": "C"}, + "appearance": "", + "solubility": "", + "storage_conditions": "", + "hazard_statements": [], + "safety_statements": [], + "uses": [], + "upstream_products": [], + "downstream_products": [], + "cas": cas, + } + + +def _has_cache_miss(cas: str) -> bool: + """ + 功能: + 判断当前 CAS 是否存在缓存缺失, 用于决定是否需要执行限速等待. + 参数: + cas: str, CAS 号. + 返回: + bool, True 表示至少一个页面需要走网络抓取. + """ + for page_type in PAGE_TYPES: + cached_result = _load_cached_page(cas=cas, page_type=page_type) + if cached_result is None: + return True + return False + + +def _apply_cas_rate_limit(cas: str) -> None: + """ + 功能: + 对同一 CAS 的连续抓取增加最小间隔, 降低触发反爬概率. + 参数: + cas: str, CAS 号. + 返回: + None. + """ + last_request_at = _LAST_REQUEST_AT_BY_CAS.get(cas) + if last_request_at is None: + _LAST_REQUEST_AT_BY_CAS[cas] = time.time() + return + + elapsed = time.time() - last_request_at + if elapsed < DEFAULT_MIN_INTERVAL_S: + wait_seconds = DEFAULT_MIN_INTERVAL_S - elapsed + logger.info("ChemicalBook 抓取限速生效, CAS=%s, 等待 %.2f 秒", cas, wait_seconds) + time.sleep(wait_seconds) + + _LAST_REQUEST_AT_BY_CAS[cas] = time.time() + + +def _fetch_page_with_cache( + cas: str, + page_type: str, + timeout: float, + session: Optional[requests.Session] = None, +) -> PageFetchResult: + """ + 功能: + 先尝试读取本地缓存, 失败后走网络抓取并更新缓存. + 参数: + session: requests.Session, 已配置会话. + cas: str, CAS 号. + page_type: str, 页面类型, cn 或 en. + timeout: float, 超时秒数. + 返回: + PageFetchResult, 页面抓取结果. + """ + storage.ensure_chemicalbook_data_layout() + cached_result = _load_cached_page(cas=cas, page_type=page_type) + if cached_result is not None: + logger.info("ChemicalBook 命中缓存, CAS=%s, page=%s", cas, page_type) + return cached_result + + if session is None: + session = _build_session() + + url = _build_page_url(cas=cas, page_type=page_type) + page_result = _fetch_page_with_playwright_direct( + session=session, + page_type=page_type, + url=url, + timeout=timeout, + cas=cas, + ) + + if page_result.html is not None: + _save_cached_page(cas=cas, page_type=page_type, result=page_result) + return page_result + + +def _fetch_page_with_playwright_direct( + session: requests.Session, + page_type: str, + url: str, + timeout: float, + cas: str = "", +) -> PageFetchResult: + """ + 功能: + 在缓存缺失时执行页面抓取. + 先尝试 requests, 若疑似触发反爬再切换到 playwright. + 参数: + session: requests.Session, 已配置会话. + page_type: str, 页面类型. + url: str, 页面 URL. + timeout: float, 超时秒数. + cas: str, CAS 号, 用于补充请求头. + 返回: + PageFetchResult, 页面抓取结果. + """ + page_result = _fetch_page_from_network( + session=session, + page_type=page_type, + url=url, + timeout=timeout, + cas=cas, + ) + + if page_result.blocked is True: + logger.warning( + "ChemicalBook 页面疑似触发反爬, CAS=%s, page=%s, reason=%s", + cas, + page_type, + page_result.blocked_reason, + ) + playwright_result = _fetch_page_with_playwright( + page_type=page_type, + url=url, + timeout=timeout, + ) + if playwright_result.html is not None and playwright_result.blocked is False: + return playwright_result + + return page_result + + +def _fetch_page_from_network( + session: requests.Session, + page_type: str, + url: str, + timeout: float, + cas: str = "", +) -> PageFetchResult: + """ + 功能: + 使用 requests 进行网络抓取, 包含重试, UA 轮换, 503 专项退避与 Referer 链. + 参数: + session: requests.Session, 已配置会话. + page_type: str, 页面类型. + url: str, 页面 URL. + timeout: float, 超时秒数. + cas: str, CAS 号, 用于构建 Referer 链. + 返回: + PageFetchResult, 页面抓取结果. + """ + last_error_message = "" + last_status_code = None + + for attempt in range(MAX_RETRIES): + # 每次重试轮换 User-Agent, 降低指纹一致性 + session.headers["User-Agent"] = random.choice(_USER_AGENT_POOL) + + # 设置 Referer 链, 模拟自然浏览行为 + if page_type == "cn": + session.headers["Referer"] = "https://www.chemicalbook.com/" + elif cas != "": + # EN 页通常从 CN 页跳转 + session.headers["Referer"] = CHEMICALBOOK_CN_URL.format(cas=cas) + + try: + response = session.get(url, timeout=timeout) + html = _decode_response_text(response) + blocked, blocked_reason = _detect_blocked_page(html) + result = PageFetchResult( + page_type=page_type, + url=url, + status_code=response.status_code, + html=html if response.status_code == 200 else None, + blocked=blocked, + blocked_reason=blocked_reason, + ) + + if response.status_code == 200: + return result + + if response.status_code in (404, 410): + logger.info("ChemicalBook 页面不存在, page=%s, url=%s", page_type, url) + return result + + last_status_code = response.status_code + last_error_message = f"HTTP {response.status_code}" + logger.warning( + "ChemicalBook 请求失败, page=%s, status=%s, attempt=%s, url=%s", + page_type, + response.status_code, + attempt + 1, + url, + ) + except requests.RequestException as exc: + last_error_message = str(exc) + logger.warning( + "ChemicalBook 请求异常, page=%s, attempt=%s, err=%s", + page_type, + attempt + 1, + exc, + ) + + if attempt + 1 < MAX_RETRIES: + if last_status_code == 503: + # 503 通常意味着限速, 需要更长等待 + wait_seconds = (5 * (attempt + 1)) + random.uniform(1.0, 3.0) + else: + wait_seconds = (2 ** attempt) + random.uniform(0.5, 1.5) + logger.debug("ChemicalBook 重试等待 %.2f 秒, attempt=%d", wait_seconds, attempt + 1) + time.sleep(wait_seconds) + + return PageFetchResult( + page_type=page_type, + url=url, + status_code=None, + html=None, + blocked=False, + blocked_reason="", + from_cache=False, + error_message=last_error_message, + ) + + +def _fetch_page_with_playwright( + page_type: str, + url: str, + timeout: float, +) -> PageFetchResult: + """ + 功能: + 当 requests 命中反爬页面时, 尝试使用 playwright 无头浏览器重新抓取. + 参数: + page_type: str, 页面类型. + url: str, 页面 URL. + timeout: float, 超时秒数. + 返回: + PageFetchResult, 浏览器抓取结果. 未安装 playwright 时返回 blocked 结果. + """ + try: + from playwright.sync_api import sync_playwright + except ImportError: + return PageFetchResult( + page_type=page_type, + url=url, + status_code=None, + html=None, + blocked=True, + blocked_reason="命中反爬页面, 且未安装 playwright", + from_cache=False, + error_message="playwright 未安装", + ) + + try: + logger.info("使用 playwright 重试 ChemicalBook 页面, page=%s, url=%s", page_type, url) + with sync_playwright() as pw: + browser = pw.chromium.launch(headless=True) + # 创建完整浏览器上下文, 模拟真实用户环境 + context = browser.new_context( + user_agent=random.choice(_USER_AGENT_POOL), + viewport={"width": 1920, "height": 1080}, + locale="zh-CN", + timezone_id="Asia/Shanghai", + ) + page = context.new_page() + page.goto(url, wait_until="networkidle", timeout=int(timeout * 1000)) + # 模拟人类浏览: 页面加载后短暂等待 + page.wait_for_timeout(random.randint(1000, 2000)) + html = page.content() + context.close() + browser.close() + except Exception as exc: # pragma: no cover - 依赖外部浏览器 + return PageFetchResult( + page_type=page_type, + url=url, + status_code=None, + html=None, + blocked=True, + blocked_reason=f"playwright 抓取失败: {exc}", + from_cache=False, + error_message=str(exc), + ) + + blocked, blocked_reason = _detect_blocked_page(html) + return PageFetchResult( + page_type=page_type, + url=url, + status_code=200, + html=html, + blocked=blocked, + blocked_reason=blocked_reason, + from_cache=False, + ) + + +def _build_page_url(cas: str, page_type: str) -> str: + """ + 功能: + 根据 CAS 与页面类型生成 ChemicalBook URL. + 参数: + cas: str, CAS 号. + page_type: str, 页面类型, cn 或 en. + 返回: + str, 页面 URL. + """ + if page_type == "cn": + return CHEMICALBOOK_CN_URL.format(cas=cas) + return CHEMICALBOOK_EN_URL.format(cas=cas) + + +def _cache_file_paths(cas: str, page_type: str) -> Tuple[Path, Path]: + """ + 功能: + 计算缓存 HTML 与元数据文件路径. + 参数: + cas: str, CAS 号. + page_type: str, 页面类型. + 返回: + Tuple[Path, Path], (html_path, meta_path). + """ + storage.ensure_chemicalbook_data_layout() + safe_cas = cas.replace("/", "_") + html_path = storage.CHEMICALBOOK_CACHE_ROOT / f"{safe_cas}_{page_type}.html" + meta_path = storage.CHEMICALBOOK_CACHE_ROOT / f"{safe_cas}_{page_type}.json" + return html_path, meta_path + + +def _load_cached_page(cas: str, page_type: str) -> Optional[PageFetchResult]: + """ + 功能: + 从本地缓存读取页面内容. 超过 TTL 的缓存将被忽略. + 参数: + cas: str, CAS 号. + page_type: str, 页面类型. + 返回: + Optional[PageFetchResult], 命中缓存返回结果, 否则返回 None. + """ + html_path, meta_path = _cache_file_paths(cas=cas, page_type=page_type) + if html_path.exists() is False or meta_path.exists() is False: + return None + + age_seconds = time.time() - html_path.stat().st_mtime + if age_seconds > DEFAULT_CACHE_TTL_S: + return None + + try: + html = html_path.read_text(encoding="utf-8") + metadata = json.loads(meta_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + logger.warning("ChemicalBook 读取缓存失败, CAS=%s, page=%s, err=%s", cas, page_type, exc) + return None + + return PageFetchResult( + page_type=page_type, + url=str(metadata.get("url", _build_page_url(cas=cas, page_type=page_type))), + status_code=metadata.get("status_code"), + html=html, + blocked=bool(metadata.get("blocked", False)), + blocked_reason=str(metadata.get("blocked_reason", "")), + from_cache=True, + error_message=str(metadata.get("error_message", "")), + ) + + +def _save_cached_page(cas: str, page_type: str, result: PageFetchResult) -> None: + """ + 功能: + 将页面抓取结果写入本地缓存. + 参数: + cas: str, CAS 号. + page_type: str, 页面类型. + result: PageFetchResult, 待缓存的页面结果. + 返回: + None. + """ + storage.ensure_chemicalbook_data_layout() + storage.CHEMICALBOOK_CACHE_ROOT.mkdir(parents=True, exist_ok=True) + html_path, meta_path = _cache_file_paths(cas=cas, page_type=page_type) + metadata = { + "url": result.url, + "status_code": result.status_code, + "blocked": result.blocked, + "blocked_reason": result.blocked_reason, + "error_message": result.error_message, + "saved_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + } + + try: + html_path.write_text(result.html or "", encoding="utf-8") + meta_path.write_text(json.dumps(metadata, ensure_ascii=False, indent=2), encoding="utf-8") + except OSError as exc: + logger.warning("ChemicalBook 写入缓存失败, CAS=%s, page=%s, err=%s", cas, page_type, exc) + + +def _save_raw_html(cas: str, page_type: str, html: str) -> Path: + """ + 功能: + 将原始 HTML 保存到独立目录, 便于人工排查解析问题. + 参数: + cas: str, CAS 号. + page_type: str, 页面类型. + html: str, 原始 HTML 文本. + 返回: + Path, 保存后的文件路径. + """ + storage.ensure_chemicalbook_data_layout() + storage.CHEMICALBOOK_RAW_ROOT.mkdir(parents=True, exist_ok=True) + raw_path = storage.CHEMICALBOOK_RAW_ROOT / f"{cas}_{page_type}.html" + raw_path.write_text(html, encoding="utf-8") + return raw_path + + +def _decode_response_text(response: requests.Response) -> str: + """ + 功能: + 优先根据响应头与 apparent_encoding 解码 HTML, 尽量避免中文乱码. + 参数: + response: requests.Response, HTTP 响应对象. + 返回: + str, 解码后的 HTML 文本. + """ + declared_encoding = response.encoding + if declared_encoding is None: + content_type = str(response.headers.get("Content-Type", "")) + meta_match = GBK_META_RE.search(content_type) + if meta_match is not None: + declared_encoding = meta_match.group(1) + + apparent_encoding = getattr(response, "apparent_encoding", None) + if apparent_encoding is not None and len(str(apparent_encoding).strip()) > 0: + declared_encoding = str(apparent_encoding) + + if declared_encoding is not None and len(str(declared_encoding).strip()) > 0: + try: + return response.content.decode(str(declared_encoding), errors="replace") + except (LookupError, UnicodeDecodeError): + pass + + try: + return response.content.decode("utf-8", errors="replace") + except UnicodeDecodeError: + return response.text + + +def _detect_blocked_page(html: str) -> Tuple[bool, str]: + """ + 功能: + 根据页面标题与正文关键词识别是否触发了反爬或安全验证页面. + 参数: + html: str, HTML 文本. + 返回: + Tuple[bool, str], (是否命中反爬, 原因说明). + """ + text = _clean_text(_strip_html_tags(html)).lower() + for keyword in BLOCK_KEYWORDS: + if keyword.lower() in text: + return True, f"页面包含反爬关键词: {keyword}" + return False, "" + + +def _parse_chemicalbook_page(html: str, page_type: str) -> Dict[str, Any]: + """ + 功能: + 解析单个 ChemicalBook 页面, 输出 sections 与标签值对列表. + 参数: + html: str, 页面 HTML. + page_type: str, 页面类型, cn 或 en. + 返回: + Dict[str, Any], 包含 sections 与 pairs. + """ + if BeautifulSoup is None: + return _parse_chemicalbook_page_fallback(html=html, page_type=page_type) + + soup = BeautifulSoup(html, "html.parser") + _remove_noise_nodes(soup) + + sections = _build_empty_sections() + pairs: List[Tuple[str, str]] = [] + + title_text = _clean_text(soup.title.get_text(" ", strip=True)) if soup.title is not None else "" + if len(title_text) > 0: + _merge_section_value(sections["基本信息"], "页面标题", title_text) + pairs.append(("页面标题", title_text)) + + h1 = soup.find("h1") + if h1 is not None: + header_text = _clean_text(h1.get_text(" ", strip=True)) + if len(header_text) > 0: + _merge_section_value(sections["基本信息"], "页面主标题", header_text) + pairs.append(("页面主标题", header_text)) + + if page_type == "cn": + for label_text, value_text in _extract_cn_basic_summary_pairs(soup): + pairs.append((label_text, value_text)) + _merge_section_value(sections["基本信息"], label_text, value_text) + + structured_result = _parse_structured_detail_container(soup=soup, page_type=page_type) + if structured_result is not None: + _merge_sections(sections, structured_result["sections"]) + pairs.extend(structured_result["pairs"]) + _extract_page_specific_title_info(page_type=page_type, pairs=pairs, sections=sections) + return { + "sections": sections, + "pairs": pairs, + } + + table_pairs = _extract_pairs_from_tables(soup) + text_pairs = _extract_pairs_from_text_nodes(soup) + definition_pairs = _extract_pairs_from_definitions(soup) + + seen_pairs = set() + for label, value in table_pairs + definition_pairs + text_pairs: + normalized_label = _normalize_text_for_match(label) + normalized_value = _normalize_text_for_match(value) + pair_key = (normalized_label, normalized_value) + if pair_key in seen_pairs: + continue + seen_pairs.add(pair_key) + pairs.append((label, value)) + section_name = _classify_section_by_label(label) + _merge_section_value(sections[section_name], label, value) + + heading_blocks = _extract_heading_blocks(soup) + for section_name, block_data in heading_blocks.items(): + current_section = sections[section_name] + text_blocks = block_data.get("text_blocks", []) + if len(text_blocks) > 0: + _merge_section_special_list(current_section, "_text_blocks", text_blocks) + links = block_data.get("links", []) + if len(links) > 0: + _merge_section_special_list(current_section, "_links", links) + + _extract_named_product_lists(soup, sections) + _extract_page_specific_title_info(page_type=page_type, pairs=pairs, sections=sections) + + return { + "sections": sections, + "pairs": pairs, + } + + +def _parse_structured_detail_container( + soup: BeautifulSoup, + page_type: str, +) -> Optional[Dict[str, Any]]: + """ + 功能: + 优先解析 ChemicalBook 详情主容器, 避免将供应商报价和登录区误当作主数据. + 参数: + soup: BeautifulSoup, 页面 DOM. + page_type: str, 页面类型, cn 或 en. + 返回: + Optional[Dict[str, Any]], 命中结构化详情容器时返回 sections 与 pairs, 否则返回 None. + """ + if page_type == "cn": + container = soup.find(id="SubClass") + if container is not None: + return _parse_cn_detail_container(container) + return None + + container = soup.find(id="ContentPlaceHolder1_SubClass") + if container is not None: + return _parse_en_detail_container(container) + return None + + +def _parse_cn_detail_container(container: Tag) -> Dict[str, Any]: + """ + 功能: + 解析中文页详情主容器中的 section 结构. + 参数: + container: Tag, id=SubClass 的详情容器. + 返回: + Dict[str, Any], 包含 sections 与 pairs. + """ + sections = _build_empty_sections() + pairs: List[Tuple[str, str]] = [] + + section_blocks = container.find_all("div", class_="sxlist", recursive=False) + for block in section_blocks: + heading_tag = block.find("h2") + if heading_tag is None: + continue + + raw_heading = _clean_text(heading_tag.get_text(" ", strip=True)) + mapped_section = _map_cn_section_heading(raw_heading) + if mapped_section is None: + continue + + for text_label, text_value in _extract_cn_block_pairs(block): + if len(text_label) == 0 or len(text_value) == 0: + continue + pairs.append((text_label, text_value)) + _merge_section_value(sections[mapped_section], text_label, text_value) + if mapped_section == "用途与合成方法": + _merge_section_special_list(sections[mapped_section], "_text_blocks", [text_value]) + + if mapped_section == "上下游产品信息": + upstream_items = _extract_cn_product_links(block=block, label_keywords=["上游产品", "上游原料"]) + downstream_items = _extract_cn_product_links(block=block, label_keywords=["下游产品"]) + if len(upstream_items) > 0: + _merge_section_value(sections[mapped_section], "上游原料", upstream_items) + for item in upstream_items: + pairs.append(("上游原料", item)) + if len(downstream_items) > 0: + _merge_section_value(sections[mapped_section], "下游产品", downstream_items) + for item in downstream_items: + pairs.append(("下游产品", item)) + return { + "sections": sections, + "pairs": pairs, + } + + +def _extract_cn_basic_summary_pairs(soup: BeautifulSoup) -> List[Tuple[str, str]]: + """ + 功能: + 从中文页顶部 Basicsl 区域提取基础摘要键值对. + 该区域位于 SubClass 之前, 若只解析详情 section 会漏掉中文名称等核心字段. + 参数: + soup: BeautifulSoup, 页面 DOM. + 返回: + List[Tuple[str, str]], 摘要区域的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + basics_container = soup.find("div", class_="Basicsl") + if basics_container is None: + return pairs + + seen_pairs = set() + for row_tag in basics_container.find_all("div", recursive=False): + label_tag = row_tag.find("span") + if label_tag is None: + continue + + label_text = _clean_text(label_tag.get_text(" ", strip=True)) + value_text = _extract_text_after_node(label_tag, row_tag) + if len(label_text) == 0 or len(value_text) == 0: + continue + + pair_key = (_normalize_text_for_match(label_text), _normalize_text_for_match(value_text)) + if pair_key in seen_pairs: + continue + seen_pairs.add(pair_key) + pairs.append((label_text, value_text)) + + return pairs + + +def _parse_en_detail_container(container: Tag) -> Dict[str, Any]: + """ + 功能: + 解析英文页详情主容器中的 section 表格结构. + 参数: + container: Tag, id=ContentPlaceHolder1_SubClass 的详情容器. + 返回: + Dict[str, Any], 包含 sections 与 pairs. + """ + sections = _build_empty_sections() + pairs: List[Tuple[str, str]] = [] + + tables = container.find_all("table", recursive=False) + for table in tables: + raw_heading = _extract_en_table_heading(table) + mapped_section = _map_en_section_heading(raw_heading) + if mapped_section is None: + continue + + rows = table.find_all("tr", recursive=False) + for row_idx, row in enumerate(rows): + if row_idx == 0: + continue + cells = row.find_all("td", recursive=False) + if len(cells) == 0: + continue + content_cell = cells[0] + label_tag = content_cell.find("font") + if label_tag is None: + continue + label_text = _clean_text(_strip_en_bracket_label(label_tag.get_text(" ", strip=True))) + value_text = _extract_text_after_node(label_tag, content_cell) + if len(label_text) == 0 or len(value_text) == 0: + continue + + pairs.append((label_text, value_text)) + _merge_section_value(sections[mapped_section], label_text, value_text) + + if mapped_section == "用途与合成方法": + _merge_section_special_list(sections[mapped_section], "_text_blocks", [value_text]) + + if mapped_section == "上下游产品信息": + if "raw materials" in label_text.lower(): + product_items = _collect_anchor_texts(content_cell) + if len(product_items) == 0: + product_items = _split_multi_value_text(value_text) + if len(product_items) > 0: + _merge_section_value(sections[mapped_section], "Raw materials", product_items) + for item in product_items: + pairs.append(("Raw materials", item)) + if "preparation products" in label_text.lower() or "downstream products" in label_text.lower(): + product_items = _collect_anchor_texts(content_cell) + if len(product_items) == 0: + product_items = _split_multi_value_text(value_text) + if len(product_items) > 0: + _merge_section_value(sections[mapped_section], "Preparation Products", product_items) + for item in product_items: + pairs.append(("Preparation Products", item)) + + return { + "sections": sections, + "pairs": pairs, + } + + +def _map_cn_section_heading(heading: str) -> Optional[str]: + """ + 功能: + 将中文页 section 标题映射到统一输出 section. + 参数: + heading: str, 中文页原始标题. + 返回: + Optional[str], 目标 section 名称, None 表示忽略该标题. + """ + if heading == "基本信息": + return "基本信息" + if heading == "物理化学性质": + return "物理化学性质" + if heading in ("安全数据", "化学品安全说明书(MSDS)", "毒性防护", "包装储运"): + return "安全信息" + if "安全特性" in heading or "毒性" in heading and "储运" in heading: + return "安全信息" + if heading in ("应用领域", "制备方法", "常见问题列表"): + return "用途与合成方法" + if heading == "上下游产品信息": + return "上下游产品信息" + return None + + +def _map_en_section_heading(heading: str) -> Optional[str]: + """ + 功能: + 将英文页 section 标题映射到统一输出 section. + 参数: + heading: str, 英文页原始标题. + 返回: + Optional[str], 目标 section 名称, None 表示忽略该标题. + """ + lower_heading = heading.lower() + if lower_heading == "identification": + return "基本信息" + if lower_heading == "chemical properties": + return "物理化学性质" + if lower_heading in ("hazard information", "safety data", "material safety data sheet(msds)"): + return "安全信息" + if lower_heading == "raw materials and preparation products": + return "上下游产品信息" + if lower_heading == "questions and answer": + return "用途与合成方法" + return None + + +def _extract_cn_block_pairs(block: Tag) -> List[Tuple[str, str]]: + """ + 功能: + 从中文页单个 sxlist 块中提取键值对. + 参数: + block: Tag, 单个 sxlist 容器. + 返回: + List[Tuple[str, str]], 该 section 提取出的键值对. + """ + pairs: List[Tuple[str, str]] = [] + + for property_row in block.select(".xztable .xztr"): + label_tag = property_row.find("span") + if label_tag is None: + continue + label_text = _clean_text(label_tag.get_text(" ", strip=True)) + value_text = _extract_text_after_node(label_tag, property_row) + if len(label_text) == 0 or len(value_text) == 0: + continue + pairs.append((label_text, value_text)) + + for content_block in block.find_all("div", class_="cwb", recursive=False): + direct_children = [child for child in content_block.children if isinstance(child, Tag)] + pending_label = "" + for child in direct_children: + classes = child.get("class", []) + if "tbt" in classes: + pending_label = _clean_text(child.get_text(" ", strip=True)) + continue + if len(pending_label) == 0: + continue + value_text = _clean_text(child.get_text("\n", strip=True)) + if len(value_text) > 0: + pairs.append((pending_label, value_text)) + pending_label = "" + + for msds_block in block.find_all("div", class_="MSDS", recursive=False): + label_text = "" + header_span = msds_block.find("span") + if header_span is not None: + label_text = _clean_text(header_span.get_text(" ", strip=True)) + if len(label_text) == 0: + label_text = "MSDS 信息" + for anchor_text in _collect_anchor_texts(msds_block): + pairs.append((label_text, anchor_text)) + + return pairs + + +def _extract_cn_product_links(block: Tag, label_keywords: List[str]) -> List[str]: + """ + 功能: + 从中文页上下游 section 中提取指定标题下的链接文本. + 参数: + block: Tag, 上下游 section 容器. + label_keywords: List[str], 标题关键词列表. + 返回: + List[str], 产品名称列表. + """ + product_items: List[str] = [] + for inner_block in block.find_all("div", class_="tyc", recursive=False): + title_tag = inner_block.find("div", class_="tbt") + if title_tag is None: + continue + title_text = _clean_text(title_tag.get_text(" ", strip=True)) + if not any(keyword in title_text for keyword in label_keywords): + continue + _extend_unique(product_items, _collect_anchor_texts(inner_block)) + return product_items + + +def _extract_en_table_heading(table: Tag) -> str: + """ + 功能: + 从英文页详情表格首行提取 section 标题. + 参数: + table: Tag, 单个详情表格. + 返回: + str, 标题文本. + """ + header_anchor = table.find("a", attrs={"name": True}) + if header_anchor is not None: + return _clean_text(header_anchor.get_text(" ", strip=True)) + + first_font = table.find("font") + if first_font is not None: + return _clean_text(first_font.get_text(" ", strip=True)) + return "" + + +def _strip_en_bracket_label(label: str) -> str: + """ + 功能: + 去除英文页字段标签两侧的中括号. + 参数: + label: str, 原始标签文本. + 返回: + str, 清洗后的标签文本. + """ + return label.strip("[] ") + + +def _extract_text_after_node(start_node: Tag, parent_node: Tag) -> str: + """ + 功能: + 提取父节点中指定子节点之后的文本, 保留换行分隔. + 参数: + start_node: Tag, 起始标签节点. + parent_node: Tag, 父节点. + 返回: + str, 提取到的文本内容. + """ + text_parts: List[str] = [] + for sibling in start_node.next_siblings: + if isinstance(sibling, Tag): + sibling_text = _clean_text(sibling.get_text("\n", strip=True)) + else: + sibling_text = _clean_text(str(sibling)) + if len(sibling_text) > 0: + text_parts.append(sibling_text) + + if len(text_parts) == 0: + parent_text = _clean_text(parent_node.get_text("\n", strip=True)) + start_text = _clean_text(start_node.get_text(" ", strip=True)) + if parent_text.startswith(start_text): + stripped = _clean_text(parent_text[len(start_text):]) + return stripped + return parent_text + + return _clean_text("\n".join(text_parts)) + + +def _collect_anchor_texts(node: Tag) -> List[str]: + """ + 功能: + 收集节点内所有链接文本并去重. + 参数: + node: Tag, 任意 DOM 节点. + 返回: + List[str], 链接文本列表. + """ + anchor_texts: List[str] = [] + for anchor in node.find_all("a"): + text = _clean_text(anchor.get_text(" ", strip=True)) + if len(text) > 0: + anchor_texts.append(text) + deduped: List[str] = [] + _extend_unique(deduped, anchor_texts) + return deduped + + +def _extract_pairs_from_tables(soup: BeautifulSoup) -> List[Tuple[str, str]]: + """ + 功能: + 从 table 结构中提取 label/value 对. + 参数: + soup: BeautifulSoup, 页面 DOM. + 返回: + List[Tuple[str, str]], 表格提取出的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + for row in soup.find_all("tr"): + cells = [] + for cell in row.find_all(["th", "td"]): + cell_text = _clean_text(cell.get_text(" ", strip=True)) + if len(cell_text) > 0: + cells.append(cell_text) + + if len(cells) < 2: + continue + + idx = 0 + while idx + 1 < len(cells): + label = cells[idx] + value = cells[idx + 1] + if _looks_like_label(label) is True and len(value) > 0: + pairs.append((label, value)) + idx += 2 + return pairs + + +def _extract_pairs_from_definitions(soup: BeautifulSoup) -> List[Tuple[str, str]]: + """ + 功能: + 从 dl/dt/dd 结构中提取 label/value 对. + 参数: + soup: BeautifulSoup, 页面 DOM. + 返回: + List[Tuple[str, str]], 定义列表提取结果. + """ + pairs: List[Tuple[str, str]] = [] + for definition in soup.find_all("dl"): + labels = definition.find_all("dt") + values = definition.find_all("dd") + pair_count = min(len(labels), len(values)) + for idx in range(pair_count): + label_text = _clean_text(labels[idx].get_text(" ", strip=True)) + value_text = _clean_text(values[idx].get_text(" ", strip=True)) + if len(label_text) == 0 or len(value_text) == 0: + continue + pairs.append((label_text, value_text)) + return pairs + + +def _extract_pairs_from_text_nodes(soup: BeautifulSoup) -> List[Tuple[str, str]]: + """ + 功能: + 从列表项, 段落等文本节点中提取 label:value 形式的键值对. + 参数: + soup: BeautifulSoup, 页面 DOM. + 返回: + List[Tuple[str, str]], 文本节点提取出的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + seen_texts = set() + candidate_tags = soup.find_all(["li", "p", "span", "div"]) + + for tag in candidate_tags: + if tag.find(["table", "tr", "td", "th", "dl", "dt", "dd"]) is not None: + continue + + text = _clean_text(tag.get_text(" ", strip=True)) + if len(text) == 0: + continue + + normalized_text = _normalize_text_for_match(text) + if normalized_text in seen_texts: + continue + seen_texts.add(normalized_text) + + pair = _split_label_value_text(text) + if pair is None: + continue + pairs.append(pair) + + return pairs + + +def _extract_heading_blocks(soup: BeautifulSoup) -> Dict[str, Dict[str, List[str]]]: + """ + 功能: + 基于页面标题节点提取 section 文本块与链接列表. + 参数: + soup: BeautifulSoup, 页面 DOM. + 返回: + Dict[str, Dict[str, List[str]]], section 到文本块/链接的映射. + """ + block_map: Dict[str, Dict[str, List[str]]] = {} + for section_name in DEFAULT_SECTIONS: + block_map[section_name] = {"text_blocks": [], "links": []} + + heading_tags = soup.find_all(["h1", "h2", "h3", "h4", "strong", "b", "span", "div", "td", "th"]) + for tag in heading_tags: + heading_text = _clean_text(tag.get_text(" ", strip=True)) + if len(heading_text) == 0 or len(heading_text) > 40: + continue + + matched_section = _match_section_by_heading(heading_text) + if matched_section is None: + continue + + base_node = tag.parent if isinstance(tag.parent, Tag) else tag + current = base_node.next_sibling + section_texts: List[str] = [] + section_links: List[str] = [] + sibling_count = 0 + + while current is not None and sibling_count < 15: + sibling_count += 1 + if isinstance(current, Tag) is False: + current = current.next_sibling + continue + + current_text = _clean_text(current.get_text(" ", strip=True)) + if len(current_text) > 0: + next_section = _match_section_by_heading(current_text) + if next_section is not None and next_section != matched_section and len(current_text) <= 40: + break + if current_text != heading_text: + section_texts.append(current_text) + + for anchor in current.find_all("a"): + anchor_text = _clean_text(anchor.get_text(" ", strip=True)) + if len(anchor_text) > 0: + section_links.append(anchor_text) + + current = current.next_sibling + + if len(section_texts) > 0: + _extend_unique(block_map[matched_section]["text_blocks"], section_texts) + if len(section_links) > 0: + _extend_unique(block_map[matched_section]["links"], section_links) + return block_map + + +def _extract_named_product_lists(soup: BeautifulSoup, sections: Dict[str, Dict[str, Any]]) -> None: + """ + 功能: + 从含有上游产品, 下游产品等标题的节点附近提取产品名称列表. + 参数: + soup: BeautifulSoup, 页面 DOM. + sections: Dict[str, Dict[str, Any]], 当前 sections 容器. + 返回: + None. + """ + product_heading_map = { + "上游产品": "upstream_products", + "上游原料": "upstream_products", + "下游产品": "downstream_products", + "Upstream products": "upstream_products", + "Downstream products": "downstream_products", + } + + for tag in soup.find_all(["h2", "h3", "h4", "strong", "b", "div", "span", "td"]): + heading_text = _clean_text(tag.get_text(" ", strip=True)) + if len(heading_text) == 0 or len(heading_text) > 40: + continue + + field_name = None + for candidate, mapped_field in product_heading_map.items(): + if candidate.lower() in heading_text.lower(): + field_name = mapped_field + break + if field_name is None: + continue + + section_name = FIELD_SECTION_MAP[field_name] + product_items: List[str] = [] + base_node = tag.parent if isinstance(tag.parent, Tag) else tag + current = base_node.next_sibling + sibling_count = 0 + while current is not None and sibling_count < 10: + sibling_count += 1 + if isinstance(current, Tag) is False: + current = current.next_sibling + continue + + current_text = _clean_text(current.get_text(" ", strip=True)) + if len(current_text) == 0: + current = current.next_sibling + continue + + matched_section = _match_section_by_heading(current_text) + if matched_section is not None and len(current_text) <= 40: + break + + for anchor in current.find_all("a"): + anchor_text = _clean_text(anchor.get_text(" ", strip=True)) + if len(anchor_text) > 0: + product_items.append(anchor_text) + + if len(product_items) == 0: + split_items = _split_multi_value_text(current_text) + if len(split_items) > 0: + product_items.extend(split_items) + + current = current.next_sibling + + if len(product_items) > 0: + _merge_section_value(sections[section_name], field_name, product_items) + + +def _extract_page_specific_title_info( + page_type: str, + pairs: List[Tuple[str, str]], + sections: Dict[str, Dict[str, Any]], +) -> None: + """ + 功能: + 使用页面标题类字段补强中英文名称. + 参数: + page_type: str, 页面类型. + pairs: List[Tuple[str, str]], 当前页面键值对列表. + sections: Dict[str, Dict[str, Any]], 当前 sections 容器. + 返回: + None. + """ + title_text = "" + basic_section = sections["基本信息"] + if "页面主标题" in basic_section: + title_text = _coerce_to_text(basic_section["页面主标题"]) + elif "页面标题" in basic_section: + title_text = _coerce_to_text(basic_section["页面标题"]) + + if len(title_text) == 0: + return + + if page_type == "cn": + if re.search(r"[\u4e00-\u9fff]", title_text) is not None: + pairs.append(("中文名称", title_text)) + else: + cleaned_title = title_text.split("|")[0].split("CAS")[0].strip(" -") + if len(cleaned_title) > 0: + pairs.append(("Name", cleaned_title)) + + +def _parse_chemicalbook_page_fallback(html: str, page_type: str) -> Dict[str, Any]: + """ + 功能: + 在未安装 beautifulsoup4 时, 使用标准库降级解析 HTML. + 该路径主要保证离线测试与基础抓取可运行, 解析稳定性低于 bs4 主路径. + 参数: + html: str, 页面 HTML. + page_type: str, 页面类型. + 返回: + Dict[str, Any], 包含 sections 与 pairs. + """ + sections = _build_empty_sections() + pairs: List[Tuple[str, str]] = [] + + title_text = _extract_first_tag_text(html=html, tag_name="title") + if len(title_text) > 0: + _merge_section_value(sections["基本信息"], "页面标题", title_text) + pairs.append(("页面标题", title_text)) + + h1_text = _extract_first_tag_text(html=html, tag_name="h1") + if len(h1_text) > 0: + _merge_section_value(sections["基本信息"], "页面主标题", h1_text) + pairs.append(("页面主标题", h1_text)) + + if page_type == "cn": + for label_text, value_text in _fallback_extract_cn_basic_summary_pairs(html): + pairs.append((label_text, value_text)) + _merge_section_value(sections["基本信息"], label_text, value_text) + + seen_pairs = set() + extracted_pairs = [] + extracted_pairs.extend(_fallback_extract_table_pairs(html)) + extracted_pairs.extend(_fallback_extract_definition_pairs(html)) + extracted_pairs.extend(_fallback_extract_text_pairs(html)) + + for label, value in extracted_pairs: + pair_key = (_normalize_text_for_match(label), _normalize_text_for_match(value)) + if pair_key in seen_pairs: + continue + seen_pairs.add(pair_key) + pairs.append((label, value)) + section_name = _classify_section_by_label(label) + _merge_section_value(sections[section_name], label, value) + + heading_blocks = _fallback_extract_heading_blocks(html) + for section_name, block_data in heading_blocks.items(): + if len(block_data["text_blocks"]) > 0: + _merge_section_special_list(sections[section_name], "_text_blocks", block_data["text_blocks"]) + if len(block_data["links"]) > 0: + _merge_section_special_list(sections[section_name], "_links", block_data["links"]) + + _fallback_extract_named_product_lists(html=html, sections=sections) + _extract_page_specific_title_info(page_type=page_type, pairs=pairs, sections=sections) + return { + "sections": sections, + "pairs": pairs, + } + + +def _extract_first_tag_text(html: str, tag_name: str) -> str: + """ + 功能: + 从 HTML 中提取指定标签的首个文本内容. + 参数: + html: str, 页面 HTML. + tag_name: str, 标签名称. + 返回: + str, 标签文本. + """ + pattern = re.compile( + rf"<{tag_name}\b[^>]*>(.*?)", + re.IGNORECASE | re.DOTALL, + ) + match = pattern.search(html) + if match is None: + return "" + return _clean_text(html_lib.unescape(_strip_html_tags(match.group(1)))) + + +def _fallback_extract_cn_basic_summary_pairs(html: str) -> List[Tuple[str, str]]: + """ + 功能: + 在降级解析路径中, 从中文页顶部 Basicsl 区域提取中文名称等基础摘要字段. + 参数: + html: str, 页面 HTML. + 返回: + List[Tuple[str, str]], 摘要区域的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + block_match = re.search( + r'
]*>(.*?)
\s*', + html, + re.IGNORECASE | re.DOTALL, + ) + if block_match is None: + return pairs + + seen_pairs = set() + row_pattern = re.compile( + r"]*>\s*([^<]+)(.*?)", + re.IGNORECASE | re.DOTALL, + ) + for label_html, value_html in row_pattern.findall(block_match.group(1)): + label_text = _clean_text(html_lib.unescape(_strip_html_tags(label_html))) + value_text = _clean_text(html_lib.unescape(_strip_html_tags(value_html))) + if len(label_text) == 0 or len(value_text) == 0: + continue + + pair_key = (_normalize_text_for_match(label_text), _normalize_text_for_match(value_text)) + if pair_key in seen_pairs: + continue + seen_pairs.add(pair_key) + pairs.append((label_text, value_text)) + + return pairs + + +def _fallback_extract_table_pairs(html: str) -> List[Tuple[str, str]]: + """ + 功能: + 使用正则从 table/tr/td 结构中提取键值对. + 参数: + html: str, 页面 HTML. + 返回: + List[Tuple[str, str]], 表格中的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + for row_html in re.findall(r"]*>.*?", html, re.IGNORECASE | re.DOTALL): + cells = re.findall(r"]*>(.*?)", row_html, re.IGNORECASE | re.DOTALL) + cell_texts = [] + for cell_html in cells: + cell_text = _clean_text(html_lib.unescape(_strip_html_tags(cell_html))) + if len(cell_text) > 0: + cell_texts.append(cell_text) + + if len(cell_texts) < 2: + continue + + idx = 0 + while idx + 1 < len(cell_texts): + label = cell_texts[idx] + value = cell_texts[idx + 1] + if _looks_like_label(label) is True and len(value) > 0: + pairs.append((label, value)) + idx += 2 + return pairs + + +def _fallback_extract_definition_pairs(html: str) -> List[Tuple[str, str]]: + """ + 功能: + 使用正则从 dt/dd 结构中提取键值对. + 参数: + html: str, 页面 HTML. + 返回: + List[Tuple[str, str]], 定义列表中的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + pattern = re.compile( + r"]*>(.*?)\s*]*>(.*?)", + re.IGNORECASE | re.DOTALL, + ) + for label_html, value_html in pattern.findall(html): + label_text = _clean_text(html_lib.unescape(_strip_html_tags(label_html))) + value_text = _clean_text(html_lib.unescape(_strip_html_tags(value_html))) + if len(label_text) == 0 or len(value_text) == 0: + continue + pairs.append((label_text, value_text)) + return pairs + + +def _fallback_extract_text_pairs(html: str) -> List[Tuple[str, str]]: + """ + 功能: + 使用正则从常见块级标签中提取 label:value 文本键值对. + 参数: + html: str, 页面 HTML. + 返回: + List[Tuple[str, str]], 提取出的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + seen_texts = set() + block_pattern = re.compile( + r"<(li|p|span|div)\b[^>]*>(.*?)", + re.IGNORECASE | re.DOTALL, + ) + for _, block_html in block_pattern.findall(html): + text = _clean_text(html_lib.unescape(_strip_html_tags(block_html))) + if len(text) == 0: + continue + normalized = _normalize_text_for_match(text) + if normalized in seen_texts: + continue + seen_texts.add(normalized) + pair = _split_label_value_text(text) + if pair is not None: + pairs.append(pair) + return pairs + + +def _fallback_extract_heading_blocks(html: str) -> Dict[str, Dict[str, List[str]]]: + """ + 功能: + 使用顺序块扫描的方式提取标题 section 与其后续文本. + 参数: + html: str, 页面 HTML. + 返回: + Dict[str, Dict[str, List[str]]], section 文本块与链接列表. + """ + block_map: Dict[str, Dict[str, List[str]]] = {} + for section_name in DEFAULT_SECTIONS: + block_map[section_name] = {"text_blocks": [], "links": []} + + blocks = _fallback_iter_blocks(html) + current_section = None + for block in blocks: + text = block["text"] + matched_section = _match_section_by_heading(text) + if matched_section is not None and len(text) <= 40: + current_section = matched_section + continue + if current_section is None: + continue + if block["tag"] == "a": + _extend_unique(block_map[current_section]["links"], [text]) + else: + _extend_unique(block_map[current_section]["text_blocks"], [text]) + return block_map + + +def _fallback_extract_named_product_lists(html: str, sections: Dict[str, Dict[str, Any]]) -> None: + """ + 功能: + 在降级解析路径中提取上游与下游产品列表. + 参数: + html: str, 页面 HTML. + sections: Dict[str, Dict[str, Any]], 当前 sections 容器. + 返回: + None. + """ + blocks = _fallback_iter_blocks(html) + current_field = None + for block in blocks: + text = block["text"] + lower_text = text.lower() + if "上游产品" in text or "上游原料" in text or "upstream products" in lower_text: + current_field = "upstream_products" + continue + if "下游产品" in text or "downstream products" in lower_text: + current_field = "downstream_products" + continue + if _match_section_by_heading(text) is not None and len(text) <= 40: + current_field = None + continue + if current_field is None: + continue + + values = [] + if block["tag"] == "a": + values = [text] + else: + values = _split_multi_value_text(text) + if len(values) == 1 and values[0] == text: + whitespace_parts = [part.strip() for part in text.split() if len(part.strip()) > 0] + if len(whitespace_parts) > 1: + values = whitespace_parts + if len(values) > 0: + section_name = FIELD_SECTION_MAP[current_field] + _merge_section_value(sections[section_name], current_field, values) + + +def _fallback_iter_blocks(html: str) -> List[Dict[str, str]]: + """ + 功能: + 将常见 HTML 标签顺序展开为简单块列表, 供降级解析路径使用. + 参数: + html: str, 页面 HTML. + 返回: + List[Dict[str, str]], 顺序块列表. + """ + blocks: List[Dict[str, str]] = [] + pattern = re.compile( + r"<(h1|h2|h3|h4|div|p|li|span|a|td|th)\b[^>]*>(.*?)", + re.IGNORECASE | re.DOTALL, + ) + for tag_name, block_html in pattern.findall(html): + text = _clean_text(html_lib.unescape(_strip_html_tags(block_html))) + if len(text) == 0: + continue + blocks.append({"tag": tag_name.lower(), "text": text}) + return blocks + + +def _normalize_from_pairs_and_sections( + cas: str, + pairs: List[Tuple[str, str]], + sections: Dict[str, Dict[str, Any]], +) -> Dict[str, Any]: + """ + 功能: + 将原始标签值对与 section 内容标准化为稳定 JSON 字段. + 参数: + cas: str, CAS 号. + pairs: List[Tuple[str, str]], 原始标签值对. + sections: Dict[str, Dict[str, Any]], 原始 section 数据. + 返回: + Dict[str, Any], 标准化结果. + """ + normalized = _build_empty_normalized(cas) + alias_to_field = _build_label_to_field_map() + + for label, value in pairs: + clean_label = _normalize_text_for_match(label) + field_name = alias_to_field.get(clean_label) + if field_name is None: + continue + + clean_value = _coerce_to_text(value) + if len(clean_value) == 0: + continue + + if field_name in ("aliases", "hazard_statements", "safety_statements", "uses"): + _extend_unique(normalized[field_name], _split_multi_value_text(clean_value)) + continue + + if field_name in ("upstream_products", "downstream_products"): + _extend_unique(normalized[field_name], _split_multi_value_text(clean_value)) + continue + + if field_name in ("density", "melting_point", "boiling_point", "flash_point"): + _update_measurement_field(field=normalized[field_name], raw_text=clean_value, field_name=field_name) + continue + + if field_name == "molecular_weight": + parsed_weight = _parse_first_float(clean_value) + if parsed_weight is not None and normalized["molecular_weight"] is None: + normalized["molecular_weight"] = parsed_weight + continue + + if field_name in ("cn_name", "en_name", "molecular_formula", "appearance", "solubility", "storage_conditions"): + if len(str(normalized[field_name]).strip()) == 0: + normalized[field_name] = clean_value + + use_section = sections.get("用途与合成方法", {}) + _merge_text_block_field(normalized["uses"], use_section.get("_text_blocks")) + + relation_section = sections.get("上下游产品信息", {}) + _merge_text_block_field(normalized["upstream_products"], relation_section.get("upstream_products")) + _merge_text_block_field(normalized["downstream_products"], relation_section.get("downstream_products")) + + relation_links = relation_section.get("_links") + if isinstance(relation_links, list) is True: + downstream_items = relation_section.get("downstream_products") + if downstream_items is None: + _merge_text_block_field(normalized["downstream_products"], relation_links) + + safety_section = sections.get("安全信息", {}) + _merge_text_block_field(normalized["hazard_statements"], safety_section.get("危险说明")) + _merge_text_block_field(normalized["safety_statements"], safety_section.get("安全说明")) + + if len(normalized["en_name"]) == 0 and len(normalized["cn_name"]) > 0: + title_from_basic = sections.get("基本信息", {}).get("页面标题") + if title_from_basic is not None: + title_text = _coerce_to_text(title_from_basic) + english_candidate = re.split(r"[||]", title_text)[0].strip() + if re.search(r"[A-Za-z]", english_candidate) is not None: + normalized["en_name"] = english_candidate + + return normalized + + +def _update_measurement_field(field: Dict[str, Any], raw_text: str, field_name: str) -> None: + """ + 功能: + 更新数值型标准字段, 保留原始文本并尽量解析数值. + 参数: + field: Dict[str, Any], 目标 measurement 字段. + raw_text: str, 原始文本. + field_name: str, 字段名称. + 返回: + None. + """ + if len(str(field.get("raw", "")).strip()) == 0: + field["raw"] = raw_text + + if field.get("value") is not None: + return + + if field_name == "density": + value = _parse_density_value(raw_text) + if value is not None: + field["value"] = value + return + + value = _parse_temperature_value(raw_text) + if value is not None: + field["value"] = value + + +def _collect_pairs_from_sections(sections: Dict[str, Any]) -> List[Tuple[str, str]]: + """ + 功能: + 从 sections 中回收可标准化的键值对, 供 normalize_chemicalbook_record 使用. + 参数: + sections: Dict[str, Any], sections 数据. + 返回: + List[Tuple[str, str]], 扁平化后的键值对列表. + """ + pairs: List[Tuple[str, str]] = [] + for section_value in sections.values(): + if isinstance(section_value, dict) is False: + continue + for label, value in section_value.items(): + if label.startswith("_"): + continue + if isinstance(value, list): + for item in value: + item_text = _coerce_to_text(item) + if len(item_text) > 0: + pairs.append((label, item_text)) + else: + value_text = _coerce_to_text(value) + if len(value_text) > 0: + pairs.append((label, value_text)) + return pairs + + +def _determine_record_status(record: Dict[str, Any], page_results: List[PageFetchResult]) -> str: + """ + 功能: + 根据页面抓取与解析结果确定最终状态. + 参数: + record: Dict[str, Any], 当前结果记录. + page_results: List[PageFetchResult], 页面抓取结果列表. + 返回: + str, ok / parse_partial / not_found / blocked / error. + """ + has_html = False + has_not_found = False + has_error = False + has_blocked = False + for page_result in page_results: + if page_result.html is not None: + has_html = True + if page_result.status_code in (404, 410): + has_not_found = True + if page_result.blocked is True: + has_blocked = True + if page_result.error_message != "" and page_result.html is None and page_result.status_code is None: + has_error = True + + normalized = record.get("normalized", {}) + normalized_hit_count = _count_normalized_hits(normalized) + section_content_count = _count_section_content(record.get("sections", {})) + + if has_html is False and has_not_found is True: + return "not_found" + + if has_html is False and has_blocked is True: + return "blocked" + + if has_html is False and has_error is True: + return "error" + + if normalized_hit_count == 0 and section_content_count == 0: + if has_blocked is True: + return "blocked" + if has_not_found is True: + return "not_found" + return "error" + + missing_page_count = sum(1 for page_result in page_results if page_result.html is None) + if missing_page_count > 0: + return "parse_partial" + + if normalized_hit_count < 4: + return "parse_partial" + + return "ok" + + +def _count_normalized_hits(normalized: Dict[str, Any]) -> int: + """ + 功能: + 统计 normalized 中命中的有效字段数量. + 参数: + normalized: Dict[str, Any], 标准化结果. + 返回: + int, 命中字段数. + """ + hit_count = 0 + for field_name, value in normalized.items(): + if field_name == "cas": + continue + if isinstance(value, dict): + raw_text = str(value.get("raw", "")).strip() + numeric_value = value.get("value") + if len(raw_text) > 0 or numeric_value is not None: + hit_count += 1 + continue + if isinstance(value, list): + if len(value) > 0: + hit_count += 1 + continue + if value is not None and len(str(value).strip()) > 0: + hit_count += 1 + return hit_count + + +def _count_section_content(sections: Dict[str, Any]) -> int: + """ + 功能: + 统计 sections 中包含内容的字段数量. + 参数: + sections: Dict[str, Any], sections 结构. + 返回: + int, 有效字段数量. + """ + item_count = 0 + for section in sections.values(): + if isinstance(section, dict) is False: + continue + for value in section.values(): + if isinstance(value, list): + if len(value) > 0: + item_count += 1 + elif value is not None and len(str(value).strip()) > 0: + item_count += 1 + return item_count + + +def _remove_noise_nodes(soup: BeautifulSoup) -> None: + """ + 功能: + 移除 script, style 等无关节点, 降低解析噪声. + 参数: + soup: BeautifulSoup, 页面 DOM. + 返回: + None. + """ + for tag in soup.find_all(["script", "style", "noscript", "iframe"]): + tag.decompose() + + +def _classify_section_by_label(label: str) -> str: + """ + 功能: + 根据标签名将字段归类到预定义 section. + 参数: + label: str, 原始标签名. + 返回: + str, section 名称. + """ + alias_to_field = _build_label_to_field_map() + field_name = alias_to_field.get(_normalize_text_for_match(label)) + if field_name is None: + return "基本信息" + return FIELD_SECTION_MAP.get(field_name, "基本信息") + + +def _build_label_to_field_map() -> Dict[str, str]: + """ + 功能: + 将中英文标签别名构建为可直接查找的映射. + 参数: + 无. + 返回: + Dict[str, str], 规范化标签到字段名的映射. + """ + label_to_field: Dict[str, str] = {} + for field_name, aliases in FIELD_ALIASES.items(): + for alias in aliases: + label_to_field[_normalize_text_for_match(alias)] = field_name + return label_to_field + + +def _merge_sections(target: Dict[str, Dict[str, Any]], incoming: Dict[str, Dict[str, Any]]) -> None: + """ + 功能: + 将单页解析结果合并进最终 sections, 避免覆盖已有信息. + 参数: + target: Dict[str, Dict[str, Any]], 目标 sections. + incoming: Dict[str, Dict[str, Any]], 单页 sections. + 返回: + None. + """ + for section_name, values in incoming.items(): + if section_name not in target: + target[section_name] = {} + for label, value in values.items(): + if label.startswith("_"): + _merge_section_special_list(target[section_name], label, value) + continue + _merge_section_value(target[section_name], label, value) + + +def _merge_section_value(section: Dict[str, Any], label: str, value: Any) -> None: + """ + 功能: + 向 section 中写入字段, 重复标签时自动合并为列表并去重. + 参数: + section: Dict[str, Any], 目标 section. + label: str, 标签名. + value: Any, 标签值. + 返回: + None. + """ + if label not in section: + section[label] = value + return + + existing = section[label] + merged_items: List[str] = [] + _extend_unique(merged_items, _coerce_to_list(existing)) + _extend_unique(merged_items, _coerce_to_list(value)) + section[label] = merged_items + + +def _merge_section_special_list(section: Dict[str, Any], key: str, values: Any) -> None: + """ + 功能: + 合并 section 中的特殊列表字段, 例如 _text_blocks 与 _links. + 参数: + section: Dict[str, Any], 目标 section. + key: str, 特殊字段名. + values: Any, 待合并值. + 返回: + None. + """ + if key not in section: + section[key] = [] + _extend_unique(section[key], _coerce_to_list(values)) + + +def _match_section_by_heading(text: str) -> Optional[str]: + """ + 功能: + 根据标题文本匹配预定义 section. + 参数: + text: str, 标题文本. + 返回: + Optional[str], 命中的 section 名称, 未命中返回 None. + """ + normalized = text.lower().strip() + for section_name, keywords in SECTION_KEYWORDS.items(): + for keyword in keywords: + if keyword.lower() in normalized: + return section_name + return None + + +def _merge_text_block_field(target_list: List[str], raw_value: Any) -> None: + """ + 功能: + 将单值或多值文本安全地合并到目标列表. + 参数: + target_list: List[str], 目标列表. + raw_value: Any, 待合并值. + 返回: + None. + """ + if raw_value is None: + return + if isinstance(raw_value, list): + values = raw_value + else: + values = [raw_value] + cleaned_values = [] + for item in values: + item_text = _coerce_to_text(item) + if len(item_text) > 0: + cleaned_values.append(item_text) + _extend_unique(target_list, cleaned_values) + + +def _looks_like_label(text: str) -> bool: + """ + 功能: + 判断一段文本是否更像属性标签而非普通段落. + 参数: + text: str, 待判断文本. + 返回: + bool, True 表示更像标签. + """ + cleaned = _clean_text(text) + if len(cleaned) == 0 or len(cleaned) > 40: + return False + + if _normalize_text_for_match(cleaned) in _build_label_to_field_map(): + return True + + chinese_chars = re.findall(r"[\u4e00-\u9fff]", cleaned) + if 0 < len(chinese_chars) <= 12: + return True + + english_word_count = len(cleaned.split()) + if 0 < english_word_count <= 5: + return True + + return False + + +def _split_label_value_text(text: str) -> Optional[Tuple[str, str]]: + """ + 功能: + 将 label:value 形式的文本拆分为键值对. + 参数: + text: str, 原始文本. + 返回: + Optional[Tuple[str, str]], 成功时返回键值对, 否则返回 None. + """ + for separator in (":", ":"): + if separator not in text: + continue + label, value = text.split(separator, 1) + label = _clean_text(label) + value = _clean_text(value) + if len(label) == 0 or len(value) == 0: + continue + if _looks_like_label(label) is False: + continue + return label, value + return None + + +def _coerce_to_text(value: Any) -> str: + """ + 功能: + 将任意值转换为清洗后的字符串. + 参数: + value: Any, 任意输入值. + 返回: + str, 清洗后的字符串. + """ + if value is None: + return "" + return _clean_text(str(value)) + + +def _coerce_to_list(value: Any) -> List[str]: + """ + 功能: + 将单值或多值输入统一转为字符串列表. + 参数: + value: Any, 任意输入值. + 返回: + List[str], 字符串列表. + """ + if value is None: + return [] + if isinstance(value, list): + result = [] + for item in value: + item_text = _coerce_to_text(item) + if len(item_text) > 0: + result.append(item_text) + return result + + value_text = _coerce_to_text(value) + if len(value_text) == 0: + return [] + return [value_text] + + +def _clean_text(text: str) -> str: + """ + 功能: + 清洗 HTML 提取出的文本, 压缩空白并去掉无意义分隔. + 参数: + text: str, 原始文本. + 返回: + str, 清洗后的文本. + """ + replaced = text.replace("\xa0", " ").replace("\u3000", " ") + replaced = replaced.replace("\r", " ").replace("\n", " ").replace("\t", " ") + replaced = re.sub(r"\s+", " ", replaced) + return replaced.strip(" |:") + + +def _normalize_text_for_match(text: str) -> str: + """ + 功能: + 将标签或文本归一化, 便于做别名匹配与去重. + 参数: + text: str, 原始文本. + 返回: + str, 归一化后的文本. + """ + cleaned = _clean_text(text).lower() + cleaned = cleaned.replace(":", ":") + cleaned = cleaned.replace("(", "(").replace(")", ")") + cleaned = re.sub(r"[\s\-_()/\[\]{}|]+", "", cleaned) + return cleaned + + +def _split_multi_value_text(text: str) -> List[str]: + """ + 功能: + 将多值文本拆分为稳定列表, 用于用途, 别名, 上下游等字段. + 参数: + text: str, 原始文本. + 返回: + List[str], 去重后的项目列表. + """ + cleaned = _clean_text(text) + if len(cleaned) == 0: + return [] + + if len(cleaned) > 120 and " " in cleaned and "。" not in cleaned and ";" not in cleaned: + return [cleaned] + + parts = re.split(r"[;;,,/、|\n]+", cleaned) + result: List[str] = [] + for part in parts: + item = _clean_text(part) + if len(item) == 0: + continue + if len(item) > 2 and item.lower() not in ("more", "details"): + result.append(item) + deduped: List[str] = [] + _extend_unique(deduped, result) + return deduped + + +def _extend_unique(target: List[str], values: Iterable[str]) -> None: + """ + 功能: + 将一组字符串按出现顺序去重后合并到目标列表. + 参数: + target: List[str], 目标列表. + values: Iterable[str], 待追加值. + 返回: + None. + """ + existing_keys = {_normalize_text_for_match(item) for item in target} + for value in values: + text = _coerce_to_text(value) + if len(text) == 0: + continue + normalized = _normalize_text_for_match(text) + if normalized in existing_keys: + continue + existing_keys.add(normalized) + target.append(text) + + +def _parse_first_float(text: str) -> Optional[float]: + """ + 功能: + 从文本中提取第一个浮点数. + 参数: + text: str, 原始文本. + 返回: + Optional[float], 提取成功返回数值, 否则返回 None. + """ + match = FLOAT_RE.search(text) + if match is None: + return None + try: + return float(match.group(0)) + except ValueError: + return None + + +def _parse_density_value(text: str) -> Optional[float]: + """ + 功能: + 解析密度文本中的数值, 默认按 g/mL 输出. + 参数: + text: str, 原始密度文本. + 返回: + Optional[float], 解析到的密度值. + """ + parsed = _parse_first_float(text) + if parsed is None: + return None + + if 0.0 < parsed < 30.0: + return round(parsed, 4) + return None + + +def _parse_temperature_value(text: str) -> Optional[float]: + """ + 功能: + 解析温度类文本中的摄氏度数值. 遇到区间时返回均值. + 参数: + text: str, 原始温度文本. + 返回: + Optional[float], 解析到的摄氏度值. + """ + range_match = CELSIUS_RANGE_RE.search(text) + if range_match is not None: + low = float(range_match.group(1)) + high = float(range_match.group(2)) + return round((low + high) / 2, 2) + + single_match = CELSIUS_RE.search(text) + if single_match is not None: + return round(float(single_match.group(1)), 2) + + range_f_match = FAHRENHEIT_RANGE_RE.search(text) + if range_f_match is not None: + low_f = float(range_f_match.group(1)) + high_f = float(range_f_match.group(2)) + avg_f = (low_f + high_f) / 2 + return round((avg_f - 32) * 5 / 9, 2) + + single_f_match = FAHRENHEIT_RE.search(text) + if single_f_match is not None: + fahrenheit = float(single_f_match.group(1)) + return round((fahrenheit - 32) * 5 / 9, 2) + + plain_value = _parse_first_float(text) + if plain_value is not None and -250.0 <= plain_value <= 1000.0: + return round(plain_value, 2) + return None + + +def _strip_html_tags(html: str) -> str: + """ + 功能: + 粗略去除 HTML 标签, 用于反爬关键词识别. + 参数: + html: str, 原始 HTML. + 返回: + str, 近似纯文本. + """ + return re.sub(r"<[^>]+>", " ", html) diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/fetch_chemicalbook.py b/unilabos/devices/eit_synthesis_station/chem_tools/fetch_chemicalbook.py new file mode 100644 index 00000000..629266a2 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/fetch_chemicalbook.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +""" +功能: + 提供 ChemicalBook 抓取命令行入口, 支持按 CAS 输出结构化 JSON 文件. +参数: + 无. +返回: + 无. +""" + +import argparse +import json +import logging +import sys +from pathlib import Path +from typing import Optional + +from ..config.setting import configure_logging +from .chemicalbook_scraper import fetch_chemicalbook_by_cas + + +logger = logging.getLogger("FetchChemicalBookCLI") + + +def main(argv: Optional[list] = None) -> int: + """ + 功能: + 解析命令行参数并执行 ChemicalBook 抓取. + 参数: + argv: Optional[list], 可选命令行参数列表, None 表示使用系统参数. + 返回: + int, 进程退出码. 成功返回 0, 失败返回 1. + """ + parser = argparse.ArgumentParser(description="按 CAS 抓取 ChemicalBook 化学品信息") + parser.add_argument("--cas", required=True, help="CAS 号, 例如 64-17-5") + parser.add_argument("--out", help="输出 JSON 文件路径") + parser.add_argument("--timeout", type=float, default=15.0, help="单次请求超时秒数") + parser.add_argument("--save-html", action="store_true", help="同时保存原始 HTML 文件") + parser.add_argument("--log-level", default="INFO", help="日志级别, 例如 INFO 或 DEBUG") + args = parser.parse_args(argv) + + configure_logging(args.log_level) + logger.info("开始抓取 ChemicalBook, CAS=%s", args.cas) + + record = fetch_chemicalbook_by_cas( + cas=args.cas, + timeout=args.timeout, + save_raw_html=args.save_html, + ) + + output_text = json.dumps(record, ensure_ascii=False, indent=2) + if args.out is not None: + output_path = Path(args.out) + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(output_text, encoding="utf-8") + logger.info("ChemicalBook 抓取结果已写入: %s", output_path) + else: + print(output_text) + + if record["status"] in ("ok", "parse_partial"): + return 0 + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/unilabos/devices/eit_synthesis_station/chem_tools/storage.py b/unilabos/devices/eit_synthesis_station/chem_tools/storage.py new file mode 100644 index 00000000..769ebf1b --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/chem_tools/storage.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +""" +功能: + 管理 chem_tools 下 ChemicalBook 缓存, 原始 HTML 与结构化记录目录. + 同时负责将旧的 eit_synthesis_station/data/chemicalbook_* 数据迁移到新目录. +参数: + 无. +返回: + 无. +""" + +import logging +import shutil +from pathlib import Path + + +logger = logging.getLogger("ChemToolsStorage") + +MODULE_ROOT = Path(__file__).resolve().parent +DATA_ROOT = MODULE_ROOT / "data" +CHEMICALBOOK_CACHE_ROOT = DATA_ROOT / "chemicalbook_cache" +CHEMICALBOOK_RAW_ROOT = DATA_ROOT / "chemicalbook_raw" +CHEMICALBOOK_RECORD_ROOT = DATA_ROOT / "chemicalbook_records" + +LEGACY_DATA_ROOT = MODULE_ROOT.parent / "data" +LEGACY_CHEMICALBOOK_CACHE_ROOT = LEGACY_DATA_ROOT / "chemicalbook_cache" +LEGACY_CHEMICALBOOK_RAW_ROOT = LEGACY_DATA_ROOT / "chemicalbook_raw" +LEGACY_CHEMICALBOOK_RECORD_ROOT = LEGACY_DATA_ROOT / "chemicalbook_records" + +_MIGRATION_DONE = False + + +def ensure_chemicalbook_data_layout() -> None: + """ + 功能: + 确保 chem_tools 化学查询数据目录存在, 并将旧目录中的 ChemicalBook 数据迁移到新目录. + 若目标文件已存在, 保留新目录文件并跳过覆盖. + 参数: + 无. + 返回: + 无. + """ + global _MIGRATION_DONE + + if _MIGRATION_DONE is True: + return + + for target_root in ( + CHEMICALBOOK_CACHE_ROOT, + CHEMICALBOOK_RAW_ROOT, + CHEMICALBOOK_RECORD_ROOT, + ): + target_root.mkdir(parents=True, exist_ok=True) + + _move_legacy_tree(LEGACY_CHEMICALBOOK_CACHE_ROOT, CHEMICALBOOK_CACHE_ROOT) + _move_legacy_tree(LEGACY_CHEMICALBOOK_RAW_ROOT, CHEMICALBOOK_RAW_ROOT) + _move_legacy_tree(LEGACY_CHEMICALBOOK_RECORD_ROOT, CHEMICALBOOK_RECORD_ROOT) + + _MIGRATION_DONE = True + + +def _move_legacy_tree(source_root: Path, target_root: Path) -> None: + """ + 功能: + 将旧目录下的文件迁移到新目录, 仅处理普通文件, 并保留目标目录已有文件. + 参数: + source_root: Path, 旧目录根路径. + target_root: Path, 新目录根路径. + 返回: + 无. + """ + if source_root.exists() is False: + return + + target_root.mkdir(parents=True, exist_ok=True) + + for source_path in sorted(source_root.rglob("*")): + if source_path.is_file() is False: + continue + + relative_path = source_path.relative_to(source_root) + target_path = target_root / relative_path + target_path.parent.mkdir(parents=True, exist_ok=True) + + if target_path.exists() is True: + logger.warning( + "化学查询迁移跳过重复文件, 保留新目录文件: src=%s, dst=%s", + source_path, + target_path, + ) + try: + source_path.unlink() + except OSError as exc: + logger.warning("化学查询迁移删除旧重复文件失败: path=%s, err=%s", source_path, exc) + continue + + shutil.move(str(source_path), str(target_path)) + + _remove_empty_dirs(source_root) + + +def _remove_empty_dirs(root_path: Path) -> None: + """ + 功能: + 删除迁移后遗留的空目录, 不影响非空目录内容. + 参数: + root_path: Path, 待清理目录. + 返回: + 无. + """ + if root_path.exists() is False: + return + + for directory in sorted(root_path.rglob("*"), reverse=True): + if directory.is_dir() is False: + continue + try: + directory.rmdir() + except OSError: + continue + + try: + root_path.rmdir() + except OSError: + pass diff --git a/unilabos/devices/eit_synthesis_station/config/__init__.py b/unilabos/devices/eit_synthesis_station/config/__init__.py new file mode 100644 index 00000000..6f747c28 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/config/__init__.py @@ -0,0 +1,24 @@ +""" +功能: + 配置模块导出. +""" +from .setting import Settings, configure_logging +from .constants import ( + TaskStatus, + StationState, + DeviceModuleStatus, + NoticeType, + NoticeStatus, + FaultRecoveryType, +) + +__all__ = [ + "Settings", + "configure_logging", + "TaskStatus", + "StationState", + "DeviceModuleStatus", + "NoticeType", + "NoticeStatus", + "FaultRecoveryType", +] diff --git a/unilabos/devices/eit_synthesis_station/config/constants.py b/unilabos/devices/eit_synthesis_station/config/constants.py new file mode 100644 index 00000000..fcc2ff07 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/config/constants.py @@ -0,0 +1,314 @@ +from enum import IntEnum + + +class DeviceCode(IntEnum): + """ + 功能: + 工站设备代码枚举. + """ + ARM = 301 # 机械臂 + POWDER_DISPENSER = 304 # 加粉模块 + MAG_STIRRER = 305 # 热磁力搅拌模块 + CAP_OPENER = 303 # 开盖模块 + W1_SHELF = 342 # W1排货架 + W1_1_2 = 360 # W-1-1、W-1-2 + W1_3_4 = 361 # W-1-3、W-1-4 + W1_5_6 = 362 # W-1-5、W-1-6 + W1_7_8 = 363 # W-1-7、W-1-8 + TRANSFER_SHELF = 346 # 中转货架 + OUTER_DOOR = 340 # 过渡舱外门 + INNER_DOOR = 341 # 过渡舱内门 + MAGNET_ADDER = 343 # 加磁子模块 + FLASH_FILTER = 336 # 闪滤模块 + EXCHANGE_SHELF = 351 # 交换仓货架 + GLOVEBOX_ENV = 352 # 手套箱箱体环境 + +class TaskStatus(IntEnum): + """ + 功能: + 任务状态码枚举, 用于轮询与判定. + 备注: + 主要来自补充文档的状态码说明, 未覆盖的状态可按实际扩展. + """ + UNSTARTED = 0 # 未开始 + RUNNING = 1 # 运行中 + COMPLETED = 2 # 已完成 + PAUSED = 3 # 已暂停 + FAILED = 4 # 失败 + STOPPED = 5 # 已停止 + PAUSING = 6 # 暂停中 + STOPPING = 7 # 停止中 + WAITING = 8 # 等待中 + HOLDING = 10 # 挂起/保持 + +class StationState(IntEnum): + """ + 功能: + 工站设备状态码枚举, 与任务状态类似但语义是整站状态. + """ + IDLE = 0 # 空闲/待机 + RUNNING = 1 # 运行中 + PAUSED = 3 # 已暂停 + PAUSING = 6 # 暂停中 + STOPPING = 7 # 停止中 + HOLDING = 10 # 挂起/保持 + +class DeviceModuleStatus(IntEnum): + """ + 功能: + 工站设备模块状态码枚举. + """ + + AVAILABLE = 0 # 可用/就绪 + RUNNING = 1 # 运行中 + UNAVAILABLE = 2 # 不可用 + OPEN = 3 # 打开 + CLOSE = 4 # 关闭 + OUTSIDE = 5 # 在外/离位 + HOME = 6 # 原点/回零 + +class NoticeType(IntEnum): + """ + 功能: + 消息通知类型. + """ + + INFO = 0 # 信息 + FAULT = 1 # 故障 + ALARM = 2 # 告警 + +class NoticeStatus(IntEnum): + """ + 功能: + 告警状态. + """ + + ABNORMAL = 1 # 异常 + FIXING = 2 # 处理中 + FIXED = 3 # 已恢复 + +class FaultRecoveryType(IntEnum): + """ + 功能: + 故障恢复处理类型. + """ + + RECOVER = 0 # 恢复 + SKIP_STEP_FAIL = 1 # 跳过步骤(并判定失败) + SKIP_STEP_SUCCESS = 2 # 跳过步骤(并判定成功) + SKIP_SAMPLE_ALL = 3 # 跳过整个样品/该样品所有步骤 + RETRY = 4 # 重试 + SKIP_AND_TERMINATE = 5 # 跳过并终止任务 + +class ResourceCode(IntEnum): + """ + 功能: + 资源码(托盘/载具)枚举. + 备注: + 资源码用于在流程/接口中引用具体耗材托盘或载具类型. + """ + + #---------------------托盘编码----------------------- + REACTION_TUBE_TRAY_2ML = 201000726 # 2 mL 反应试管托盘 + TEST_TUBE_MAGNET_TRAY_2ML = 201000711 # 2 mL 试管磁子托盘 + REACTION_SEAL_CAP_TRAY = 201000712 # 反应密封盖托盘 + FLASH_FILTER_INNER_BOTTLE_TRAY = 201000727 # 闪滤瓶内瓶托盘 + FLASH_FILTER_OUTER_BOTTLE_TRAY = 201000728 # 闪滤瓶外瓶托盘 + + TIP_TRAY_50UL = 201000815 # 50 μL Tip 头托盘 + TIP_TRAY_1ML = 201000731 # 1 mL Tip 头托盘 + TIP_TRAY_5ML = 201000512 # 5 mL Tip 头托盘 + + POWDER_BUCKET_TRAY_30ML = 201000600 # 30 mL 粉桶托盘 + + REAGENT_BOTTLE_TRAY_2ML = 201000730 # 2 mL 试剂瓶托盘 + REAGENT_BOTTLE_TRAY_8ML = 201000502 # 8 mL 试剂瓶托盘 + REAGENT_BOTTLE_TRAY_40ML = 201000503 # 40 mL 试剂瓶托盘 + REAGENT_BOTTLE_TRAY_125ML = 220000023 # 125 mL 试剂瓶托盘 + + #---------------------耗材编码----------------------- + REACTION_TUBE_2ML = 551000502 # 2 mL 反应试管 + TEST_TUBE_MAGNET_2ML = 220000322 # 2 mL 试管磁子 + REACTION_SEAL_CAP = 211009427 # 反应密封盖 + FLASH_FILTER_INNER_BOTTLE = 220000320 # 闪滤瓶内瓶 + FLASH_FILTER_OUTER_BOTTLE = 220000321 # 闪滤瓶外瓶 + + TIP_1ML = 220000308 # 1 mL Tip 头 + TIP_5ML = 214000037 # 5 mL Tip 头 + TIP_50UL = 220000304 # 50 μL Tip 头 + + POWDER_BUCKET_30ML = 201000816 # 30 mL 粉桶 + + REAGENT_BOTTLE_2ML = 502000353 # 2 mL 试剂瓶 + REAGENT_BOTTLE_8ML = 220000005 # 8 mL 试剂瓶 + REAGENT_BOTTLE_40ML = 220000092 # 40 mL 试剂瓶 + REAGENT_BOTTLE_125ML = 220000008 # 125 mL 试剂瓶 + +TRAY_CODE_DISPLAY_NAME = { + int(ResourceCode.REACTION_TUBE_TRAY_2ML): "2 mL反应试管托盘", + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML): "2 mL试管磁子托盘", + int(ResourceCode.REACTION_SEAL_CAP_TRAY): "反应密封盖托盘", + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY): "闪滤瓶内瓶托盘", + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): "闪滤瓶外瓶托盘", + int(ResourceCode.TIP_TRAY_50UL): "50 μL Tip 头托盘", + int(ResourceCode.TIP_TRAY_1ML): "1 mL Tip 头托盘", + int(ResourceCode.TIP_TRAY_5ML): "5 mL Tip 头托盘", + int(ResourceCode.POWDER_BUCKET_TRAY_30ML): "30 mL粉桶托盘", + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): "2 mL试剂瓶托盘", + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): "8 mL试剂瓶托盘", + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): "40 mL试剂瓶托盘", + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML): "125 mL试剂瓶托盘", +} + +# 耗材编码到标准中文名称映射. +CONSUMABLE_CODE_DISPLAY_NAME = { + int(ResourceCode.TIP_50UL): "50 μL Tip头", + int(ResourceCode.TIP_1ML): "1 mL Tip头", + int(ResourceCode.TIP_5ML): "5 mL Tip头", + int(ResourceCode.TEST_TUBE_MAGNET_2ML): "2 mL反应管磁子", + int(ResourceCode.REACTION_TUBE_2ML): "2 mL反应管", + int(ResourceCode.REACTION_SEAL_CAP): "反应密封盖", + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE): "闪滤瓶内瓶", + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE): "闪滤瓶外瓶", +} + +# 耗材编码到托盘编码映射. +CONSUMABLE_CODE_TO_TRAY_CODE = { + int(ResourceCode.TIP_50UL): int(ResourceCode.TIP_TRAY_50UL), + int(ResourceCode.TIP_1ML): int(ResourceCode.TIP_TRAY_1ML), + int(ResourceCode.TIP_5ML): int(ResourceCode.TIP_TRAY_5ML), + int(ResourceCode.TEST_TUBE_MAGNET_2ML): int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML), + int(ResourceCode.REACTION_TUBE_2ML): int(ResourceCode.REACTION_TUBE_TRAY_2ML), + int(ResourceCode.REACTION_SEAL_CAP): int(ResourceCode.REACTION_SEAL_CAP_TRAY), + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE): int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY), + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE): int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY), +} + +# 全量耗材/试剂编码到对应托盘编码映射(包含试剂瓶)。 +# 用于当 API 缺少 slot=-1 的托盘本体记录时,从 media_items 反推 tray_code。 +ITEM_CODE_TO_TRAY_CODE = { + **CONSUMABLE_CODE_TO_TRAY_CODE, + int(ResourceCode.REAGENT_BOTTLE_2ML): int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML), + int(ResourceCode.REAGENT_BOTTLE_8ML): int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML), + int(ResourceCode.REAGENT_BOTTLE_40ML): int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML), + int(ResourceCode.REAGENT_BOTTLE_125ML): int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML), + int(ResourceCode.POWDER_BUCKET_30ML): int(ResourceCode.POWDER_BUCKET_TRAY_30ML), +} + +# 归一化后的耗材别名到耗材编码映射. +CONSUMABLE_ALIAS_TO_CODE = { + # 50 μL Tip头 + "50ul枪头": int(ResourceCode.TIP_50UL), + "50ul吸头": int(ResourceCode.TIP_50UL), + "50ultip": int(ResourceCode.TIP_50UL), + "50ultip头": int(ResourceCode.TIP_50UL), + "50ultip枪头": int(ResourceCode.TIP_50UL), + "50ultip吸头": int(ResourceCode.TIP_50UL), + # 1 mL Tip头 + "1ml枪头": int(ResourceCode.TIP_1ML), + "1ml吸头": int(ResourceCode.TIP_1ML), + "1mltip": int(ResourceCode.TIP_1ML), + "1mltip头": int(ResourceCode.TIP_1ML), + "1mltip枪头": int(ResourceCode.TIP_1ML), + "1mltip吸头": int(ResourceCode.TIP_1ML), + # 5 mL Tip头 + "5ml枪头": int(ResourceCode.TIP_5ML), + "5ml吸头": int(ResourceCode.TIP_5ML), + "5mltip": int(ResourceCode.TIP_5ML), + "5mltip头": int(ResourceCode.TIP_5ML), + "5mltip枪头": int(ResourceCode.TIP_5ML), + "5mltip吸头": int(ResourceCode.TIP_5ML), + + # 反应管 + "2ml反应管": int(ResourceCode.REACTION_TUBE_2ML), + "2ml反应试管": int(ResourceCode.REACTION_TUBE_2ML), + "反应管": int(ResourceCode.REACTION_TUBE_2ML), + "反应试管": int(ResourceCode.REACTION_TUBE_2ML), + + # 磁子 + "2ml反应管磁子": int(ResourceCode.TEST_TUBE_MAGNET_2ML), + "2ml试管磁子": int(ResourceCode.TEST_TUBE_MAGNET_2ML), + "反应管磁子": int(ResourceCode.TEST_TUBE_MAGNET_2ML), + "试管磁子": int(ResourceCode.TEST_TUBE_MAGNET_2ML), + + # 反应密封盖 + "反应密封盖": int(ResourceCode.REACTION_SEAL_CAP), + "反应盖板": int(ResourceCode.REACTION_SEAL_CAP), + "密封盖": int(ResourceCode.REACTION_SEAL_CAP), + + # 闪滤瓶 + "闪滤瓶内瓶": int(ResourceCode.FLASH_FILTER_INNER_BOTTLE), + "闪滤内瓶": int(ResourceCode.FLASH_FILTER_INNER_BOTTLE), + "内瓶": int(ResourceCode.FLASH_FILTER_INNER_BOTTLE), + "闪滤瓶外瓶": int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE), + "闪滤外瓶": int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE), + "外瓶": int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE), +} + +class TraySpec: + """ + 功能: + 托盘规格,使用 (col, row) 数字表示;行按字母序 A=1, B=2 ... H=8,列保持原数字. + """ + + REAGENT_BOTTLE_TRAY_2ML = (8, 6) # 2 mL 试剂瓶托盘 + REAGENT_BOTTLE_TRAY_8ML = (4, 3) # 8 mL 试剂瓶托盘 + REAGENT_BOTTLE_TRAY_40ML = (3, 2) # 40 mL 试剂瓶托盘 + REAGENT_BOTTLE_TRAY_125ML = (2, 1) # 125 mL 试剂瓶托盘 + REACTION_TUBE_TRAY_2ML = (6, 4) # 2 mL 反应试管托盘 + TEST_TUBE_MAGNET_TRAY_2ML = (6, 4) # 2 mL 试管磁子托盘 + REACTION_SEAL_CAP_TRAY = (1, 1) # 反应密封盖托盘 + FLASH_FILTER_INNER_BOTTLE_TRAY = (8, 6) # 闪滤瓶内瓶托盘 + FLASH_FILTER_OUTER_BOTTLE_TRAY = (8, 6) # 闪滤瓶外瓶托盘 + TIP_TRAY_50UL = (12, 8) # 50 μL Tip 头托盘 + TIP_TRAY_1ML = (12, 8) # 1 mL Tip 头托盘 + TIP_TRAY_5ML = (6, 4) # 5 mL Tip 头托盘 + POWDER_BUCKET_TRAY_30ML = (1, 2) # 30 mL 粉桶托盘 + + +# ===================== AGV 自动下料相关常量 ===================== + +# 资源码到 AGV 物料类型名称的映射 +RESOURCE_CODE_TO_MATERIAL_TYPE = { + int(ResourceCode.REACTION_TUBE_TRAY_2ML): "REACTION_TUBE_TRAY_2ML", + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML): "TEST_TUBE_MAGNET_TRAY_2ML", + int(ResourceCode.REACTION_SEAL_CAP_TRAY): "REACTION_SEAL_CAP_TRAY", + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY): "FLASH_FILTER_INNER_BOTTLE_TRAY", + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): "FLASH_FILTER_OUTER_BOTTLE_TRAY", + int(ResourceCode.TIP_TRAY_50UL): "TIP_TRAY_50UL", + int(ResourceCode.TIP_TRAY_1ML): "TIP_TRAY_1ML", + int(ResourceCode.TIP_TRAY_5ML): "TIP_TRAY_5ML", + int(ResourceCode.POWDER_BUCKET_TRAY_30ML): "POWDER_BUCKET_TRAY_30ML", + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): "REAGENT_BOTTLE_TRAY_2ML", + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): "REAGENT_BOTTLE_TRAY_8ML", + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): "REAGENT_BOTTLE_TRAY_40ML", + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML): "REAGENT_BOTTLE_TRAY_125ML", +} + +# 合成工站下料位置(TB-x-x)到 AGV 托盘名称的映射 +TB_CODE_TO_SYNTHESIS_TRAY = { + "TB-1-1": "synthesis_station_tray_1-1", + "TB-1-2": "synthesis_station_tray_1-2", + "TB-1-3": "synthesis_station_tray_1-3", + "TB-1-4": "synthesis_station_tray_1-4", + "TB-2-1": "synthesis_station_tray_2-1", + "TB-2-2": "synthesis_station_tray_2-2", + "TB-2-3": "synthesis_station_tray_2-3", + "TB-2-4": "synthesis_station_tray_2-4", +} + +# 货架托盘位置列表 +SHELF_TRAY_POSITIONS = [ + "shelf_tray_1-1", "shelf_tray_1-2", "shelf_tray_1-3", "shelf_tray_1-4", + "shelf_tray_2-1", "shelf_tray_2-2", "shelf_tray_2-3", "shelf_tray_2-4", + "shelf_tray_3-1", "shelf_tray_3-2", "shelf_tray_3-3", "shelf_tray_3-4", +] + +# 分析工站托盘位置列表 +ANALYSIS_STATION_TRAY_POSITIONS = [ + "analysis_station_tray_1-2" +] + + + + diff --git a/unilabos/devices/eit_synthesis_station/config/setting.py b/unilabos/devices/eit_synthesis_station/config/setting.py new file mode 100644 index 00000000..9a09c48d --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/config/setting.py @@ -0,0 +1,131 @@ +import logging +import os +from dataclasses import dataclass, field +from pathlib import Path + +from ..notification.notification_settings import NotificationSettings + + +@dataclass +class Settings: + """ + 功能: + 统一存放 driver 的基础配置, 例如 URL, 账号密码, 默认超时时间. + 参数: + base_url: 设备上位机服务地址, 例如 "http://127.0.0.1:4669". + username: 登录用户名. + password: 登录密码. + timeout_s: requests 默认超时(秒). + verify_ssl: https 场景是否校验证书, 内网调试可为 False. + log_level: 日志级别字符串, 例如 "INFO". + auto_rename_on_duplicate: 任务名称重复(409)时是否自动追加时间戳后缀并重试, + 格式为 "<原名称>_YYYYMMDD_HHmmss", 默认 True. + 返回: + Settings. + """ + + base_url: str = "http://10.40.13.51:4669" + username: str = "admin" + password: str = "admin" + timeout_s: float = 30.0 + verify_ssl: bool = True + log_level: str = "INFO" + + # 数据存储配置 + data_dir: Path = Path(__file__).parent.parent / "data" + enable_data_logging: bool = True + task_retention_days: int = 90 + + # 任务提交配置 + auto_rename_on_duplicate: bool = True # 遇到 409 冲突时自动重命名任务, 追加 _YYYYMMDD_HHmmss 后缀 + + # 异常通知配置 + notification: NotificationSettings = field(default_factory=NotificationSettings) + + @staticmethod + def from_env() -> "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, + SYN_STATION_DATA_DIR, SYN_STATION_ENABLE_DATA_LOGGING, + SYN_STATION_TASK_RETENTION_DAYS, SYN_STATION_AUTO_RENAME_ON_DUPLICATE. + """ + 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) + + # 数据存储配置 + data_dir_str = os.getenv("SYN_STATION_DATA_DIR") + data_dir = Path(data_dir_str) if data_dir_str else Settings.data_dir + + enable_data_logging_str = os.getenv("SYN_STATION_ENABLE_DATA_LOGGING", str(Settings.enable_data_logging)) + enable_data_logging = enable_data_logging_str.strip().lower() in ("1", "true", "yes", "y", "on") + + task_retention_days_str = os.getenv("SYN_STATION_TASK_RETENTION_DAYS", str(Settings.task_retention_days)) + try: + task_retention_days = int(task_retention_days_str) + except ValueError: + task_retention_days = Settings.task_retention_days + + # 任务提交配置 + auto_rename_str = os.getenv("SYN_STATION_AUTO_RENAME_ON_DUPLICATE", str(Settings.auto_rename_on_duplicate)) + auto_rename_on_duplicate = auto_rename_str.strip().lower() in ("1", "true", "yes", "y", "on") + + # 异常通知配置 + notification = NotificationSettings.from_env() + + return Settings( + base_url=base_url, + username=username, + password=password, + timeout_s=timeout_s, + verify_ssl=verify_ssl, + log_level=log_level, + data_dir=data_dir, + enable_data_logging=enable_data_logging, + task_retention_days=task_retention_days, + auto_rename_on_duplicate=auto_rename_on_duplicate, + notification=notification, + ) + + +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/eit_synthesis_station/controller/__init__.py b/unilabos/devices/eit_synthesis_station/controller/__init__.py new file mode 100644 index 00000000..739e0e78 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/controller/__init__.py @@ -0,0 +1 @@ +from .station_controller import SynthesisStationController diff --git a/unilabos/devices/eit_synthesis_station/controller/station_controller.py b/unilabos/devices/eit_synthesis_station/controller/station_controller.py new file mode 100644 index 00000000..4b763cf5 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/controller/station_controller.py @@ -0,0 +1,5309 @@ +import json +import logging +import time +from pathlib import Path +from typing import Any, Callable, Dict, List, Optional, Set, Tuple +from collections import OrderedDict +from datetime import datetime + +from ..config.constants import ( + CONSUMABLE_CODE_DISPLAY_NAME, + CONSUMABLE_CODE_TO_TRAY_CODE, + ITEM_CODE_TO_TRAY_CODE, + 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 +from ..data.data_manager import DataManager +import re +import math + +import uuid + +JsonDict = Dict[str, Any] + +logger = logging.getLogger(__name__) + + +def _parse_filter_experiment_numbers(raw_text: Any) -> Optional[Set[int]]: + """ + 功能: + 解析闪滤实验编号字符串, 支持范围和逗号分隔, 兼容中文标点. + 空字符串或"全部"返回 None, 表示全部实验均需闪滤. + 参数: + raw_text: Any, 用户输入, 例如 "1-12,24,28" 或 "1-12,24,28" 或 "全部". + 返回: + Optional[Set[int]], 实验编号集合; None 表示全部实验均需闪滤. + """ + text = str(raw_text).strip() if raw_text is not None else "" + if text == "" or text == "全部": + return None + + # 统一中文标点为英文标点 + text = text.replace(",", ",") + text = text.replace("、", ",") + text = text.replace("—", "-") + text = text.replace("-", "-") + text = text.replace("\u2013", "-") # en-dash + text = text.replace("\u2014", "-") # em-dash + text = text.replace(" ", "") + + result: Set[int] = set() + for segment in text.split(","): + segment = segment.strip() + if segment == "": + continue + if "-" in segment: + parts = segment.split("-", 1) + try: + start = int(parts[0]) + end = int(parts[1]) + if start > end: + start, end = end, start + result.update(range(start, end + 1)) + except ValueError: + logger.warning("闪滤实验编号解析失败, 无法识别范围: '%s'", segment) + else: + try: + result.add(int(segment)) + except ValueError: + logger.warning("闪滤实验编号解析失败, 无法识别编号: '%s'", segment) + + return result if len(result) > 0 else None + + +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__) + + # 初始化数据管理器 + if self._settings.enable_data_logging: + self._data_manager = DataManager(self._settings.data_dir) + self._logger.debug("数据存储已启用,数据目录: %s", self._settings.data_dir) + else: + self._data_manager = None + self._logger.debug("数据存储已禁用") + + # 异常通知监控器 (惰性创建, 调用 start_notification_monitor 时初始化) + self._notification_monitor = None + + @staticmethod + def _collect_duplicate_texts(values: List[str]) -> List[str]: + """ + 功能: + 按首次重复出现的顺序收集重复文本. + 参数: + values: List[str], 待检查的文本列表. + 返回: + List[str], 去重后的重复文本列表. + """ + seen_values: set[str] = set() + duplicate_values: List[str] = [] + + for raw_value in values: + value = str(raw_value).strip() + if value == "": + continue + if value in seen_values: + if value not in duplicate_values: + duplicate_values.append(value) + continue + seen_values.add(value) + + return duplicate_values + + @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 get_setup_params(self) -> JsonDict: + """ + 功能: + 调用 GetSetUp 接口, 提取工站配置中的 addition_timeout、accuracy、liquid_threshold、substance_shortage_nums。 + 参数: + 无 + 返回: + Dict[str, Any], 包含四个配置值。 + """ + resp = self._call_with_relogin(self._client.get_set_up) + # 兼容返回体可能的 result/data 包裹 + data_container = resp.get("result") or resp.get("data") or resp + + required_keys = [ + "addition_timeout", + "accuracy", + "liquid_threshold", + "substance_shortage_nums", + ] + missing = [k for k in required_keys if k not in data_container] + if missing: + raise ValidationError(f"GetSetUp 返回缺少字段: {missing}, resp={resp}") + + result = {k: data_container[k] for k in required_keys} + self._logger.debug( + "获取工站配置成功 addition_timeout=%s accuracy=%s liquid_threshold=%s substance_shortage_nums=%s", + result["addition_timeout"], + result["accuracy"], + result["liquid_threshold"], + result["substance_shortage_nums"], + ) + return result + + # ---------- 设备初始化 ---------- + def device_init( + self, + device_id: Optional[List[str]] = None, + *, + poll_interval_s: float = 1.0, + timeout_s: float = 600.0, + ) -> JsonDict: + """ + 功能: + 触发设备初始化, 然后轮询站点状态直到空闲。 + 初始化完成后自动检测W-1-1到W-1-4上的125mL溶剂瓶, 如有则执行复位操作。 + 参数: + device_id: 设备id列表, 当前忽略, 始终传空JSON。 + poll_interval_s: 轮询间隔秒数。 + timeout_s: 超时秒数, 超时抛出 TimeoutError。 + 返回: + Dict, 初始化接口原始响应。 + """ + resp = self._call_with_relogin(self._client.device_init, {}) # 传空JSON + 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("设备初始化完成") + + # 初始化完成后检测W-1-1到W-1-4上的125mL溶剂瓶 + self._check_and_reset_w1_shelves() + + 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 _check_and_reset_w1_shelves(self) -> None: + """ + 功能: + 检测W-1-1到W-1-4上是否有125mL溶剂瓶, 如有则自动执行复位操作 + 参数: + 无 + 返回: + 无 + """ + try: + self._logger.info("开始检测W-1-1到W-1-4上的125mL溶剂瓶") + + # 获取资源信息 + resources = self.get_resource_info() + + # 定义需要检测的位置和对应的控制位置 + check_positions = { + "W-1-1": "W-1-1", # W-1-1和W-1-2由W-1-1控制 + "W-1-2": "W-1-1", + "W-1-3": "W-1-3", # W-1-3和W-1-4由W-1-3控制 + "W-1-4": "W-1-3" + } + + # 125mL试剂瓶托盘编码 + bottle_125ml_tray_code = int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML) + + # 记录需要复位的控制位置 + positions_to_reset = set() + + # 遍历资源信息检查是否有125mL溶剂瓶 + for resource in resources: + layout_code = resource.get("layout_code", "") + resource_type = resource.get("resource_type") + + # 检查是否是目标位置且是125mL试剂瓶托盘 + if layout_code in check_positions and resource_type == bottle_125ml_tray_code: + control_position = check_positions[layout_code] + positions_to_reset.add(control_position) + self._logger.info("检测到%s位置有125mL溶剂瓶托盘", layout_code) + + # 执行复位操作 + if positions_to_reset: + self._logger.info("开始对检测到的货架执行复位操作") + for position in sorted(positions_to_reset): + try: + self._logger.info("正在复位%s货架", position) + self.control_w1_shelf(position, "home") + self._logger.info("%s货架复位成功", position) + except Exception as e: + self._logger.error("复位%s货架失败: %s", position, str(e)) + else: + self._logger.info("W-1-1到W-1-4上未检测到125mL溶剂瓶, 无需复位") + + except Exception as e: + self._logger.error("检测和复位W1货架时发生错误: %s", str(e)) + + # ---------- 获取设备状态 ------------ + 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): + state_code = int(obj[key]) + + # 自动保存工站状态快照 + if self._data_manager: + state_name = ( + StationState(state_code).name + if state_code in StationState._value2member_map_ + else "UNKNOWN" + ) + self._data_manager.save_station_state({ + "timestamp": datetime.now().isoformat(), + "state": state_name, + "state_code": state_code + }) + + return state_code + + 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.debug("手套箱环境数据: %s", result) + + # 自动保存手套箱环境快照 + if self._data_manager: + self._data_manager.save_glovebox_env({ + "timestamp": datetime.now().isoformat(), + "pressure_pa": box_pressure, + "humidity_ppm": water_content, + "oxygen_ppm": oxygen_content + }) + + return result + + def list_device_info(self) -> JsonDict: + """ + 功能: + 获取站点设备模块列表。暂不使用,使用get_all_device_info. + 参数: + 无. + 返回: + Dict, 接口响应. + """ + resp = self._call_with_relogin(self._client.list_device_info) + 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) + + 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.debug("设备状态汇总完成, 数量=%s", len(device_status)) + + # 自动保存设备状态快照 + if self._data_manager: + self._data_manager.save_device_status({ + "timestamp": datetime.now().isoformat(), + "devices": device_status + }) + + return device_status + + def wait_idle( + self, + *, + poll_interval_s: float = 1.0, + timeout_s: float = 600.0, + stage: str = "", + ) -> None: + """ + 功能: + 轮询站点状态直到空闲,超时则抛出 TimeoutError。 + 参数: + poll_interval_s: 轮询间隔秒数. + timeout_s: 等待空闲的超时时长. + stage: 日志阶段提示文本. + 返回: + None. + """ + start_ts = time.time() + last_state: Optional[int] = None + while True: + state = self.station_state() + if state != last_state: + stage_text = stage if stage != "" else "等待空闲" + self._logger.debug("%s最新状态: state=%s", stage_text, state) + last_state = state + if state == int(StationState.IDLE): + return + if time.time() - start_ts > timeout_s: + stage_text = stage if stage != "" else "等待空闲" + raise TimeoutError(f"{stage_text} 超时, last_state={state}") + time.sleep(poll_interval_s) + + # ---------- 获取站内资源信息 ---------- + 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, {}) + resource_list = self._extract_resource_list(response) + rows = self._format_resource_rows(resource_list) + + self._logger.debug("获取资源信息成功") + + # 自动保存物料资源快照 + if self._data_manager: + self._data_manager.save_resource_info({ + "timestamp": datetime.now().isoformat(), + "resources": rows + }) + + 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]: + """ + 功能: + 归整资源清单, 校验布局编码并按耗材类型汇总可用数量与展示明细. + 参数: + resource_list: List[JsonDict], 接口返回的资源明细列表. + 返回: + List[JsonDict], 每个元素包含布局编码、数量、托盘类型和物料明细. + """ + def _pick_amount_value(media_item: JsonDict, field_names: List[str]) -> Tuple[Any, Optional[str]]: + """ + 功能: + 按字段优先级获取首个可用数值. + 参数: + media_item: JsonDict, 当前资源明细. + field_names: List[str], 按优先级排列的字段名. + 返回: + Tuple[Any, Optional[str]], 包含首个非空值及字段名, 若不存在则为(None, None). + """ + for field_name in field_names: + if field_name in media_item: + candidate_value = media_item.get(field_name) + if candidate_value is not None and str(candidate_value).strip() != "": + return candidate_value, field_name + return None, None + + def _format_amount_text(raw_value: Any, media_item: JsonDict, kind: str) -> str: + """ + 功能: + 依据数值类型和单位拼接展示文本, 数值为0时仅返回数值. + 参数: + raw_value: Any, 原始数值. + media_item: JsonDict, 当前资源明细. + kind: str, 数值类型标记, 取值为weight或volume. + 返回: + str, 拼接单位后的展示文本. + """ + formatted_value = self._format_number(raw_value) + numeric_value = 0.0 + try: + numeric_value = float(raw_value) + except Exception: + numeric_value = 0.0 + + if abs(numeric_value) < 1e-9: + return formatted_value + + raw_unit = str(media_item.get("unit") or "").strip() + unit_lower = raw_unit.lower() + if kind == "weight": + if unit_lower == "g" or unit_lower == "mg": + final_unit = raw_unit + else: + final_unit = "mg" + elif kind == "volume": + if unit_lower == "ml" or unit_lower == "l" or unit_lower == "ul": + final_unit = raw_unit + else: + final_unit = "mL" + else: + final_unit = raw_unit + + if final_unit == "": + return formatted_value + return f"{formatted_value}{final_unit}" + + 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) + if layout_prefix == "": + continue + if layout_prefix[0].isalpha() is False: + self._logger.debug("layout_code不合规 %s", layout_code) + continue + + 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) + + consumable_codes = { + ResourceCode.TEST_TUBE_MAGNET_2ML, + ResourceCode.REACTION_SEAL_CAP, + ResourceCode.FLASH_FILTER_INNER_BOTTLE, + ResourceCode.TIP_1ML, + ResourceCode.TIP_5ML, + ResourceCode.TIP_50UL, + } + detail_codes = { + ResourceCode.REACTION_TUBE_2ML, + ResourceCode.FLASH_FILTER_OUTER_BOTTLE, + } + substance_codes = { + ResourceCode.POWDER_BUCKET_30ML, + ResourceCode.REAGENT_BOTTLE_2ML, + ResourceCode.REAGENT_BOTTLE_8ML, + ResourceCode.REAGENT_BOTTLE_40ML, + ResourceCode.REAGENT_BOTTLE_125ML, + } + 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), + } + + rows: List[JsonDict] = [] + for layout_prefix, group in grouped_by_layout.items(): + tray_item = group.get("tray_item") + media_items = group.get("media_items", []) + + tray_code = self._get_tray_code(tray_item) + + # 当 tray_item 缺失(API 无 slot=-1 记录)时,尝试从 media_items 反推 tray_code + if tray_code is None and media_items: + for media in media_items: + item_type = self._safe_int(media.get("resource_type")) + if item_type is not None and item_type in ITEM_CODE_TO_TRAY_CODE: + tray_code = ITEM_CODE_TO_TRAY_CODE[item_type] + self._logger.debug( + "layout_code=%s 缺少 tray_item,从 media resource_type=%s 反推 tray_code=%s", + layout_prefix, item_type, tray_code, + ) + break + + tray_name = self._get_tray_name(tray_code) + tray_spec = self._get_tray_spec(tray_code) if tray_code is not None else None + + count = 0 + substance_details: List[JsonDict] = [] + + for media in media_items: + resource_type_val = self._safe_int(media.get("resource_type")) + if resource_type_val is None: + continue + try: + resource_code = ResourceCode(resource_type_val) + except Exception: + continue + + slot_index = self._extract_slot_index(media) + used_flag = bool(media.get("used")) + + if resource_code in consumable_codes: + # 未使用的耗材才计数量 + if used_flag is False: + count += 1 + continue + + if resource_code in detail_codes: + # 已使用耗材输出当前剩余量 + if used_flag is True: + substance_details.append( + { + "slot": slot_index, + "well": self._slot_to_well_text(slot_index, tray_spec), + "cur_volume": _format_amount_text(media.get("cur_volume"), media, "volume"), + "cur_weight": _format_amount_text(media.get("cur_weight"), media, "weight"), + } + ) + else: + count += 1 + continue + + if resource_code in substance_codes: + # 试剂类计数并附带物料明细 + count += 1 + substance_text = str(media.get("substance") or "").strip() + if substance_text != "": + if tray_code == powder_tray_code: + amount_value, amount_field = _pick_amount_value( + media, + ["cur_weight", "available_weight", "initial_weight"], + ) + default_kind = "weight" + elif tray_code in bottle_tray_codes: + amount_value, amount_field = _pick_amount_value( + media, + ["cur_volume", "available_volume", "initial_volume"], + ) + default_kind = "volume" + else: + amount_value, amount_field = _pick_amount_value( + media, + [ + "cur_weight", + "cur_volume", + "available_weight", + "available_volume", + "initial_weight", + "initial_volume", + ], + ) + default_kind = "volume" + if amount_field is not None and "weight" in amount_field: + amount_kind = "weight" + elif amount_field is not None and "volume" in amount_field: + amount_kind = "volume" + else: + amount_kind = default_kind + + substance_details.append( + { + "slot": slot_index, + "well": self._slot_to_well_text(slot_index, tray_spec), + "substance": substance_text, + "value": _format_amount_text(amount_value, media, amount_kind), + } + ) + continue + + rows.append( + { + "layout_code": layout_prefix, + "count": count, + "resource_type": tray_code, + "resource_type_name": tray_name, + "substance_details": substance_details, + } + ) + + 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 = ( + "cur_weight", + "available_weight", + "initial_weight" + ) + elif tray_code is not None and tray_code in bottle_tray_codes: + amount_fields = ( + "cur_volume", + "available_volume", + "initial_volume", + ) + else: + amount_fields = ( + "cur_weight", + "cur_volume", + "available_weight", + "available_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._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", + "chemical_id", + "substance_english_name", + "molecular_weight", + "density (g/mL)", + "physical_state", + "physical_form", + "active_content(mol/L or wt%)" + ] + normalized_headers = [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 normalized_headers] + has_chinese_name_header = "substance_chinese_name" in normalized_headers or "substance" in normalized_headers + if has_chinese_name_header is False: + missing_headers.append("substance/substance_chinese_name") + if len(missing_headers) > 0: + warnings.append(f"表头缺少字段: {', '.join(missing_headers)}") + + english_name_count: Dict[str, int] = {} + chinese_name_count: Dict[str, int] = {} + missing_name_rows: List[str] = [] + invalid_state_rows: List[str] = [] + missing_form_rows: List[str] = [] + missing_mw_neat: List[str] = [] + missing_density_neat_liquid: List[str] = [] + missing_content_solution: List[str] = [] + missing_fields_beads: List[str] = [] + allowed_states = {"solid", "liquid", "gas"} + + for idx, row in enumerate(rows, start=2): + english_name = str(row.get("substance_english_name") or "").strip() + chinese_name = str(row.get("substance_chinese_name") or row.get("substance") or "").strip() + cas_number = str(row.get("cas_number") or "").strip() + chemical_id = str(row.get("chemical_id") or "").strip() + molecular_weight = str(row.get("molecular_weight") or "").strip() + density = str(row.get("density (g/mL)") or "").strip() + physical_state_raw = str(row.get("physical_state") or "").strip() + physical_form_raw = str(row.get("physical_form") or "").strip() + active_content = str(row.get("active_content(mol/L or wt%)") or "").strip() + + row_label = english_name or chinese_name or cas_number or chemical_id or f"第{idx}行" # 标记行用于提示 + + if english_name != "" and physical_form_raw.strip().lower() == "neat": + english_name_count[english_name] = english_name_count.get(english_name, 0) + 1 + if chinese_name != "": + chinese_name_count[chinese_name] = chinese_name_count.get(chinese_name, 0) + 1 + if english_name == "" and chinese_name == "": + missing_name_rows.append(row_label) + + physical_state = physical_state_raw.lower() + if physical_state == "" or physical_state not in allowed_states: + invalid_state_rows.append(row_label) + + physical_form = physical_form_raw.lower() + if physical_form == "": + missing_form_rows.append(row_label) + + if physical_form == "neat": + if molecular_weight == "": + missing_mw_neat.append(row_label) + if physical_state == "liquid" and density == "": + missing_density_neat_liquid.append(row_label) + if physical_form == "solution": + if active_content == "": + missing_content_solution.append(row_label) + if physical_form == "beads": + missing_items = [] + if molecular_weight == "": + missing_items.append("molecular_weight") + if active_content == "": + missing_items.append("active_content(mol/L or wt%)") + if len(missing_items) > 0: + missing_fields_beads.append(f"{row_label} 缺少 {', '.join(missing_items)}") + + duplicated_english = [name for name, count in english_name_count.items() if count > 1] + duplicated_chinese = [name for name, count in chinese_name_count.items() if count > 1] + if len(duplicated_english) > 0: + errors.append(f"substance_english_name 出现重复: {', '.join(duplicated_english)}") + if len(duplicated_chinese) > 0: + errors.append(f"substance/substance_chinese_name 出现重复: {', '.join(duplicated_chinese)}") + if len(missing_name_rows) > 0: + warnings.append(f"至少填写中文名或英文名: {', '.join(missing_name_rows)}") + if len(invalid_state_rows) > 0: + warnings.append(f"physical_state 缺失或非法(仅支持 solid/liquid/gas): {', '.join(invalid_state_rows)}") + if len(missing_form_rows) > 0: + warnings.append(f"physical_form 缺失: {', '.join(missing_form_rows)}") + if len(missing_mw_neat) > 0: + warnings.append(f"physical_form 为 neat 需填写 molecular_weight: {', '.join(missing_mw_neat)}") + if len(missing_density_neat_liquid) > 0: + warnings.append(f"neat 且 physical_state 为 liquid 需填写 density (g/mL): {', '.join(missing_density_neat_liquid)}") + if len(missing_content_solution) > 0: + warnings.append(f"physical_form 为 solution 需填写 active_content(mol/L or wt%): {', '.join(missing_content_solution)}") + if len(missing_fields_beads) > 0: + warnings.append(f"physical_form 为 beads 需填写 molecular_weight 和 active_content(mol/L or wt%): {', '.join(missing_fields_beads)}") + + 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 聚合化合物行数据, 合并品牌类字段和其他多值字段 + 参数: + rows: List[Dict[str, Any]], 表格行数据, 键名与表头一致 + headers: List[str], 原始表头列表, 用于保持输出列顺序 + 返回: + List[Dict[str, Any]], 去重后的数据 + """ + normalized_headers = [str(h).strip().lower() for h in headers] + if len(headers) == 0: + self._logger.error("表头为空, 无法去重") + return rows + if "substance" not in normalized_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] + substance_field = next((orig for orig, lower in header_info if lower == "substance"), "substance") + dedup_substance: Dict[str, Dict[str, List[str]]] = {} + substance_order: List[str] = [] + no_substance_stores: List[Dict[str, 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)) + return str(val).rstrip("0").rstrip(".") + if isinstance(val, int): + return str(val) + text = str(val).strip() + return "" if text.lower() == "nan" else text + + def _init_store() -> Dict[str, List[str]]: + return {info[1]: [] for info in header_info} + + def _merge_row(store: Dict[str, List[str]], row_data: JsonDict) -> None: + for orig, key in header_info: + val = _clean(row_data.get(orig)) + if val == "": + continue + if val not in store[key]: + store[key].append(val) + + # 按 substance 聚合行并记录顺序 + for row in rows: + substance_key = _clean(row.get(substance_field)) + if substance_key != "": + if substance_key not in dedup_substance: + dedup_substance[substance_key] = _init_store() + substance_order.append(substance_key) + _merge_row(dedup_substance[substance_key], row) + else: + tmp_store = _init_store() + _merge_row(tmp_store, row) + no_substance_stores.append(tmp_store) + + # 生成输出, 品牌类字段用分号拼接, 其他多值加括号 + def _build_output(store: Dict[str, List[str]]) -> JsonDict: + out_row: JsonDict = {} + for orig, field_key in header_info: + values = store.get(field_key, []) + if field_key in brand_fields: + out_row[orig] = ";".join(values) + else: + if len(values) == 0: + out_row[orig] = "" + elif len(values) == 1: + out_row[orig] = values[0] + else: + out_row[orig] = f"({';'.join(values)})" + return out_row + + result: List[JsonDict] = [] + for key in substance_order: + result.append(_build_output(dedup_substance[key])) + for store in no_substance_stores: + result.append(_build_output(store)) + + 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) + """ + + self._logger.info("开始执行化学品库对齐操作") + + 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 + # 统一单位:把 MICRO SIGN(U+00B5) 归一成 GREEK MU(U+03BC) + unit_norm = unit_part.strip().replace("µ", "μ") + unit = unit_norm if unit_norm else "mL" + return value, unit + + def batch_in_tray( + self, + resource_req_list: List[JsonDict], + *, + task_id: Optional[int] = None, + poll_interval_s: float = 1.0, + timeout_s: float = 900.0, + ) -> JsonDict: + """ + 功能: + 批量上料,执行前后均等待设备空闲,确保空闲状态下触发上料并等待恢复空闲。 + 参数: + resource_req_list: 批量上料的资源信息列表。 + task_id: int, 可选, 任务ID (用于记录). + poll_interval_s: 轮询间隔秒数. + timeout_s: 等待空闲的超时时长. + 返回: + Dict, 执行 batch_in_tray 的接口响应. + """ + if resource_req_list is None or len(resource_req_list) == 0: + raise ValidationError("resource_req_list 不能为空") + + start_time = datetime.now().isoformat() + + self.wait_idle(stage="上料前", poll_interval_s=poll_interval_s, timeout_s=timeout_s) + self._logger.info("仪器空闲,开始上料") + resp = self._call_with_relogin(self._client.batch_in_tray, resource_req_list) + self._logger.info("上料指令已发送,等待仪器回到空闲") + self.wait_idle(stage="上料后", poll_interval_s=poll_interval_s, timeout_s=timeout_s) + self._logger.info("上料完成") + + end_time = datetime.now().isoformat() + + # 保存上料日志 + if self._data_manager: + # 构造日志记录格式 + log_resources: List[JsonDict] = [] + for req in resource_req_list: + layout_code = req.get("layout_code", "") + resource_type = req.get("resource_type") + resource_type_name = req.get("resource_type_name", "") + + # 从 substance_list 构造 substance_details + substance_details = [] + substance_list = req.get("substance_list", []) + for sub in substance_list: + substance_details.append({ + "slot": sub.get("slot", 0), + "well": sub.get("well", ""), + "substance": sub.get("substance", ""), + "value": sub.get("value", "") + }) + + count = len(substance_list) if substance_list else req.get("count", 0) + + log_resource = { + "layout_code": layout_code, + "count": count, + "resource_type": resource_type, + "resource_type_name": resource_type_name, + "substance_details": substance_details, + "task_id": task_id + } + log_resources.append(log_resource) + + log_data = { + "start_time": start_time, + "end_time": end_time, + "resources": log_resources + } + self._data_manager.save_batch_in_tray_log(log_data) + + return resp + + def build_batch_in_tray_payload(self, rows: List[Tuple[str, str, str]]) -> List[JsonDict]: + """ + 功能: + 将上料表格行转换为批量上料的payload, 校验托盘点位或耗材数量, 并统一用量单位为mg和mL + 参数: + rows: List[Tuple[str, str, str]], 每行依次为托盘位置、托盘类型文本、槽位/物质/用量描述 + 返回: + List[JsonDict], 符合 batch_in_tray 接口要求的 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] + 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 + if len(lst) > 0: + 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}") + + def _normalize_amount(value: float, unit_text: str, amount_kind: str, default_unit: str) -> Tuple[float, str]: + if unit_text is None: + unit_text = "" + unit_text = str(unit_text).strip() + if unit_text == "": + unit_text = default_unit + unit_lower = unit_text.lower().replace("µ", "μ").replace("渐", "μ") + if amount_kind == "volume": + if unit_lower == "l": + return value * 1000.0, "mL" + if unit_lower == "ml": + return value, "mL" + if unit_lower in ("μl", "µl", "ul"): + return value / 1000.0, "mL" + return value, "mL" + if amount_kind == "weight": + if unit_lower == "g": + return value * 1000.0, "mg" + if unit_lower == "mg": + return value, "mg" + return value, "mg" + return value, default_unit + + def _validate_slot_index(slot_idx: Optional[int], tray_layout: str, tray_spec: Optional[Tuple[int, int]]) -> int: + if slot_idx is None: + raise ValidationError(f"托盘 {tray_layout} 的点位填写有误") + if tray_spec is None: + self._logger.warning("托盘 %s 缺少规格定义, 跳过点位范围校验", tray_layout) + return slot_idx + col_count, row_count = tray_spec + max_slot = col_count * row_count + if slot_idx < 0 or slot_idx >= max_slot: + raise ValidationError(f"托盘 {tray_layout} 点位 {slot_idx} 超出有效范围 0~{max_slot - 1}") + return slot_idx + + def _validate_consumable_qty(qty: Optional[int], tray_layout: str, tray_spec: Optional[Tuple[int, int]]) -> int: + if qty is None: + raise ValidationError(f"托盘 {tray_layout} 填写的耗材数量为空") + if qty <= 0: + raise ValidationError(f"托盘 {tray_layout} 填写的耗材数量必须大于0") + if tray_spec is None: + self._logger.warning("托盘 %s 缺少规格定义, 跳过容量校验", tray_layout) + return qty + capacity = tray_spec[0] * tray_spec[1] + if qty > capacity: + raise ValidationError(f"托盘 {tray_layout} 填写的耗材数量 {qty} 超出容量 {capacity}") + return qty + + resource_req_list: List[JsonDict] = [] + + for position, tray_type_raw, content in rows: + tray_layout = str(position).strip() + if 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 + + tray_spec = self._get_tray_spec(tray_code_int) + + resource_list: List[JsonDict] = [] + resource_list.append({ + "layout_code": f"{tray_layout}:-1", + "resource_type": str(tray_code_int), + }) + + if tray_code_int in no_substance_trays: + qty = self._safe_int(content) + qty = _validate_consumable_qty(qty, tray_layout, tray_spec) # 校验耗材数量是否在容量内 + 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 + }) + 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") + ) + + 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) + slot_idx = _validate_slot_index(slot_idx, tray_layout, tray_spec) # 校验点位是否在托盘范围内 + + raw_value, raw_unit = self._split_amount_unit(amt_str) + norm_value, norm_unit = _normalize_amount(raw_value, raw_unit, amt_kind, 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": norm_unit, + "chemical_id": fid + } + + if amt_kind == "volume": + media_item["initial_volume"] = norm_value + else: + media_item["initial_weight"] = norm_value + + resource_list.append(media_item) + + resource_req_list.append({ + "remark": "", + "resource_list": resource_list + }) + + self._logger.info("已解析上料信息, 包含 %s 个托盘", len(resource_req_list)) + return resource_req_list + + @staticmethod + def _normalize_batch_in_header_text(value: Any) -> str: + """ + 功能: + 规范化 batch_in 表头文本, 用于跨模板兼容匹配. + 参数: + value: 任意类型单元格值. + 返回: + str, 去空白并去掉常见分隔符后的小写文本. + """ + text = "" if value is None else str(value) + return ( + text.replace(" ", "") + .replace("\n", "") + .replace("\r", "") + .replace("\t", "") + .replace("_", "") + .replace("-", "") + .strip() + .lower() + ) + + def _find_batch_in_header_map( + self, + worksheet: Any, + *, + max_scan_rows: int = 30, + max_scan_cols: int = 30, + ) -> Tuple[Optional[int], Optional[Dict[str, int]]]: + """ + 功能: + 在单个工作表中定位 batch_in 表头行, 并返回关键列映射. + 参数: + worksheet: openpyxl 工作表对象. + max_scan_rows: 最大扫描行数. + max_scan_cols: 最大扫描列数. + 返回: + Tuple[Optional[int], Optional[Dict[str, int]]], 命中时返回(表头行号, 列映射), 未命中返回(None, None). + """ + scan_rows = min(worksheet.max_row, max_scan_rows) + scan_cols = min(worksheet.max_column, max_scan_cols) + required_headers = { + "position": "position", + "tray_type": "traytype", + "content": "content", + } + optional_headers = { + "shelf_position": "shelfposition", + "storage": "storage", + } + + for row_index in range(1, scan_rows + 1): + normalized_cells: Dict[int, str] = {} + for col_index in range(1, scan_cols + 1): + normalized_cells[col_index] = self._normalize_batch_in_header_text( + worksheet.cell(row_index, col_index).value + ) + + header_map: Dict[str, int] = {} + for field_name, expected_header in required_headers.items(): + matched_col = None + for col_index, normalized_text in normalized_cells.items(): + if normalized_text == expected_header: + matched_col = col_index + break + if matched_col is None: + header_map = {} + break + header_map[field_name] = matched_col + + if len(header_map) != len(required_headers): + continue + + for field_name, expected_header in optional_headers.items(): + for col_index, normalized_text in normalized_cells.items(): + if normalized_text == expected_header: + header_map[field_name] = col_index + break + + return row_index, header_map + + return None, None + + def _select_batch_in_sheet(self, workbook: Any) -> Tuple[Any, int, Dict[str, int]]: + """ + 功能: + 从 batch_in 工作簿中选择可解析的工作表. + 选择顺序: batch_in_tray > 当前 active > 其它工作表. + 参数: + workbook: openpyxl Workbook 对象. + 返回: + Tuple[Any, int, Dict[str, int]], (工作表对象, 表头行号, 列映射). + """ + candidate_sheet_names: List[str] = [] + preferred_sheet_name = "batch_in_tray" + if preferred_sheet_name in workbook.sheetnames: + candidate_sheet_names.append(preferred_sheet_name) + + active_sheet_name = workbook.active.title + if active_sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(active_sheet_name) + + for sheet_name in workbook.sheetnames: + if sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(sheet_name) + + matched_candidates: List[Tuple[Any, int, Dict[str, int]]] = [] + for sheet_name in candidate_sheet_names: + worksheet = workbook[sheet_name] + header_row, header_map = self._find_batch_in_header_map(worksheet) + if header_row is None or header_map is None: + continue + + matched_candidates.append((worksheet, header_row, header_map)) + + if len(matched_candidates) > 0: + worksheet, header_row, header_map = matched_candidates[0] + if len(matched_candidates) > 1: + candidate_names = [item[0].title for item in matched_candidates] + self._logger.warning( + "batch_in 命中多个候选工作表, 将按优先顺序使用 [%s], 其余候选: %s", + worksheet.title, + candidate_names[1:], + ) + if worksheet.title != workbook.active.title: + self._logger.info( + "batch_in 解析使用工作表: %s, 当前活动工作表: %s", + worksheet.title, + workbook.active.title, + ) + return worksheet, header_row, header_map + + raise ValueError( + f"未找到可解析的 batch_in 工作表, 可用工作表: {workbook.sheetnames}" + ) + + def _iter_batch_in_records( + self, + worksheet: Any, + header_row: int, + header_map: Dict[str, int], + ) -> List[Dict[str, str]]: + """ + 功能: + 按列映射读取 batch_in 工作表数据行. + 参数: + worksheet: openpyxl 工作表对象. + header_row: 表头所在行号. + header_map: 字段到列号映射. + 返回: + List[Dict[str, str]], 标准化后的上料行记录. + """ + records: List[Dict[str, str]] = [] + for row_index in range(header_row + 1, worksheet.max_row + 1): + position = worksheet.cell(row_index, header_map["position"]).value + if position is None or str(position).strip() == "": + continue + + tray_type = worksheet.cell(row_index, header_map["tray_type"]).value + content = worksheet.cell(row_index, header_map["content"]).value + shelf_position = "" + if "shelf_position" in header_map: + shelf_position_value = worksheet.cell(row_index, header_map["shelf_position"]).value + shelf_position = "" if shelf_position_value is None else str(shelf_position_value).strip() + + storage = "" + if "storage" in header_map: + storage_value = worksheet.cell(row_index, header_map["storage"]).value + storage = "" if storage_value is None else str(storage_value).strip() + + records.append( + { + "position": str(position).strip(), + "tray_type": "" if tray_type is None else str(tray_type).strip(), + "content": "" if content is None else str(content).strip(), + "shelf_position": shelf_position, + "storage": storage, + } + ) + + return records + + # ---------- AGV 转运辅助方法 ---------- + + def _build_agv_transfer_tasks( + self, + records: List[Dict[str, str]], + ) -> Tuple[List[Dict[str, Any]], List[str]]: + """ + 功能: + 将上料记录列表转换为 AGV 转运任务列表. + 从 record 的 position / tray_type / shelf_position / content 字段解析出 + source_tray, target_tray, material_type, tray_display_name, substance_details. + 参数: + records: List[Dict], 由 _iter_batch_in_records 返回的记录列表, + 每条需含 position, tray_type, shelf_position, content 字段. + 返回: + Tuple[List[Dict], List[str]]: + - transfer_tasks: 有效的 AGV 转运任务列表 + - errors: 解析过程中的错误信息列表 + """ + from ..config.constants import ( + RESOURCE_CODE_TO_MATERIAL_TYPE, + TB_CODE_TO_SYNTHESIS_TRAY, + TRAY_CODE_DISPLAY_NAME, + ) + + transfer_tasks: List[Dict[str, Any]] = [] + errors: List[str] = [] + target_tray_to_position: Dict[str, str] = {} + source_tray_to_shelf_position: Dict[str, str] = {} + + for record in records: + position = record["position"] + tray_type_text = record["tray_type"] + shelf_position = record["shelf_position"] + content = record.get("content", "") + + # 跳过没有 shelf_position 的行 + if not shelf_position: + self._logger.warning(f"跳过没有 shelf_position 的行: {position}") + continue + + # 解析托盘类型代码, 格式: "托盘名称(代码)" 或 "托盘名称(代码) [范围]" + match = re.search(r"\((\d+)\)", tray_type_text) + if not match: + error_msg = f"无法解析托盘类型代码: {tray_type_text}" + self._logger.error(error_msg) + errors.append(error_msg) + continue + + tray_type_code = int(match.group(1)) + + # 映射目标托盘位置: position (TB-x-x) -> synthesis_station_tray_x-x + target_tray = TB_CODE_TO_SYNTHESIS_TRAY.get(position) + if target_tray is None: + error_msg = f"无法映射上料位置 {position} 到合成工站托盘" + self._logger.error(error_msg) + errors.append(error_msg) + continue + + # 映射源托盘位置: shelf_position (x-x) -> shelf_tray_x-x + source_tray = f"shelf_tray_{shelf_position}" + + # 同一批次内目标 TB 位不能重复, 否则 AGV 会对同一工位重复放料 + if target_tray in target_tray_to_position: + existed_position = target_tray_to_position[target_tray] + error_msg = ( + f"检测到重复的目标工位: {existed_position} 与 {position} " + f"均映射到 {target_tray}, 请检查上料文件轮次或 position 配置" + ) + self._logger.error(error_msg) + errors.append(error_msg) + continue + + # 同一批次内货架位也不能重复, 避免对同一源位重复取料 + if source_tray in source_tray_to_shelf_position: + existed_shelf_position = source_tray_to_shelf_position[source_tray] + error_msg = ( + f"检测到重复的货架位: {existed_shelf_position} 与 {shelf_position} " + f"均映射到 {source_tray}, 请检查上料文件中的 shelf_position 配置" + ) + self._logger.error(error_msg) + errors.append(error_msg) + continue + + # 映射物料类型 + material_type = RESOURCE_CODE_TO_MATERIAL_TYPE.get(tray_type_code) + if material_type is None: + tray_type_name = TRAY_CODE_DISPLAY_NAME.get( + tray_type_code, f"未知托盘({tray_type_code})" + ) + self._logger.warning( + f"未知的资源类型 {tray_type_code} ({tray_type_name}), 使用 None" + ) + + # 解析 tray_display_name(托盘中文显示名称) + tray_display_name = TRAY_CODE_DISPLAY_NAME.get(tray_type_code, material_type or f"未知托盘({tray_type_code})") + + # 解析 substance_details(物料详细信息:试剂名、坑位、用量) + substance_details = self._parse_content_to_substance_details( + content, tray_type_code, + ) + + # 构建转运任务 + task = { + "source_tray": source_tray, + "target_tray": target_tray, + "material_type": material_type, + "tray_display_name": tray_display_name, + "substance_details": substance_details, + } + target_tray_to_position[target_tray] = position + source_tray_to_shelf_position[source_tray] = shelf_position + transfer_tasks.append(task) + self._logger.info( + f"转运任务: {source_tray} -> {target_tray}, " + f"物料类型: {material_type}, " + f"显示名: {tray_display_name}, " + f"试剂数: {len(substance_details)}" + ) + + return transfer_tasks, errors + + def _parse_content_to_substance_details( + self, + content: str, + tray_type_code: int, + ) -> List[Dict[str, str]]: + """ + 功能: + 解析上料 content 列,提取试剂/物料详细信息列表。 + content 格式有两种: + 1. 含试剂的托盘: "A1|无水碳酸钾|2000mg;B1|碳酸铯|2000mg" + 2. 耗材托盘: "24" (纯数字,表示数量) + 参数: + content: 上料表格 content 列原始文本。 + tray_type_code: 托盘类型代码(整数)。 + 返回: + List[Dict[str, str]], 每条包含 well、substance、amount 字段。 + 耗材托盘返回空列表(无试剂信息)。 + """ + from ..config.constants import ResourceCode + + if not content or not content.strip(): + return [] + + content = str(content).strip() + + # 判断是否为纯耗材类型(content 为纯数字, 表示数量) + no_substance_codes = { + 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), + } + if tray_type_code in no_substance_codes: + # 耗材托盘:content 为数量,如 "24" + qty = self._safe_int(content) + if qty is not None and qty > 0: + return [{"well": "", "substance": f"×{qty}", "amount": ""}] + return [] + + # 含试剂的托盘: 解析 "A1|无水碳酸钾|2000mg;B1|碳酸铯|2000mg" + details: List[Dict[str, str]] = [] + entries = [seg.strip() for seg in content.split(";") if seg.strip()] + for seg in entries: + parts = [p.strip() for p in seg.split("|")] + if len(parts) >= 3: + details.append({ + "well": parts[0], + "substance": parts[1], + "amount": parts[2], + }) + elif len(parts) == 2: + details.append({ + "well": parts[0], + "substance": parts[1], + "amount": "", + }) + return details + + def _ensure_outer_door_open(self) -> List[str]: + """ + 功能: + 检查过渡舱外门状态, 如果关闭则执行开门操作. + 返回: + List[str], 操作过程中的错误信息列表(空列表表示无错误). + """ + errors: List[str] = [] + try: + device_status_list = self.list_device_status() + outer_door_status = None + for device in device_status_list: + if device.get("device_name") == "过渡舱外门": + outer_door_status = device.get("status") + break + + if outer_door_status == "CLOSE": + self._logger.info("过渡舱外门状态为关闭, 正在开门...") + door_result = self.open_close_door("open") + self._logger.info(f"开门操作完成: {door_result}") + elif outer_door_status == "OPEN": + self._logger.info("过渡舱外门已打开, 无需操作") + else: + self._logger.warning(f"过渡舱外门状态未知: {outer_door_status}") + except Exception as e: + error_msg = f"检查或操作过渡舱外门时发生异常: {str(e)}" + self._logger.error(error_msg) + errors.append(error_msg) + + return errors + + def _execute_agv_transfer_batches( + self, + transfer_tasks: List[Dict[str, Any]], + agv_controller: Any, + *, + block: bool = True, + agv_batch_size: int = 4, + ) -> Tuple[int, List[Dict[str, Any]], List[str]]: + """ + 功能: + 将转运任务按 AGV 单次最大运载量分批执行. + 任意一批失败则立即终止后续批次. + 参数: + transfer_tasks: AGV 转运任务列表. + agv_controller: AGVController 实例. + block: 是否阻塞等待每批完成. + agv_batch_size: AGV 单次最大转运托盘数, 默认 4. + 返回: + Tuple[int, List[Dict], List[str]]: + - transferred_count: 成功转运的托盘数 + - batches_result: 每批次的详情列表 + - errors: 错误信息列表 + """ + batches_result: List[Dict[str, Any]] = [] + errors: List[str] = [] + transferred_count = 0 + + for batch_index in range(0, len(transfer_tasks), agv_batch_size): + batch_tasks = transfer_tasks[batch_index:batch_index + agv_batch_size] + batch_num = batch_index // agv_batch_size + 1 + + self._logger.info( + f"开始执行第 {batch_num} 批转运, 共 {len(batch_tasks)} 个托盘" + ) + + try: + result = agv_controller.batch_transfer_materials( + batch_tasks, block=block + ) + + batch_result = { + "batch_num": batch_num, + "tasks": batch_tasks, + "success": result, + } + batches_result.append(batch_result) + + if result: + transferred_count += len(batch_tasks) + self._logger.info(f"第 {batch_num} 批转运成功") + else: + error_msg = f"第 {batch_num} 批转运失败" + self._logger.error(error_msg) + errors.append(error_msg) + # 立即停止后续批次 + break + + except Exception as e: + error_msg = f"第 {batch_num} 批转运异常: {str(e)}" + self._logger.error(error_msg) + errors.append(error_msg) + batches_result.append({ + "batch_num": batch_num, + "tasks": batch_tasks, + "success": False, + "error": str(e), + }) + # 立即停止后续批次 + break + + return transferred_count, batches_result, errors + + def auto_load_trays_from_agv( + self, + batch_in_file: Optional[str] = None, + *, + block: bool = True, + ) -> JsonDict: + """ + 功能: + 读取 batch_in_tray.xlsx 中的上料信息, 调用 AGV 的 batch_transfer_materials + 函数将托盘从货架转运到合成工站 + 参数: + batch_in_file: 上料信息文件路径, 默认为 sheet/batch_in_tray.xlsx + block: 是否阻塞执行, True 表示等待转运完成 + 返回: + Dict, 包含转运结果信息: + - success: bool, 是否全部成功 + - total_trays: int, 总托盘数 + - transferred_trays: int, 成功转运的托盘数 + - batches: List[Dict], 每批次的转运详情 + - errors: List[str], 错误信息列表 + """ + from pathlib import Path + import openpyxl + import sys + + # 创建 AGV 控制器实例 + sys.path.append(str(Path(__file__).resolve().parent.parent.parent)) + from eit_agv.controller.agv_controller import AGVController + agv_controller = AGVController() + self._logger.info("已创建AGV控制器实例") + + # 1. 确定文件路径 + if batch_in_file is None: + from ..manager.station_manager import MODULE_ROOT + batch_in_path = MODULE_ROOT / "sheet" / "batch_in_tray.xlsx" + else: + batch_in_path = Path(batch_in_file) + + # 2. 读取上料信息 + if not batch_in_path.exists(): + raise FileNotFoundError(f"上料信息文件不存在: {batch_in_path}") + + wb = openpyxl.load_workbook(batch_in_path) + try: + ws, header_row, header_map = self._select_batch_in_sheet(wb) + batch_records = self._iter_batch_in_records(ws, header_row, header_map) + finally: + wb.close() + + # 3. 解析上料信息 -> 转运任务 + transfer_tasks_all, errors = self._build_agv_transfer_tasks(batch_records) + + if len(errors) > 0: + self._logger.error("上料文件校验失败, 停止 AGV 转运: %s", "; ".join(errors)) + return { + "success": False, + "total_trays": len(batch_records), + "transferred_trays": 0, + "batches": [], + "errors": errors, + } + + if len(transfer_tasks_all) == 0: + self._logger.warning("没有有效的转运任务") + return { + "success": False, + "total_trays": 0, + "transferred_trays": 0, + "batches": [], + "errors": errors, + } + + # 4. 检查并打开过渡舱外门 + door_errors = self._ensure_outer_door_open() + errors.extend(door_errors) + + # 5. 分批执行 AGV 转运 + transferred_count, batches_result, transfer_errors = ( + self._execute_agv_transfer_batches( + transfer_tasks_all, agv_controller, block=block + ) + ) + errors.extend(transfer_errors) + + # 6. 返回结果 + success = ( + transferred_count == len(transfer_tasks_all) and len(errors) == 0 + ) + + return { + "success": success, + "total_trays": len(transfer_tasks_all), + "transferred_trays": transferred_count, + "batches": batches_result, + "errors": errors, + } + + # ---------- 下料函数 ---------- + def batch_out_tray( + self, + layout_list: List[JsonDict], + move_type: str = "main_out", + *, + task_id: Optional[int] = None, + poll_interval_s: float = 1.0, + timeout_s: float = 900.0, + ) -> JsonDict: + """ + 功能: + 批量下料,执行前后均等待设备空闲,确保空闲状态下触发下料并等待恢复空闲。 + 参数: + layout_list: List[Dict], 资源位置信息列表, 每项包含: + - layout_code: str, 源位置编码, 如 "N-1-1" + - resource_type: str, 可选, 资源类型 (不需要传入, 会自动获取) + - dst_layout_code: str, 可选, 目标下料位置, 如 "TB-1-1" + 如果未指定, 则按 TB-2-1 到 TB-2-4, 再 TB-1-1 到 TB-1-4 的顺序自动分配 + - task_id: int, 可选, 任务ID (用于记录, 如果在 layout_list 中指定则优先使用) + move_type: str, 下料方式, 默认 "main_out". + task_id: int, 可选, 默认任务ID (如果 layout_list 中的项没有指定 task_id 则使用此值). + poll_interval_s: 轮询间隔秒数. + timeout_s: 等待空闲的超时时长. + 返回: + Dict, 接口响应. + 示例: + layout_list = [ + {"layout_code": "N-1-1", "dst_layout_code": "TB-1-1", "task_id": 123}, + {"layout_code": "N-1-2"} # 自动分配下料位置, 使用默认 task_id + ] + """ + + if layout_list is None: + raise ValidationError("layout_list 不能为空") + if len(layout_list) == 0: + raise ValidationError("layout_list 不能为空") + + start_time = datetime.now().isoformat() + + # 获取资源信息用于查询 resource_type + resource_info = self.get_resource_info() + resource_map = {item["layout_code"]: item for item in resource_info} + + # 定义默认下料位置顺序: TB-2-1 到 TB-2-4, 然后 TB-1-1 到 TB-1-4 + default_dst_positions = [ + "TB-2-1", "TB-2-2", "TB-2-3", "TB-2-4", + "TB-1-1", "TB-1-2", "TB-1-3", "TB-1-4" + ] + dst_position_index = 0 + + processed_layout_list: List[JsonDict] = [] + log_resources: List[JsonDict] = [] + + for item in layout_list: + if item is None: + continue + + layout_code = str(item.get("layout_code", "")).strip() + if layout_code == "": + continue + + # 确定目标下料位置 + dst_layout_code = item.get("dst_layout_code") + if dst_layout_code: + dst_layout_code = str(dst_layout_code).strip() + else: + # 自动分配下料位置 + if dst_position_index >= len(default_dst_positions): + raise ValidationError( + f"下料位置不足, 最多支持 {len(default_dst_positions)} 个托盘下料" + ) + dst_layout_code = default_dst_positions[dst_position_index] + dst_position_index += 1 + + # 从资源信息中获取源位置的 resource_type + resource_type = None + resource_type_name = "" + count = 0 + substance_details = [] + + if layout_code in resource_map: + resource_data = resource_map[layout_code] + resource_type = resource_data.get("resource_type") + resource_type_name = resource_data.get("resource_type_name", "") + count = resource_data.get("count", 0) + substance_details = resource_data.get("substance_details", []) + + if resource_type is None: + self._logger.warning( + "无法从资源信息中获取 layout_code=%s 的 resource_type, 将使用 None", + layout_code + ) + + # 获取 task_id (优先使用 item 中的, 否则使用参数中的) + item_task_id = item.get("task_id", task_id) + + # 构造 API 需要的格式 + processed_item = { + "layout_code": layout_code, + "resource_type": resource_type, + "dst_layout_code": dst_layout_code + } + processed_layout_list.append(processed_item) + + # 构造日志记录格式 + log_resource = { + "layout_code": layout_code, + "count": count, + "resource_type": resource_type, + "resource_type_name": resource_type_name, + "substance_details": substance_details, + "task_id": item_task_id, + "dst_layout_code": dst_layout_code + } + log_resources.append(log_resource) + + self._logger.debug( + "下料配置: layout_code=%s, dst_layout_code=%s, resource_type=%s, task_id=%s", + layout_code, dst_layout_code, resource_type, item_task_id + ) + + if len(processed_layout_list) == 0: + raise ValidationError("layout_list 解析后为空") + + self.wait_idle(stage="下料前", poll_interval_s=poll_interval_s, timeout_s=timeout_s) + self._logger.info("仪器空闲,开始下料") + resp = self._call_with_relogin(self._client.batch_out_tray, processed_layout_list, move_type) + self._logger.info("下料指令已发送,等待仪器回到空闲") + self.wait_idle(stage="下料后", poll_interval_s=poll_interval_s, timeout_s=timeout_s) + self._logger.info("下料完成,共 %d 个托盘", len(processed_layout_list)) + + end_time = datetime.now().isoformat() + + # 保存下料日志 + if self._data_manager: + log_data = { + "start_time": start_time, + "end_time": end_time, + "resources": log_resources + } + self._data_manager.save_batch_out_tray_log(log_data) + + return resp + + # ---------- AGV 自动下料函数 ---------- + def auto_unload_trays_to_agv( + self, + batch_out_file: Optional[str] = None, + *, + block: bool = True, + auto_run_analysis: bool = True, + ) -> JsonDict: + """ + 功能: + 读取 batch_out_tray.json 中的下料信息, 调用 AGV 的 batch_transfer_materials + 函数将托盘从合成工站转运到目标位置. + 转运顺序规则: + - 闪滤瓶外瓶托盘(检测物料)始终排在任务列表最前面, 优先进入前几批 + - 同一批次内, 分析工站任务也排在货架任务前面, AGV 在同一趟行程中先 + 卸货到分析工站再卸货到货架 + - 每批最多携带 4 个托盘, 任意批次失败则立即中止后续批次 + 当分析物料(闪滤瓶外瓶托盘)全部转运到分析工站后, 若 auto_run_analysis + 为 True, 则立即调用 AnalysisStationController.run_analysis 提交分析任务, + 无需等待后续货架物料转运完成. + 参数: + batch_out_file: 下料信息文件路径, 默认为 data/operations/batch_out_tray.json + block: 是否阻塞执行, True 表示等待转运完成 + auto_run_analysis: 是否在分析物料转运完成后自动执行分析, 默认 True + 返回: + Dict, 包含转运结果信息: + - success: bool, 是否全部成功 + - total_trays: int, 总托盘数 + - transferred_trays: int, 成功转运的托盘数 + - batches: List[Dict], 每批次的转运详情 + - errors: List[str], 错误信息列表 + - analysis_results: Dict[str, Dict], 分析任务提交结果, + key 为 task_id 字符串, value 为 run_analysis 返回值. + 仅在存在分析物料且 auto_run_analysis=True 时非空. + 分析失败不影响 success 状态. + """ + from pathlib import Path + import json + import sys + + # 创建AGV控制器实例 + sys.path.append(str(Path(__file__).resolve().parent.parent.parent)) + from eit_agv.controller.agv_controller import AGVController + agv_controller = AGVController() + self._logger.info("已创建AGV控制器实例") + + # 1. 确定文件路径 + if batch_out_file is None: + if self._data_manager is None: + raise ValueError("数据管理器未启用, 请指定 batch_out_file 参数") + batch_out_path = self._data_manager.operations_dir / "batch_out_tray.json" + else: + batch_out_path = Path(batch_out_file) + + # 2. 读取下料信息 + if not batch_out_path.exists(): + raise FileNotFoundError(f"下料信息文件不存在: {batch_out_path}") + + with batch_out_path.open("r", encoding="utf-8") as f: + batch_out_data = json.load(f) + + resources = batch_out_data.get("resources", []) + if len(resources) == 0: + self._logger.warning("下料信息为空, 无需转运") + return { + "success": True, + "total_trays": 0, + "transferred_trays": 0, + "batches": [], + "errors": [], + } + + manifest = self.build_unload_transfer_manifest(resources) + return agv_controller.transfer_manifest( + manifest, + block=block, + auto_run_analysis=auto_run_analysis, + ) + + def get_task_tray_mapping(self, task_id: int) -> JsonDict: + """ + 功能: + 获取指定任务的托盘编号信息, 提取反应试管托盘与样品托盘. + 参数: + task_id: 任务 id. + 返回: + Dict[str, Any], 包含 reaction_trays 与 sampling_trays 两个列表. + """ + resp = self._call_with_relogin(self._client.get_task_info, task_id) + data = resp.get("result") or resp.get("data") or resp + + units = data.get("layout_list") or data.get("unit_list") or [] + if not isinstance(units, list) or not units: + raise ValidationError(f"任务 {task_id} 缺少 unit_list/layout_list, resp={resp}") + + reaction_trays: set[str] = set() + sampling_trays: set[str] = set() + + for unit in units: + layout_code = str(unit.get("layout_code") or "") + if ":" in layout_code: + reaction_trays.add(layout_code.split(":", 1)[0]) # 反应试管托盘 + + if str(unit.get("unit_type")) == "exp_filtering_sample": + process_json = unit.get("process_json") or {} + sampling_layout_code = str(process_json.get("sampling_layout_code") or "") + if ":" in sampling_layout_code: + sampling_trays.add(sampling_layout_code.split(":", 1)[0]) # 样品托盘 + + result = { + "task_id": task_id, + "reaction_trays": sorted(reaction_trays), + "sampling_trays": sorted(sampling_trays), + } + self._logger.debug( + "任务 %s 托盘提取完成 reaction_trays=%s sampling_trays=%s", + task_id, + result["reaction_trays"], + result["sampling_trays"], + ) + return result + + def list_empty_trays(self) -> List[JsonDict]: + """ + 功能: + 查询资源信息并返回可用数量为0的托盘位置. + 参数: + 无 + 返回: + List[Dict[str, Any]], 每项包含托盘布局编码及托盘类型信息. + """ + rows = self.get_resource_info() + empty_trays: List[JsonDict] = [] + + for row in rows: + count_val = row.get("count") + if isinstance(count_val, int) and count_val == 0: + empty_trays.append( + { + "layout_code": row.get("layout_code"), + "resource_type": row.get("resource_type"), + "resource_type_name": row.get("resource_type_name", ""), + } + ) + + self._logger.debug("可用资源为0的托盘数量=%s", len(empty_trays)) + return empty_trays + + def _resolve_completed_task_id_for_unload(self, task_id: Optional[int]) -> int: + """ + 功能: + 为下料流程解析目标任务 ID;未传入时自动选择最近完成的任务。 + 参数: + task_id: 可选任务 ID。 + 返回: + int, 目标任务 ID。 + """ + if task_id is not None: + return int(task_id) + + tasks_resp = self.get_task_list(sort="desc", offset=0, limit=50) + task_list = ( + tasks_resp.get("task_list") + or tasks_resp.get("result", {}).get("task_list") + or tasks_resp.get("data", {}).get("task_list") + ) + completed_ids: List[int] = [] + if isinstance(task_list, list): + for item in task_list: + cur_id = item.get("task_id") + cur_status = item.get("status") + if isinstance(cur_id, int) and cur_status == int(TaskStatus.COMPLETED): + completed_ids.append(cur_id) + + if len(completed_ids) == 0: + raise ValidationError("未找到下料的已完成任务") + + target_task_id = max(completed_ids) + self._logger.info("未传入 task_id, 自动选择最近完成的任务: %s", target_task_id) + return target_task_id + + @staticmethod + def _is_excluded_unload_code(code: Any) -> bool: + """ + 功能: + 判断下料位置是否应按前缀规则排除。 + 参数: + code: 任意位置编码对象。 + 返回: + bool, True 表示应排除。 + """ + if code is None: + return False + text = str(code).strip().upper() + return any(text.startswith(prefix) for prefix in ("MSB", "MS", "AS", "TS")) + + def _build_task_and_empty_unload_plan( + self, + task_id: Optional[int] = None, + *, + poll_interval_s: float = 1.0, + ignore_missing: bool = True, + timeout_s: float = 900.0, + ) -> Tuple[int, List[JsonDict], List[JsonDict], JsonDict]: + """ + 功能: + 构建“任务托盘 + 空托盘”下料计划,并同步生成供 AGV 消费的 manifest 资源明细。 + 参数: + task_id: 任务 ID;为空时自动选择最近完成任务。 + poll_interval_s: 轮询空闲间隔。 + ignore_missing: 是否忽略资源列表中不存在的位置。 + timeout_s: 等待空闲超时。 + 返回: + Tuple[target_task_id, layout_list, manifest_resources, summary] + """ + self.wait_idle(stage="等待任务结束", poll_interval_s=poll_interval_s, timeout_s=timeout_s) + + target_task_id = self._resolve_completed_task_id_for_unload(task_id) + self._logger.info("准备下料") + + mapping = self.get_task_tray_mapping(target_task_id) + reaction_trays = mapping.get("reaction_trays") or [] + sampling_trays = mapping.get("sampling_trays") or [] + + raw_task_tray_codes = {(code or "").strip() for code in reaction_trays if (code or "").strip()} + raw_task_tray_codes |= {(code or "").strip() for code in sampling_trays if (code or "").strip()} + + empty_trays = self.list_empty_trays() + raw_empty_codes = { + str(item.get("layout_code")).strip() + for item in empty_trays + if item.get("layout_code") + } + + excluded_codes = { + code + for code in (raw_task_tray_codes | raw_empty_codes) + if self._is_excluded_unload_code(code) + } + if excluded_codes: + self._logger.info("按前缀规则忽略托盘: %s", sorted(excluded_codes)) + + task_tray_codes = {code for code in raw_task_tray_codes if code not in excluded_codes} + empty_codes = {code for code in raw_empty_codes if code not in excluded_codes} + target_codes = {*(task_tray_codes or []), *empty_codes} + target_codes = {str(code).strip() for code in target_codes if str(code).strip()} + + resource_rows = self.get_resource_info() + resource_map = { + str(row.get("layout_code") or "").strip(): row + for row in resource_rows + if row.get("layout_code") + } + existing_codes = set(resource_map.keys()) + + magnet_type = int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML) + magnet_full_count = TraySpec.TEST_TUBE_MAGNET_TRAY_2ML[0] * TraySpec.TEST_TUBE_MAGNET_TRAY_2ML[1] + used_magnet_codes: set[str] = set() + for row in resource_rows: + if row.get("resource_type") == magnet_type: + row_count = row.get("count") + if isinstance(row_count, int) and row_count != magnet_full_count: + layout_code = str(row.get("layout_code") or "").strip() + if layout_code and not self._is_excluded_unload_code(layout_code): + used_magnet_codes.add(layout_code) + + if used_magnet_codes: + self._logger.info( + "检测到已使用的磁子托盘(磁子数量不等于 %s), 纳入下料: %s", + magnet_full_count, + sorted(used_magnet_codes), + ) + target_codes |= used_magnet_codes + + if not target_codes: + raise ValidationError(f"任务 {target_task_id} 未找到需要下料的托盘位置") + + missing_codes = sorted(code for code in target_codes if code not in existing_codes) + if missing_codes: + if ignore_missing: + self._logger.warning("下料位置未在资源列表, 已忽略: %s", missing_codes) + target_codes = {code for code in target_codes if code not in missing_codes} + else: + raise ValidationError(f"下料位置不存在: {missing_codes}") + + if not target_codes: + raise ValidationError("可执行下料的位置为空, 请检查输入或资源列表") + + empty_set = {code for code in empty_codes if code} + empty_only = sorted(empty_set - task_tray_codes) + default_dst_positions = [ + "TB-2-1", "TB-2-2", "TB-2-3", "TB-2-4", + "TB-1-1", "TB-1-2", "TB-1-3", "TB-1-4", + ] + + layout_list: List[JsonDict] = [] + manifest_resources: List[JsonDict] = [] + for index, code in enumerate(sorted(target_codes)): + if index >= len(default_dst_positions): + raise ValidationError(f"下料位置不足, 最多支持 {len(default_dst_positions)} 个托盘下料") + + dst_layout_code = default_dst_positions[index] + layout_item: JsonDict = { + "layout_code": code, + "dst_layout_code": dst_layout_code, + } + if code in task_tray_codes: + layout_item["task_id"] = target_task_id + layout_list.append(layout_item) + + resource_row = resource_map.get(code, {}) + manifest_resources.append( + { + "layout_code": code, + "count": resource_row.get("count", 0), + "resource_type": resource_row.get("resource_type"), + "resource_type_name": resource_row.get("resource_type_name", ""), + "substance_details": resource_row.get("substance_details", []), + "task_id": layout_item.get("task_id"), + "dst_layout_code": dst_layout_code, + } + ) + + self._logger.info( + "准备批量下料 task_id=%s 任务托盘=%s 空托盘=%s 实际下料位置=%s", + target_task_id, + sorted(task_tray_codes), + empty_only, + sorted(target_codes), + ) + + summary = { + "task_id": target_task_id, + "task_tray_codes": sorted(task_tray_codes), + "empty_tray_codes": empty_only, + "target_codes": sorted(target_codes), + "excluded_codes": sorted(excluded_codes), + "missing_codes": missing_codes, + "used_magnet_codes": sorted(used_magnet_codes), + } + return target_task_id, layout_list, manifest_resources, summary + + def build_unload_transfer_manifest(self, resources: List[JsonDict]) -> JsonDict: + """ + 功能: + 将手套箱下料明细转换为 AGV 可消费的标准转运 manifest。 + 参数: + resources: 下料资源明细列表,字段格式与 batch_out_tray 日志一致。 + 返回: + Dict, 包含 transfer_tasks、analysis_task_ids 等标准化信息。 + """ + from ..config.constants import ( + ResourceCode, + RESOURCE_CODE_TO_MATERIAL_TYPE, + TB_CODE_TO_SYNTHESIS_TRAY, + SHELF_TRAY_POSITIONS, + ANALYSIS_STATION_TRAY_POSITIONS, + TRAY_CODE_DISPLAY_NAME, + ) + + analysis_tasks: List[JsonDict] = [] + shelf_tasks: List[JsonDict] = [] + analysis_task_ids: set[str] = set() + shelf_position_index = 0 + analysis_position_index = 0 + errors: List[str] = [] + + for resource in resources: + dst_layout_code = str(resource.get("dst_layout_code") or "").strip() + resource_type = resource.get("resource_type") + resource_type_name = resource.get("resource_type_name", "") + + if resource_type is None or dst_layout_code == "": + continue + + source_tray = TB_CODE_TO_SYNTHESIS_TRAY.get(dst_layout_code) + if source_tray is None: + error_msg = f"无法映射下料位置 {dst_layout_code} 到合成工站托盘" + self._logger.error(error_msg) + errors.append(error_msg) + continue + + material_type = RESOURCE_CODE_TO_MATERIAL_TYPE.get(resource_type) + if material_type is None: + self._logger.warning( + "未知的资源类型 %s (%s), 使用 None", + resource_type, + resource_type_name, + ) + + # 生成 tray_display_name 和 substance_details 供 AGV Deck 显示 + tray_display_name = TRAY_CODE_DISPLAY_NAME.get( + resource_type, resource_type_name or (material_type or "") + ) + raw_substance_details = resource.get("substance_details") or [] + # 将下料资源中的 substance_details 转换为上料格式 (well/substance/amount) + substance_details = [] + for sd in raw_substance_details: + if isinstance(sd, dict): + substance_details.append({ + "well": sd.get("well", sd.get("slot", "")), + "substance": sd.get("substance", sd.get("name", "")), + "amount": sd.get("amount", sd.get("value", "")), + }) + + if resource_type == int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): + if analysis_position_index >= len(ANALYSIS_STATION_TRAY_POSITIONS): + error_msg = f"分析工站托盘位置已用尽, 无法放置 {resource_type_name}" + self._logger.error(error_msg) + errors.append(error_msg) + continue + + target_tray = ANALYSIS_STATION_TRAY_POSITIONS[analysis_position_index] + analysis_position_index += 1 + analysis_tasks.append( + { + "source_tray": source_tray, + "target_tray": target_tray, + "material_type": material_type, + "tray_display_name": tray_display_name, + "substance_details": substance_details, + } + ) + raw_task_id = resource.get("task_id") + if raw_task_id is not None: + analysis_task_ids.add(str(raw_task_id)) + else: + if shelf_position_index >= len(SHELF_TRAY_POSITIONS): + error_msg = f"货架托盘位置已用尽, 无法放置 {resource_type_name}" + self._logger.error(error_msg) + errors.append(error_msg) + continue + + target_tray = SHELF_TRAY_POSITIONS[shelf_position_index] + shelf_position_index += 1 + shelf_tasks.append( + { + "source_tray": source_tray, + "target_tray": target_tray, + "material_type": material_type, + "tray_display_name": tray_display_name, + "substance_details": substance_details, + } + ) + + transfer_tasks = analysis_tasks + shelf_tasks + success = len(transfer_tasks) > 0 and len(errors) == 0 + return { + "manifest_type": "eit_synthesis_unload", + "source_device": "eit_synthesis_station", + "transfer_tasks": transfer_tasks, + "analysis_task_ids": sorted(analysis_task_ids), + "analysis_tasks_count": len(analysis_tasks), + "shelf_tasks_count": len(shelf_tasks), + "total_trays": len(resources), + "resources": resources, + "errors": errors, + "success": success, + "message": "已生成 AGV 下料 manifest" if success else "生成 AGV 下料 manifest 时存在错误", + } + + def unload_task_and_empty_trays_return_manifest( + self, + task_id: Optional[int] = None, + *, + poll_interval_s: float = 1.0, + ignore_missing: bool = True, + timeout_s: float = 900.0, + move_type: str = "main_out", + ) -> JsonDict: + """ + 功能: + 执行手套箱本地下料(任务托盘 + 空托盘),并返回标准 AGV manifest。 + 适合前端先调用该函数,再将返回值传给 AGV 的 `transfer_manifest`。 + 参数: + task_id: 任务 ID;为空时自动选择最近完成任务。 + poll_interval_s: 等待任务结束轮询间隔。 + ignore_missing: 是否忽略资源列表中不存在的位置。 + timeout_s: 等待任务结束超时。 + move_type: 下料方式。 + 返回: + Dict, 包含 batch_out_result 与 AGV manifest。 + """ + target_task_id, layout_list, manifest_resources, summary = self._build_task_and_empty_unload_plan( + task_id, + poll_interval_s=poll_interval_s, + ignore_missing=ignore_missing, + timeout_s=timeout_s, + ) + batch_out_result = self.batch_out_tray( + layout_list, + move_type=move_type, + task_id=None, + poll_interval_s=poll_interval_s, + timeout_s=timeout_s, + ) + manifest = self.build_unload_transfer_manifest(manifest_resources) + manifest.update( + { + "task_id": target_task_id, + "layout_list": layout_list, + "selection": summary, + "batch_out_result": batch_out_result, + "message": "手套箱下料完成,已返回 AGV manifest", + } + ) + return manifest + + def batch_out_task_and_empty_trays(self, task_id: Optional[int] = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out") -> JsonDict: + """ + 功能: + 汇总指定或自动选择的已完成任务涉及托盘与当前空托盘, 校验存在性后执行批量下料 + 参数: + task_id: 任务 id, None 时自动选择最近完成的任务 + poll_interval_s: 轮询空闲状态的时间间隔(秒) + ignore_missing: True 时忽略未在资源列表中的托盘并记录 warning, False 时抛错终止 + timeout_s: 等待空闲的超时时间(秒) + move_type: 下料方式, 默认 "main_out" + 返回: + Dict, 执行 batch_out_tray 的接口响应 + """ + _, layout_list, _, _ = self._build_task_and_empty_unload_plan( + task_id, + poll_interval_s=poll_interval_s, + ignore_missing=ignore_missing, + timeout_s=timeout_s, + ) + + resp = self.batch_out_tray( + layout_list, + move_type=move_type, + task_id=None, # 已在 layout_list 中指定 + poll_interval_s=poll_interval_s, + timeout_s=timeout_s + ) + + return resp + + def batch_out_task_trays(self, task_id: Optional[int] = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out") -> JsonDict: + """ + 功能: + 下料指定或自动选择的已完成任务涉及的物料托盘 + 参数: + task_id: 任务 id, None 时自动选择最近完成的任务 + poll_interval_s: 轮询空闲状态的时间间隔(秒) + ignore_missing: True 时忽略未在资源列表中的托盘并记录 warning, False 时抛错终止 + timeout_s: 等待空闲的超时时间(秒) + move_type: 下料方式, 默认 "main_out" + 返回: + Dict, 执行 batch_out_tray 的接口响应 + """ + + self.wait_idle(stage="等待任务结束", poll_interval_s=poll_interval_s, timeout_s=timeout_s) #等待任务结束后再提取托盘信息,否则会提取不到 + + target_task_id = task_id + if target_task_id is None: + tasks_resp = self.get_task_list(sort="desc", offset=0, limit=50) + task_list = tasks_resp.get("task_list") or tasks_resp.get("result", {}).get("task_list") or tasks_resp.get("data", {}).get("task_list") + completed_ids: List[int] = [] + if isinstance(task_list, list): + for item in task_list: + cur_id = item.get("task_id") + cur_status = item.get("status") + if isinstance(cur_id, int) and cur_status == int(TaskStatus.COMPLETED): + completed_ids.append(cur_id) + if len(completed_ids) == 0: + raise ValidationError("未找到下料的已完成任务") + target_task_id = max(completed_ids) + self._logger.info("未传入 task_id, 自动选择最近完成的任务: %s", target_task_id) + + self._logger.info("准备下料任务物料托盘") + + mapping = self.get_task_tray_mapping(target_task_id) + reaction_trays = mapping.get("reaction_trays") or [] + sampling_trays = mapping.get("sampling_trays") or [] + + skip_prefixes = ("MSB", "MS", "AS", "TS") + + def _is_excluded_code(code: Any) -> bool: + if code is None: + return False + text = str(code).strip().upper() + return any(text.startswith(prefix) for prefix in skip_prefixes) + + raw_task_tray_codes = {(code or "").strip() for code in reaction_trays if (code or "").strip()} + raw_task_tray_codes |= {(code or "").strip() for code in sampling_trays if (code or "").strip()} + + excluded_codes = {code for code in raw_task_tray_codes if _is_excluded_code(code)} + if excluded_codes: + self._logger.info("按前缀规则忽略托盘: %s", sorted(excluded_codes)) + + task_tray_codes = {code for code in raw_task_tray_codes if code not in excluded_codes} + target_codes = {str(code).strip() for code in task_tray_codes if str(code).strip()} + + if not target_codes: + raise ValidationError(f"任务 {target_task_id} 未找到需要下料的物料托盘位置") + + resource_rows = self.get_resource_info() + existing_codes = {str(row.get("layout_code") or "").strip() for row in resource_rows if row.get("layout_code")} + missing_codes = sorted(code for code in target_codes if code not in existing_codes) + + if missing_codes: + if ignore_missing: + self._logger.warning("下料位置未在资源列表, 已忽略: %s", missing_codes) + target_codes = {code for code in target_codes if code not in missing_codes} + else: + raise ValidationError(f"下料位置不存在: {missing_codes}") + + if not target_codes: + raise ValidationError("可执行下料的位置为空, 请检查输入或资源列表") + + self._logger.info( + "准备批量下料任务物料托盘 task_id=%s 任务托盘=%s 实际下料位置=%s", + target_task_id, + sorted(task_tray_codes), + sorted(target_codes), + ) + + # 构造新的 layout_list 格式,附上 task_id + layout_list = [{"layout_code": code, "task_id": target_task_id} for code in sorted(target_codes)] + resp = self.batch_out_tray( + layout_list, + move_type=move_type, + task_id=target_task_id, + poll_interval_s=poll_interval_s, + timeout_s=timeout_s + ) + + return resp + + def batch_out_task_and_chemical_trays(self, task_id: Optional[int] = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out") -> JsonDict: + """ + 功能: + 下料指定或自动选择的已完成任务涉及的物料托盘, 以及该任务中使用过的药品所在的托盘 + 参数: + task_id: 任务 id, None 时自动选择最近完成的任务 + poll_interval_s: 轮询空闲状态的时间间隔(秒) + ignore_missing: True 时忽略未在资源列表中的托盘并记录 warning, False 时抛错终止 + timeout_s: 等待空闲的超时时间(秒) + move_type: 下料方式, 默认 "main_out" + 返回: + Dict, 执行 batch_out_tray 的接口响应 + """ + + self.wait_idle(stage="等待任务结束", poll_interval_s=poll_interval_s, timeout_s=timeout_s) + + target_task_id = task_id + if target_task_id is None: + tasks_resp = self.get_task_list(sort="desc", offset=0, limit=50) + task_list = tasks_resp.get("task_list") or tasks_resp.get("result", {}).get("task_list") or tasks_resp.get("data", {}).get("task_list") + completed_ids: List[int] = [] + if isinstance(task_list, list): + for item in task_list: + cur_id = item.get("task_id") + cur_status = item.get("status") + if isinstance(cur_id, int) and cur_status == int(TaskStatus.COMPLETED): + completed_ids.append(cur_id) + if len(completed_ids) == 0: + raise ValidationError("未找到下料的已完成任务") + target_task_id = max(completed_ids) + self._logger.info("未传入 task_id, 自动选择最近完成的任务: %s", target_task_id) + + self._logger.info("准备下料任务物料托盘和药品托盘") + + # 获取任务物料托盘 + mapping = self.get_task_tray_mapping(target_task_id) + reaction_trays = mapping.get("reaction_trays") or [] + sampling_trays = mapping.get("sampling_trays") or [] + + # 获取任务中使用的化学品名称 + task_info_resp = self.get_task_info(target_task_id) + task_data = task_info_resp.get("result") or task_info_resp.get("data") or task_info_resp + units = task_data.get("layout_list") or task_data.get("unit_list") or [] + + used_chemicals: set[str] = set() + if isinstance(units, list): + for unit in units: + process_json = unit.get("process_json") or {} + substance = str(process_json.get("substance") or "").strip() + if substance: + used_chemicals.add(substance) + + self._logger.info("任务 %s 使用的化学品: %s", target_task_id, sorted(used_chemicals)) + + # 获取资源信息, 找到这些化学品所在的托盘 + resource_rows = self.get_resource_info() + chemical_trays: set[str] = set() + + for row in resource_rows: + layout_code = str(row.get("layout_code") or "").strip() + substance_details = row.get("substance_details") or [] + + if isinstance(substance_details, list): + for detail in substance_details: + substance = str(detail.get("substance") or "").strip() + if substance in used_chemicals: + chemical_trays.add(layout_code) + break # 找到一个匹配的化学品就添加该托盘 + + self._logger.info("任务 %s 使用的化学品所在托盘: %s", target_task_id, sorted(chemical_trays)) + + # 合并所有托盘 + skip_prefixes = ("MSB", "MS", "AS", "TS") + + def _is_excluded_code(code: Any) -> bool: + if code is None: + return False + text = str(code).strip().upper() + return any(text.startswith(prefix) for prefix in skip_prefixes) + + raw_task_tray_codes = {(code or "").strip() for code in reaction_trays if (code or "").strip()} + raw_task_tray_codes |= {(code or "").strip() for code in sampling_trays if (code or "").strip()} + raw_task_tray_codes |= chemical_trays + + excluded_codes = {code for code in raw_task_tray_codes if _is_excluded_code(code)} + if excluded_codes: + self._logger.info("按前缀规则忽略托盘: %s", sorted(excluded_codes)) + + task_tray_codes = {code for code in raw_task_tray_codes if code not in excluded_codes} + target_codes = {str(code).strip() for code in task_tray_codes if str(code).strip()} + + if not target_codes: + raise ValidationError(f"任务 {target_task_id} 未找到需要下料的物料托盘或药品托盘位置") + + existing_codes = {str(row.get("layout_code") or "").strip() for row in resource_rows if row.get("layout_code")} + missing_codes = sorted(code for code in target_codes if code not in existing_codes) + + if missing_codes: + if ignore_missing: + self._logger.warning("下料位置未在资源列表, 已忽略: %s", missing_codes) + target_codes = {code for code in target_codes if code not in missing_codes} + else: + raise ValidationError(f"下料位置不存在: {missing_codes}") + + if not target_codes: + raise ValidationError("可执行下料的位置为空, 请检查输入或资源列表") + + self._logger.info( + "准备批量下料任务物料托盘和药品托盘 task_id=%s 任务托盘=%s 药品托盘=%s 实际下料位置=%s", + target_task_id, + sorted(set(reaction_trays) | set(sampling_trays)), + sorted(chemical_trays), + sorted(target_codes), + ) + + # 构造新的 layout_list 格式, 附上 task_id + layout_list = [{"layout_code": code, "task_id": target_task_id} for code in sorted(target_codes)] + resp = self.batch_out_tray( + layout_list, + move_type=move_type, + task_id=target_task_id, + poll_interval_s=poll_interval_s, + timeout_s=timeout_s + ) + + return resp + + def batch_out_empty_trays(self, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out") -> JsonDict: + """ + 功能: + 下料当前所有空托盘 + 参数: + poll_interval_s: 轮询空闲状态的时间间隔(秒) + ignore_missing: True 时忽略未在资源列表中的托盘并记录 warning, False 时抛错终止 + timeout_s: 等待空闲的超时时间(秒) + move_type: 下料方式, 默认 "main_out" + 返回: + Dict, 执行 batch_out_tray 的接口响应 + """ + self.wait_idle(stage="等待任务结束", poll_interval_s=poll_interval_s, timeout_s=timeout_s) #等待任务结束后再提取托盘信息,否则会提取不到 + + self._logger.info("准备下料空托盘") + + skip_prefixes = ("MSB", "MS", "AS", "TS") + + def _is_excluded_code(code: Any) -> bool: + if code is None: + return False + text = str(code).strip().upper() + return any(text.startswith(prefix) for prefix in skip_prefixes) + + empty_trays = self.list_empty_trays() + raw_empty_codes = {str(item.get("layout_code")).strip() for item in empty_trays if item.get("layout_code")} + + excluded_codes = {code for code in raw_empty_codes if _is_excluded_code(code)} + if excluded_codes: + self._logger.info("按前缀规则忽略托盘: %s", sorted(excluded_codes)) + + empty_codes = {code for code in raw_empty_codes if code not in excluded_codes} + target_codes = {str(code).strip() for code in empty_codes if str(code).strip()} + + if not target_codes: + raise ValidationError("未找到需要下料的空托盘位置") + + resource_rows = self.get_resource_info() + existing_codes = {str(row.get("layout_code") or "").strip() for row in resource_rows if row.get("layout_code")} + missing_codes = sorted(code for code in target_codes if code not in existing_codes) + + if missing_codes: + if ignore_missing: + self._logger.warning("下料位置未在资源列表, 已忽略: %s", missing_codes) + target_codes = {code for code in target_codes if code not in missing_codes} + else: + raise ValidationError(f"下料位置不存在: {missing_codes}") + + if not target_codes: + raise ValidationError("可执行下料的位置为空, 请检查输入或资源列表") + + # 构造新的 layout_list 格式,空托盘不附 task_id + layout_list = [{"layout_code": code} for code in sorted(target_codes)] + + # 构建源位置到目标过渡舱位置的映射,用于日志显示 + default_dst_positions = [ + "TB-2-1", "TB-2-2", "TB-2-3", "TB-2-4", + "TB-1-1", "TB-1-2", "TB-1-3", "TB-1-4" + ] + tray_to_dst_map = {} + for idx, item in enumerate(layout_list): + if idx < len(default_dst_positions): + tray_to_dst_map[item["layout_code"]] = default_dst_positions[idx] + + # 格式化显示:源位置 -> 目标位置 + dst_mapping_str = ", ".join([f"{src}->{dst}" for src, dst in sorted(tray_to_dst_map.items())]) + + self._logger.info( + "准备批量下料空托盘 空托盘=%s 实际下料位置=%s", + sorted(empty_codes), + dst_mapping_str, + ) + resp = self.batch_out_tray( + layout_list, + move_type=move_type, + task_id=None, + poll_interval_s=poll_interval_s, + timeout_s=timeout_s + ) + + 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) + + # ---------- 控制W1货架 ---------- + def control_w1_shelf(self, position: str, action: str, *, station: str = "FSY") -> JsonDict: + """ + 功能: + 控制W1货架的推出或复位操作 + 参数: + position: str, 货架位置, 可选值: "W-1-1", "W-1-3", "W-1-5", "W-1-7" + W-1-1 控制 W-1-1 和 W-1-2 + W-1-3 控制 W-1-3 和 W-1-4 + W-1-5 控制 W-1-5 和 W-1-6 + W-1-7 控制 W-1-7 和 W-1-8 + action: str, 动作类型, "home" 表示复位, "outside" 表示推出 + station: str, 站点编码, 默认 "FSY" + 返回: + Dict, 接口响应 + """ + # 位置映射到num参数 + position_map = { + "W-1-1": 1, + "W-1-3": 3, + "W-1-5": 5, + "W-1-7": 7 + } + + if position not in position_map: + raise ValidationError(f"position 必须是 {list(position_map.keys())} 之一") + if action not in ("home", "outside"): + raise ValidationError("action 必须是 'home' 或 'outside'") + + num = position_map[position] + op = action # op与action保持一致 + + self._logger.info("控制W1货架, position=%s, action=%s, station=%s", position, action, station) + return self._call_with_relogin(self._client.single_control_w1_shelf, station, action, op, num) + + # ---------- 任务模块 ---------- 未完成 + def add_task(self, payload: JsonDict) -> JsonDict: + resp = self._call_with_relogin(self._client.add_task, payload) + + # 自动保存任务记录 + if self._data_manager: + # 从响应中提取 task_id + task_id = resp.get("task_id") or resp.get("result", {}).get("task_id") or resp.get("data", {}).get("task_id") + if task_id: + task_id_str = str(task_id) + self._data_manager.create_task_record(task_id_str, { + "task_id": task_id_str, + "status": "UNSTARTED", + "created_at": datetime.now().isoformat() + }) + # 保存任务 Payload + self._data_manager.save_task_payload(task_id_str, payload) + + return resp + + def start_task(self, task_id: Optional[int] = None, *, check_glovebox_env: bool = True, water_limit_ppm: float = 10.0, oxygen_limit_ppm: float = 10.0) -> JsonDict: + """ + 功能: + 确认设备空闲且手套箱环境达标后, 启动指定任务或task_id最大的任务 + 参数: + task_id: 可选int, 指定任务id, None时自动查找task_id最大的任务 + check_glovebox_env: bool, 启动前是否校验手套箱水氧 + water_limit_ppm: float, 手套箱水含量上限(ppm) + oxygen_limit_ppm: float, 手套箱氧含量上限(ppm) + 返回: + Dict[str, Any], StartTask接口的响应结果 + """ + state = self.station_state() + if state != int(StationState.IDLE): + raise ValidationError("设备未处于空闲状态, 暂不可启动任务") + + if check_glovebox_env is True: + env_info = self.get_glovebox_env() + water_raw = env_info.get("water_content") + oxygen_raw = env_info.get("oxygen_content") + if water_raw is None or oxygen_raw is None: + raise ValidationError("手套箱水氧数据缺失, 已停止启动流程") + + def _to_float(value: Any) -> float: + if isinstance(value, (int, float)): + return float(value) + if isinstance(value, str): + text = value.strip() + if text != "": + try: + return float(text) + except Exception: + pass + raise ValidationError(f"手套箱环境数值格式异常 {value}") + + water_ppm = _to_float(water_raw) + oxygen_ppm = _to_float(oxygen_raw) + + if water_ppm >= float(water_limit_ppm): + raise ValidationError(f"手套箱水含量超限, 当前={water_ppm}ppm, 阈值={water_limit_ppm}ppm") + if oxygen_ppm >= float(oxygen_limit_ppm): + raise ValidationError(f"手套箱氧含量超限, 当前={oxygen_ppm}ppm, 阈值={oxygen_limit_ppm}ppm") + + target_task_id = task_id + if target_task_id is None: + # 通过任务列表取task_id最大的一条 + first_resp = self.get_task_list(sort="desc", offset=0, limit=1) + task_sums = self._extract_task_sums(first_resp) + if task_sums is None: + task_sums = 50 # 回退拉取最近任务, 避免列表为空 + tasks_resp = self.get_task_list(sort="desc", offset=0, limit=task_sums) + task_list = ( + tasks_resp.get("task_list") + or tasks_resp.get("result", {}).get("task_list") + or tasks_resp.get("data", {}).get("task_list") + ) + if task_list is None or len(task_list) == 0: + raise ValidationError("未找到任务记录, 请先创建任务") + + valid_tasks: List[JsonDict] = [] + for item in task_list: + cur_id = item.get("task_id") + if isinstance(cur_id, int): + valid_tasks.append(item) + + if len(valid_tasks) == 0: + raise ValidationError("任务列表缺少有效的task_id字段") + + latest_task = max(valid_tasks, key=lambda x: x.get("task_id", -1)) + target_task_id = int(latest_task["task_id"]) + + def _parse_status(value: Any) -> Optional[int]: + if isinstance(value, int): + return value + if isinstance(value, str): + text = value.strip() + if text != "": + try: + return int(text) + except Exception: + return None + return None + + latest_status = _parse_status(latest_task.get("status")) + if latest_status is None: + raise ValidationError(f"任务{target_task_id}缺少有效状态码, 无法自动启动") + if latest_status != int(TaskStatus.UNSTARTED): + raise ValidationError(f"task_id={target_task_id}状态非未运行, 不允许自动启动") + + self._logger.info("准备启动任务 task_id=%s", target_task_id) + resp = self._call_with_relogin(self._client.start_task, int(target_task_id)) + self._logger.info("任务启动请求已提交 task_id=%s", target_task_id) + + # 自动更新任务状态 + if self._data_manager: + self._data_manager.update_task_status( + str(target_task_id), + "RUNNING", + started_at=datetime.now().isoformat() + ) + + return resp + + 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 export_task_report(self, task_id: int, file_type: str = "excel") -> Path: + """ + 功能: + 导出指定任务的报告并保存到该任务的数据目录中 + 参数: + task_id: int, 任务id + file_type: str, 文件类型, 默认 "excel" + 返回: + Path, 保存的报告文件路径 + """ + # 确定任务数据目录 + task_dir = self._settings.data_dir / "tasks" / str(task_id) + task_dir.mkdir(parents=True, exist_ok=True) + + # 调用底层API获取报告二进制内容 + content = self._call_with_relogin( + self._client.export_task_report, [task_id], file_type + ) + + # 根据文件类型确定后缀 + ext_map = {"excel": ".xlsx", "csv": ".csv", "pdf": ".pdf"} + ext = ext_map.get(file_type, ".xlsx") + report_path = task_dir / f"{task_id}_task_report{ext}" + + report_path.write_bytes(content) + self._logger.info("任务 %s 报告已保存: %s", task_id, report_path) + return report_path + + def wait_task_with_ops(self, task_id: Optional[int] = None, *, poll_interval_s: float = 5.0) -> int: + """ + 功能: + 轮询指定或自动选取的运行中任务直至完成, 并按新增步骤增量输出操作进度 + 参数: + task_id: 可选int, 指定任务id, None时自动选择状态为RUNNING的任务 + poll_interval_s: float, 轮询间隔秒 + 返回: + int, 任务最终状态码 + """ + target_task_id = task_id + if target_task_id is None: + retry_count = 0 + running_ids: List[int] = [] + + while retry_count < 3: + running_info = self.get_task_list(status=[int(TaskStatus.RUNNING)], sort="desc", offset=0, limit=20) + task_list = ( + running_info.get("task_list") + or running_info.get("result", {}).get("task_list") + or running_info.get("data", {}).get("task_list") + ) + + running_ids.clear() + if isinstance(task_list, list) is True: + for item in task_list: + cur_id = item.get("task_id") + cur_status = item.get("status") + if isinstance(cur_id, int) is True and cur_status == int(TaskStatus.RUNNING): + running_ids.append(cur_id) + + if len(running_ids) > 0: + target_task_id = max(running_ids) + break + + retry_count += 1 + if retry_count < 3: + time.sleep(10) + + if target_task_id is None: + msg = "未找到运行中的任务" + self._logger.error(msg) + raise ValidationError(msg) + + self._logger.info("开始监控任务 %s 运行进度", target_task_id) + + start_ts = time.time() + seen_steps: Dict[str, bool] = {} + first_output = True + + def _format_steps(op_info: JsonDict) -> List[str]: + result_steps: List[str] = [] + if isinstance(op_info, dict) is False: + return result_steps + for key in ("done_units", "running_units"): + units = op_info.get(key) + if isinstance(units, list) is False: + continue + for unit_obj in units: + if isinstance(unit_obj, dict) is False: + continue + for unit_name, steps in unit_obj.items(): + if isinstance(steps, list) is False: + continue + for step_item in steps: + if isinstance(step_item, list) is False: + continue + action = step_item[0] if len(step_item) >= 1 else "" + target = step_item[1] if len(step_item) >= 2 else "" + action_text = str(action).strip() + target_text = str(target).strip() + if target_text != "": + result_steps.append(f"{unit_name}: {action_text} -> {target_text}") + else: + result_steps.append(f"{unit_name}: {action_text}") + return result_steps + + while True: + info = self.get_task_info(int(target_task_id)) + status = self._extract_task_status(info) + if status == int(TaskStatus.COMPLETED): + self._logger.info("任务 %s 已完成", target_task_id) + + # 自动导出任务报告并保存到任务目录 + try: + report_path = self.export_task_report(int(target_task_id)) + self._logger.info("任务 %s 报告已自动保存: %s", target_task_id, report_path) + except Exception as exc: + self._logger.warning("任务 %s 报告导出失败: %s", target_task_id, exc) + + # 自动更新任务状态为完成 + if self._data_manager: + self._data_manager.update_task_status( + str(target_task_id), + "COMPLETED", + completed_at=datetime.now().isoformat() + ) + + return int(status) + if status in (int(TaskStatus.FAILED), int(TaskStatus.STOPPED)): + self._logger.warning("任务 %s 已结束但未完成, status=%s", target_task_id, status) + + # 自动更新任务状态为失败或停止 + if self._data_manager: + status_name = TaskStatus(status).name if status in TaskStatus._value2member_map_ else "UNKNOWN" + self._data_manager.update_task_status( + str(target_task_id), + status_name, + completed_at=datetime.now().isoformat() + ) + + return int(status) + + op_info = self._call_with_relogin(self._client.get_task_op_info, int(target_task_id)) + step_texts = _format_steps(op_info) + new_steps: List[str] = [] + for step in step_texts: + if step not in seen_steps: + new_steps.append(step) + seen_steps[step] = True + + if first_output is True: + if len(step_texts) > 0: + self._logger.info("任务 %s 已执行步骤:", target_task_id) + for text in step_texts: + self._logger.info("%s", text) + first_output = False + else: + if len(new_steps) > 0: + for text in new_steps: + self._logger.info("%s", text) + + time.sleep(poll_interval_s) + + # ---------- 任务json生成 ---------- + def _calc_liquid_vol_ml( + self, + amt_val: float, + amt_unit: str, + chem_info: dict, + reaction_scale_mmol: float, + ) -> float: + """ + 功能: + 将液体的用量换算为 mL 体积. + 换算规则与最终加料生成阶段完全一致, 同时用于排序阶段的 max_vol 计算. + 支持单位: mL, μL, eq, mmol, mg, g. + 换算失败或参数缺失时返回 0.0, 不影响实际计量. + + 参数: + amt_val 用量数值 + amt_unit 用量单位字符串 + chem_info 化学品信息字典(含 molecular_weight, density (g/mL), + physical_form, active_content, physical_state) + reaction_scale_mmol 反应规模(mmol), 用于 eq 换算 + + 返回: + float, 体积(mL), 无法换算时返回 0.0 + """ + unit = str(amt_unit).strip().lower() + + # 体积单位直接换算 + if unit == "ml": + return amt_val + if unit in ("ul", "μl", "µl"): + return amt_val / 1000.0 + + # 读取化学品物性 + mw = float(chem_info.get("molecular_weight", 0) or 0) + density = float(chem_info.get("density (g/mL)", 0) or 0) + physical_form = str(chem_info.get("physical_form", "") or "").lower() + active_content = chem_info.get("active_content") + state = str(chem_info.get("physical_state", "")).lower() + + # 换算为 mmol + target_mmol = None + if unit == "eq": + if reaction_scale_mmol <= 0: + return 0.0 + target_mmol = amt_val * reaction_scale_mmol + elif unit == "mmol": + target_mmol = amt_val + elif unit == "g": + if mw <= 0: + return 0.0 + target_mmol = (amt_val * 1000.0) / mw + elif unit == "mg": + if mw <= 0: + return 0.0 + target_mmol = amt_val / mw + else: + return 0.0 + + if target_mmol is None: + return 0.0 + + # mmol → mL, 与 _add_reagent_unit 换算逻辑一致 + try: + if physical_form == "solution": + # 复用已有方法(支持 mmol/mL 和 wt% 两种 active_content 格式) + return self._convert_active_content_to_volume( + target_mmol=target_mmol, + active_content=active_content, + molecular_weight=mw, + density=density, + physical_form=physical_form, + substance=str(chem_info.get("substance_chinese_name") or chem_info.get("substance") or ""), + ) + elif "liquid" in state: + # neat 液体: volume = mmol × MW / (1000 × density) + if mw <= 0 or density <= 0: + return 0.0 + mass_mg = target_mmol * mw + return (mass_mg / 1000.0) / density + else: + return 0.0 + except Exception: + return 0.0 + + def build_task_payload( + self, + params: Dict[str, Any], + headers: List[str], + data_rows: List[List[Any]], + chemical_db: Dict[str, Any] + ) -> JsonDict: + """ + 功能: + 将结构化的实验数据转换为 AddTask API 请求体. + 兼容同一试剂列中同时出现固体与液体的情况: 若检测到混用, 自动拆分为多个虚拟列(固体/液体/其他, 可选磁子列), + 以保证后续加料排序与布局行号映射正确. + 参数: + params: Dict[str, Any], 实验全局参数(反应时间、温度、反应规模、反应器类型、内标信息等). + headers: List[str], 实验数据表头列表. + data_rows: List[List[Any]], 实验数据行(每行为值列表). + chemical_db: Dict[str, Any], 化学品信息字典. + 返回: + JsonDict, AddTask 请求体. + """ + + def _safe_float(value: Any, default_val: float) -> float: + try: + return float(str(value).replace("%", "").replace("mmol", "").strip()) + except Exception: + return default_val + + def _chem_kind(chem_info: Dict[str, Any]) -> str: + # 统一将物态归一为 liquid/solid/other, 便于拆列与排序策略一致. + state_text = str(chem_info.get("physical_state", "")).lower() + if "liquid" in state_text: + return "liquid" + if "solid" in state_text: + return "solid" + return "other" + + weighing_error_pct = _safe_float(params.get("称量误差(%)", 1), 1.0) + max_error_mg = _safe_float(params.get("最大称量误差(mg)", 1), 1.0) + reaction_scale_mmol = _safe_float(params.get("反应规模(mmol)", 0), 0.0) + reactor_type = str(params.get("反应器类型", "")).strip() + + auto_magnet = str(params.get("自动加磁子", "是")).strip() == "是" + fixed_order = str(params.get("固定加料顺序", "否")).strip() == "是" + exp_count = len(data_rows) + + # 解析闪滤实验编号, None 表示全部实验均需闪滤 + filter_exp_numbers = _parse_filter_experiment_numbers( + params.get("闪滤实验编号", "") + ) + + if exp_count not in [12, 24, 36, 48]: + self._logger.warning(f"实验数量 {exp_count} 非标准(12/24/36/48).") + + col_metadata: List[Dict[str, Any]] = [] + col_idx = 0 + next_virtual_col_idx = -1000 # 负数虚拟列索引, 避免与真实列冲突. + + while col_idx < len(headers): + header_text = str(headers[col_idx]) + + if "试剂" in header_text: + name_col_idx = col_idx + amt_col_idx = col_idx + 1 if (col_idx + 1) < len(headers) else None + + has_liquid = False + has_solid = False + has_other = False + has_magnet_manual = False + max_liquid_vol_ml = 0.0 + + for row_vals in data_rows: + if name_col_idx >= len(row_vals): + continue + + chem_name = str(row_vals[name_col_idx]).strip() + if chem_name == "" or chem_name == "0": + continue + + if chem_name == "加磁子": + has_magnet_manual = True + continue + + if chem_name not in chemical_db: + # 此处仅用于列级扫描, 不直接抛错, 具体实验行处理时再校验并报错更准确. + has_other = True + continue + + chem_info = chemical_db[chem_name] + kind = _chem_kind(chem_info) + + if kind == "liquid": + has_liquid = True + amt_text = "0" + if amt_col_idx is not None and amt_col_idx < len(row_vals): + amt_text = str(row_vals[amt_col_idx]) + + amt_val, amt_unit = self._split_amount_unit(amt_text) + vol_ml = self._calc_liquid_vol_ml( + amt_val, amt_unit, chem_info, reaction_scale_mmol + ) # 非体积单位同样换算为 mL, 与最终加料逻辑一致 + if vol_ml > max_liquid_vol_ml: + max_liquid_vol_ml = vol_ml + + elif kind == "solid": + has_solid = True + else: + has_other = True + + if has_liquid is True and has_solid is True: + self._logger.debug(f"检测到试剂列混用固体与液体, 将拆分虚拟列, 原列索引={name_col_idx}.") + + # 固体虚拟列. + col_metadata.append({ + "col_idx": next_virtual_col_idx, + "src_col_idx": name_col_idx, + "src_amt_idx": amt_col_idx, + "type": "solid", + "split_kind": "solid", + "max_vol": 0.0, + "is_reagent_group": True, + "is_magnet_only": False, + }) + next_virtual_col_idx -= 1 + + # 磁子虚拟列(仅在该试剂列出现过“加磁子”时创建, 避免重复添加). + if has_magnet_manual is True: + col_metadata.append({ + "col_idx": next_virtual_col_idx, + "src_col_idx": name_col_idx, + "src_amt_idx": None, + "type": "magnet_manual", + "split_kind": None, + "max_vol": 0.0, + "is_reagent_group": False, + "is_magnet_only": True, + }) + next_virtual_col_idx -= 1 + + # 液体虚拟列. + col_metadata.append({ + "col_idx": next_virtual_col_idx, + "src_col_idx": name_col_idx, + "src_amt_idx": amt_col_idx, + "type": "liquid", + "split_kind": "liquid", + "max_vol": max_liquid_vol_ml, + "is_reagent_group": True, + "is_magnet_only": False, + }) + next_virtual_col_idx -= 1 + + # 其他虚拟列(兜底未知物态, 避免数据丢失). + if has_other is True: + col_metadata.append({ + "col_idx": next_virtual_col_idx, + "src_col_idx": name_col_idx, + "src_amt_idx": amt_col_idx, + "type": "other", + "split_kind": "other", + "max_vol": 0.0, + "is_reagent_group": True, + "is_magnet_only": False, + }) + next_virtual_col_idx -= 1 + + else: + # 未混用时保持原逻辑, 仍可基于列内最大体积做液体排序. + final_type = "other" + if has_magnet_manual is True: + final_type = "magnet_manual" + elif has_liquid is True: + final_type = "liquid" + elif has_solid is True: + final_type = "solid" + + col_metadata.append({ + "col_idx": name_col_idx, + "src_col_idx": name_col_idx, + "src_amt_idx": amt_col_idx, + "type": final_type, + "split_kind": None, + "max_vol": max_liquid_vol_ml if final_type == "liquid" else 0.0, + "is_reagent_group": True, + "is_magnet_only": False, + }) + + # 试剂列默认占用(名称+用量)两列. + col_idx += 2 + continue + + if "加磁子" in header_text: + col_metadata.append({ + "col_idx": col_idx, + "src_col_idx": col_idx, + "src_amt_idx": None, + "type": "magnet_manual", + "split_kind": None, + "max_vol": 0.0, + "is_reagent_group": False, + "is_magnet_only": True, + }) + col_idx += 1 + continue + + col_idx += 1 + + VIRTUAL_MAGNET_COL_IDX = -999 + ordered_cols: List[Dict[str, Any]] = [] + + if fixed_order is False: + 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.get("max_vol", 0.0), 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 is True: + 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 col_item in col_metadata: + if auto_magnet is True and inserted_magnet is False and col_item["type"] == "liquid": + ordered_cols.append({"col_idx": VIRTUAL_MAGNET_COL_IDX, "type": "magnet_auto"}) + inserted_magnet = True + ordered_cols.append(col_item) + + if auto_magnet is True and inserted_magnet is False: + ordered_cols.append({"col_idx": VIRTUAL_MAGNET_COL_IDX, "type": "magnet_auto"}) + + col_to_row_map: Dict[int, int] = {} + curr_row = 0 + for col_item in ordered_cols: + col_to_row_map[col_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 + 4 # 内标后搅拌 + ROW_IDX_DILUTION = curr_row + 3 # 稀释移液操作行 + ROW_IDX_FILTER = curr_row + 5 # 闪滤操作行 + + layout_list: List[Dict[str, Any]] = [] + 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 col_item in ordered_cols: + col_key = col_item["col_idx"] + target_row = col_to_row_map[col_key] + + if col_key == VIRTUAL_MAGNET_COL_IDX: + # 自动磁子: 仅当该实验行未显式写“加磁子”时才补一个. + has_explicit = False + for raw_val in row_vals: + if str(raw_val).strip() == "加磁子": + has_explicit = True + break + if has_explicit is False: + self._add_unit_magnet(layout_list, common_fields, unit_column, target_row) + continue + + src_col_idx = col_item.get("src_col_idx", None) + if src_col_idx is None: + continue + if src_col_idx >= len(row_vals): + continue + + val_name = str(row_vals[src_col_idx]).strip() + if val_name == "" or val_name == "0": + continue + + if val_name == "加磁子": + # 拆列后仅由磁子专用虚拟列处理, 避免同一行重复加磁子. + if col_item.get("is_magnet_only", False) is True: + self._add_unit_magnet(layout_list, common_fields, unit_column, target_row) + elif col_item.get("split_kind", None) is None: + self._add_unit_magnet(layout_list, common_fields, unit_column, target_row) + continue + + if col_item.get("is_magnet_only", False) is True: + continue + + if val_name not in chemical_db: + raise ValidationError(f"实验 {exp_idx + 1}: 未知化学品 '{val_name}'.") + + chem_info = chemical_db[val_name] + chem_kind = _chem_kind(chem_info) + + split_kind = col_item.get("split_kind", None) + if split_kind is not None and chem_kind != split_kind: + continue + + if col_item.get("is_reagent_group", False) is True: + src_amt_idx = col_item.get("src_amt_idx", None) + amt_text = "0" + if src_amt_idx is not None and src_amt_idx < len(row_vals): + amt_text = str(row_vals[src_amt_idx]) + + amt_val, amt_unit = self._split_amount_unit(amt_text) + 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, + reaction_scale_mmol, + ) + + if reactor_type != "": + self._add_reaction_unit(layout_list, common_fields, unit_column, ROW_IDX_REACTION, params) + else: + # 反应器未配置时跳过反应搅拌单元. + self._logger.debug("反应器类型为空, 跳过反应搅拌单元 exp=%s", exp_idx + 1) + + 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, + ) + else: + # 内标未配置时不做内标后搅拌. + self._logger.debug("内标种类为空, 跳过内标后搅拌 exp=%s", exp_idx + 1) + + # 稀释移液操作: 稀释液种类 + 稀释量(μL) + dil_name = str(params.get("稀释液种类", "")).strip() + if dil_name != "": + self._add_dilution_unit( + layout_list, + common_fields, + unit_column, + ROW_IDX_DILUTION, + dil_name, + chemical_db, + params, + ) + + # 闪滤操作: 闪滤液种类 + 闪滤液用量(μL) + 取样量(μL) + filter_name = str(params.get("闪滤液种类", "")).strip() + if filter_name != "": + current_exp_no = exp_idx + 1 # 实验编号从1开始 + # filter_exp_numbers 为 None 时表示全部实验都需要闪滤 + if filter_exp_numbers is None or current_exp_no in filter_exp_numbers: + self._add_filter_unit( + layout_list, + common_fields, + unit_column, + ROW_IDX_FILTER, + filter_name, + chemical_db, + params, + ) + + 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, reaction_scale_mmol: float) -> None: + """ + 功能: + 添加加粉或加液操作单元, 支持 eq 换算, 溶液/树脂的 active_content 换算. + 参数: + layout_list: List[Dict], 任务布局列表. + common_fields: Dict, 通用字段模板. + col: int, 单元所在列号. + row: int, 单元所在行号. + name: str, 试剂名称. + chem_info: Dict, 化学品信息. + amt_val: float, 配方数值. + amt_unit: str, 配方单位(eq/mmol/mg/mL等). + error_pct: float, 称量误差百分比. + max_error_mg: float, 最大偏差 mg. + reaction_scale_mmol: float, 反应规模 mmol, eq 换算所需. + 返回: + 无. + """ + 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() + physical_form = str(chem_info.get('physical_form', '') or '').lower() + active_content = chem_info.get('active_content') + mw = float(chem_info.get('molecular_weight', 0) or 0) + density = float(chem_info.get('density (g/mL)', 0) or 0) + + amt_unit_lower = str(amt_unit or "").lower() + target_mmol = None + + if amt_unit_lower == "eq": + if reaction_scale_mmol <= 0: + self._logger.error("反应规模(mmol)未填写, 无法换算eq: %s", name) + raise ValidationError("反应规模(mmol)未填写, 无法换算eq当量") + target_mmol = amt_val * reaction_scale_mmol + amt_val = target_mmol + amt_unit_lower = "mmol" + elif amt_unit_lower == "mmol": + target_mmol = amt_val + + # 溶液: active_content 视为 mmol/mL, 换算体积 + if target_mmol is not None and physical_form == "solution": + volume_ml = self._convert_active_content_to_volume( + target_mmol=target_mmol, + active_content=active_content, + molecular_weight=mw, + density=density, + physical_form=physical_form, + substance=name + ) + unit_dict.update({ + "unit_type": "exp_pipetting", + "process_json": { + "custom": {"unit": "mL", "unitOptions": ["mL", "L"]}, + "substance": name, + "chemical_id": chem_info['chemical_id'], + "add_volume": round(volume_ml, 3) + } + }) + layout_list.append(unit_dict) + return + + # 树脂(beads): active_content 视为 wt%,按摩尔需求折算出总质量,走加粉 + if target_mmol is not None and physical_form == "beads": + content_type, content_value = self._parse_active_content(active_content, physical_form) + if content_type != "wt_percent" or content_value <= 0: + self._logger.error("试剂 %s 的active_content(wt%%)无效", name) + raise ValidationError(f"{name} 的active_content(wt%)无效") + if mw <= 0: + self._logger.error("试剂 %s 缺少分子量, 无法按wt%%换算质量", name) + raise ValidationError(f"{name} 缺少分子量, 无法按wt%换算质量") + active_mass_mg = target_mmol * mw + total_mass_mg = active_mass_mg / (content_value / 100.0) + calc_offset = total_mass_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(total_mass_mg, 1) + } + }) + layout_list.append(unit_dict) + return + + # 原有固体逻辑 + 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 in ('μl', 'ul'): + target_vol_ml = amt_val / 1000.0 + + unit_dict.update({ + "unit_type": "exp_pipetting", + "process_json": { + "custom": {"unit": "mL","unitOptions": ["mL","L"]}, + "substance": name, + "chemical_id": chem_info['chemical_id'], + "add_volume": round(target_vol_ml, 3) + } + }) + layout_list.append(unit_dict) + + def _parse_active_content(self, value: Any, physical_form: str) -> Tuple[str, float]: + """ + 功能: + 解析 active_content 字段, 结合物理形态区分 mmol/mL 与 wt%. + 参数: + value: Any, active_content 原始值. + physical_form: str, 物理形态(solution/beads等). + 返回: + Tuple[str, float], (类型标记, 数值), 无法解析返回 ("", 0.0). + """ + form = (physical_form or "").lower().strip() + if value is None: + return "", 0.0 + text_raw = str(value).strip() + if text_raw == "": + return "", 0.0 + + try: + num_val = float(value) + except Exception: + text_norm = text_raw.lower() + numbers = re.findall(r"[0-9]+(?:\\.[0-9]+)?", text_norm) + num_val = float(numbers[0]) if len(numbers) > 0 else 0.0 + + if form == "solution": + return "mmol_per_ml", num_val + if form == "beads": + return "wt_percent", num_val + + text = text_raw.lower() + if "mmol/ml" in text or "mmol per ml" in text or "mmolml" in text: + return "mmol_per_ml", num_val + if "wt%" in text or "wt percent" in text or "wt" in text: + return "wt_percent", num_val + if num_val > 0: + return "mmol_per_ml", num_val + return "", 0.0 + + def _convert_active_content_to_volume(self, target_mmol: float, active_content: Any, molecular_weight: float, density: float, physical_form: str, substance: str) -> float: + """ + 功能: + 按 active_content 换算所需移取体积(mL). + 参数: + target_mmol: float, 目标有效摩尔数. + active_content: Any, active_content 原始值. + molecular_weight: float, 分子量, wt% 换算时使用. + density: float, 密度(g/mL), wt% 换算体积时使用. + physical_form: str, 物理形态(solution/beads等). + substance: str, 试剂名称, 用于提示. + 返回: + float, 需要的体积(mL). + """ + content_type, content_value = self._parse_active_content(active_content, physical_form) + if content_type == "": + self._logger.error("试剂 %s 缺少active_content, 无法按mmol换算体积", substance) + raise ValidationError(f"{substance} 缺少active_content, 无法按mmol换算体积") + + if content_type == "mmol_per_ml": + if content_value <= 0: + self._logger.error("试剂 %s 的active_content(mmol/mL)无效", substance) + raise ValidationError(f"{substance} 的active_content(mmol/mL)无效") + return target_mmol / content_value + + if content_type == "wt_percent": + if molecular_weight <= 0: + self._logger.error("试剂 %s 缺少分子量, 无法按wt%%换算", substance) + raise ValidationError(f"{substance} 缺少分子量, 无法按wt%换算") + if density <= 0: + self._logger.error("试剂 %s 缺少密度, 无法按wt%%换算体积", substance) + raise ValidationError(f"{substance} 缺少密度, 无法按wt%换算体积") + active_mass_mg = target_mmol * molecular_weight + total_mass_mg = active_mass_mg / (content_value / 100.0) + return total_mass_mg / 1000.0 / density + + self._logger.error("试剂 %s 的active_content单位未支持: %s", substance, active_content) + raise ValidationError(f"{substance} 的active_content单位未支持: {active_content}") + + 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) + + @staticmethod + def _parse_reaction_time_secs(text: str) -> int: + """ + 功能: + 将带单位的反应时间字符串解析为秒数, 支持 h/min 及大小写、空格变体. + 参数: + text: str, 如 "10h", "10 H", "30min", "30 MIN". + 返回: + int, 对应的秒数. + 异常: + ValueError: 无法识别单位或格式非法时抛出. + """ + m = re.match(r'^\s*([\d.]+)\s*(h|min)\s*$', str(text).strip(), re.IGNORECASE) + if m is None: + raise ValueError(f"无法识别反应时间格式: '{text}', 请使用带单位的格式, 如 '10h' 或 '30min'.") + value = float(m.group(1)) + unit = m.group(2).lower() + if unit == "h": + return int(value * 3600) + else: # min + return int(value * 60) + + 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_raw = str(params.get("反应时间(min/h)", "0h")).strip() # 读取带单位的反应时间字段 + rxn_time_secs = self._parse_reaction_time_secs(rxn_time_raw) + rxn_rpm = int(params.get("转速(rpm)", 0)) + is_wait = str(params.get("等待目标温度", "否")) == "是" + + process_data = { + "rotation_speed": rxn_rpm, + "reaction_duration": rxn_time_secs, + "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_dilution_unit(self, layout_list, common_fields, col, row, diluent_name, db, params): + """ + 功能: + 添加稀释移液操作单元. + 参数: + layout_list: List[Dict], 任务布局列表. + common_fields: Dict, 通用字段模板. + col: int, 单元所在列号. + row: int, 单元所在行号. + diluent_name: str, 稀释液名称. + db: Dict, 化学品信息字典. + params: Dict, 实验参数. + 返回: + 无. + """ + if diluent_name not in db: + return + chem_id = db[diluent_name]['chemical_id'] + dilution_vol_ul = float(params.get("稀释量(μL)", 0)) + if dilution_vol_ul <= 0: + return + + unit_dict = common_fields.copy() + unit_dict.update({ + "unit_type": "exp_pipetting", + "unit_column": col, "unit_row": row, "unit_id": f"unit-{uuid.uuid4().hex[:8]}", + "process_json": { + "custom": {"unit": "mL", "unitOptions": ["mL", "µL", "L"]}, + "substance": diluent_name, + "chemical_id": chem_id, + "add_volume": round(dilution_vol_ul / 1000, 3) + } + }) + layout_list.append(unit_dict) + + def _add_filter_unit(self, layout_list, common_fields, col, row, filter_liquid_name, db, params): + """ + 功能: + 添加闪滤操作单元. + 参数: + layout_list: List[Dict], 任务布局列表. + common_fields: Dict, 通用字段模板. + col: int, 单元所在列号. + row: int, 单元所在行号. + filter_liquid_name: str, 闪滤液名称. + db: Dict, 化学品信息字典. + params: Dict, 实验参数. + 返回: + 无. + """ + if filter_liquid_name not in db: + return + chem_id = db[filter_liquid_name]['chemical_id'] + filter_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": filter_liquid_name, "chemical_id": chem_id, + "add_volume": filter_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 start_notification_monitor(self) -> None: + """ + 功能: + 启动异常通知邮件监控, 后台守护线程周期轮询 Notice API. + 首次调用时惰性创建 NotificationMonitor 实例. + 参数: + 无. + 返回: + 无. + """ + from ..notification.monitor import NotificationMonitor + + if self._notification_monitor is None: + self._notification_monitor = NotificationMonitor( + controller=self, + settings=self._settings.notification, + ) + self._notification_monitor.start() + + def stop_notification_monitor(self) -> None: + """ + 功能: + 停止异常通知邮件监控. + 参数: + 无. + 返回: + 无. + """ + if self._notification_monitor is not None: + self._notification_monitor.stop() + + def notification_monitor_status(self) -> Dict: + """ + 功能: + 返回通知监控器的运行状态信息. + 参数: + 无. + 返回: + Dict, 包含 running, total_processed, last_poll_time 等字段. + """ + if self._notification_monitor is None: + return {"running": False, "total_processed": 0, "message": "监控器未初始化"} + return self._notification_monitor.status_info + + # ---------- 方法模块 ---------- 弃用 + 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)) + + #---------- 资源核实 ---------- + def analyze_resource_readiness( + self, + task_payload: JsonDict, + resource_rows: List[JsonDict], + chemical_db: Dict[str, Dict[str, Any]], + task_id: Optional[int] = None, + ) -> JsonDict: + """ + 功能: + 基于任务配置与站内资源统计药品与耗材需求, 对比库存给出缺口与冗余, 并跳过以 TB 开头的过渡舱资源. + 参数: + task_payload: Dict, build_task_payload 生成的任务数据. + resource_rows: List[Dict], get_resource_info 的返回值. + chemical_db: Dict[str, Dict[str, Any]], 化学品密度与物态数据, 用于单位换算. + task_id: Optional[int], 实验ID, 用于二次校验任务资源, 默认为 None. + 返回: + Dict[str, Any], 包含耗材需求、药品需求、库存差值、缺失与冗余列表. + """ + layout_list = task_payload.get("layout_list") or [] + experiment_num = int(task_payload.get("task_setup", {}).get("experiment_num", 0)) + + def _safe_float(val: Any) -> float: + try: + return float(val) + except Exception: + return 0.0 + + def _normalize_unit(value: float, unit: str, state: str) -> Tuple[str, float]: + unit_l = (unit or "").lower().strip() + if unit_l in ("mg", "g"): + if unit_l == "g": + return "mg", value * 1000 + return "mg", value + if unit_l in ("l", "ml", "ul", "μl", "µl"): + if unit_l == "l": + return "ml", value * 1000 + if unit_l in ("ul", "μl", "µl"): + return "ml", value / 1000 + return "ml", value + if "liquid" in state: + return "ml", value + if "solid" in state: + return "mg", value + return "", value + + def _convert_amount(from_kind: str, to_kind: str, value: float, density: Any) -> float: + try: + dens = float(density) + except Exception: + dens = 0.0 + if dens <= 0: + return 0.0 + if from_kind == "mg" and to_kind == "ml": + return value / 1000.0 / dens + if from_kind == "ml" and to_kind == "mg": + return value * dens * 1000.0 + return 0.0 + + def _pick_amount(detail: JsonDict, state: str) -> Tuple[str, float]: + for key in ( + "available_weight", + "cur_weight", + "initial_weight", + "available_volume", + "cur_volume", + "initial_volume", + "value", + ): + if key not in detail: + continue + raw = detail.get(key) + if raw is None or str(raw).strip() == "": + continue + num, unit = self._parse_amount_string(raw) + if unit == "": + if "weight" in key: + unit = "mg" + elif "volume" in key: + unit = "mL" + kind, val = _normalize_unit(num, unit, state) + if kind != "": + return kind, val + return "", 0.0 + + def _tip_usage(volume_ml: float) -> Dict[int, int]: + usable_50ul = 0.05 * 0.7 + usable_1ml = 1.0 * 0.7 + usable_5ml = 5.0 * 0.7 + if volume_ml <= usable_50ul: + return {int(ResourceCode.TIP_50UL): 1} + if volume_ml <= usable_1ml: + return {int(ResourceCode.TIP_1ML): 1} + # 1 mL 及以上统一用 5 mL 枪头, 每根最大 3.5 mL + count = math.ceil(volume_ml / usable_5ml) + return {int(ResourceCode.TIP_5ML): count} + + # 按试剂瓶规格定义液体死体积(mL) + container_dead_volume_map = { + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): 0.1, + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): 1.0, + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): 4.0, + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML): 14.0, + } + # 固定粉末死体积(mg) + powder_dead_weight = 20.0 + self._logger.debug("冗余配置: 粉末死体积 %s mg, 液体死体积按容器映射", powder_dead_weight) + + # 识别磁力搅拌相关操作, 无磁力搅拌则不需要反应盖板 + magnet_related_types = {"exp_magnetic_stirrer"} + has_magnetic_operation = any( + str(unit.get("unit_type") or "").strip() in magnet_related_types + for unit in layout_list + ) + if has_magnetic_operation is False: + reaction_seal_cap_need = 0 + self._logger.debug("任务未包含磁力搅拌相关操作, 反应盖板需求设为 0") + else: + reaction_seal_cap_need = math.ceil(experiment_num / 24) if experiment_num else 0 + + # 1) 需求统计 + reagent_need: Dict[str, Dict[str, float]] = {} + reagent_kind: Dict[str, str] = {} + consumable_need: Dict[int, int] = { + int(ResourceCode.TIP_50UL): 0, + int(ResourceCode.TIP_1ML): 0, + int(ResourceCode.TIP_5ML): 0, + int(ResourceCode.TEST_TUBE_MAGNET_2ML): 0, + int(ResourceCode.REACTION_TUBE_2ML): experiment_num, + int(ResourceCode.REACTION_SEAL_CAP): reaction_seal_cap_need, + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE): 0, + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE): 0, + } + magnet_from_unit = 0 + pipetting_tip_plan: Dict[Tuple[Optional[int], str], float] = {} + filtering_rows = set() + filtering_diluent_tip_plan: Dict[str, float] = {} + + for unit in layout_list: + utype = str(unit.get("unit_type") or "").strip() + process_json = unit.get("process_json") or {} + substance = str(process_json.get("substance") or "").strip() + row_index = self._safe_int(unit.get("unit_row")) + + if utype == "exp_add_powder" and substance: + add_weight = _safe_float(process_json.get("add_weight")) + reagent_need.setdefault(substance, {"mg": 0.0, "ml": 0.0}) + reagent_need[substance]["mg"] += add_weight + reagent_kind[substance] = "solid" + continue + + if utype == "exp_pipetting" and substance: + add_volume = _safe_float(process_json.get("add_volume")) + reagent_need.setdefault(substance, {"mg": 0.0, "ml": 0.0}) + reagent_need[substance]["ml"] += add_volume + reagent_kind[substance] = "liquid" + if add_volume > 0: + tip_key = (row_index, substance) + current_max = pipetting_tip_plan.get(tip_key, 0.0) + if add_volume > current_max: + pipetting_tip_plan[tip_key] = add_volume + continue + + if utype == "exp_add_magnet": + magnet_from_unit += 1 + continue + + if utype == "exp_filtering_sample" and substance: + add_volume = _safe_float(process_json.get("add_volume")) + reagent_need.setdefault(substance, {"mg": 0.0, "ml": 0.0}) + reagent_need[substance]["ml"] += add_volume + reagent_kind[substance] = "liquid" + consumable_need[int(ResourceCode.FLASH_FILTER_INNER_BOTTLE)] += 1 + consumable_need[int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE)] += 1 + filtering_rows.add(row_index) + if add_volume > 0: + current_max = filtering_diluent_tip_plan.get(substance, 0.0) + if add_volume > current_max: + filtering_diluent_tip_plan[substance] = add_volume + continue + + if magnet_from_unit > consumable_need[int(ResourceCode.TEST_TUBE_MAGNET_2ML)]: + consumable_need[int(ResourceCode.TEST_TUBE_MAGNET_2ML)] = magnet_from_unit + + for max_volume in pipetting_tip_plan.values(): + if max_volume <= 0: + continue + tip_dict = _tip_usage(max_volume) + for code in tip_dict.keys(): + consumable_need[code] = consumable_need.get(code, 0) + 1 + + if len(filtering_rows) > 0 and experiment_num > 0: + sample_tip_need = len(filtering_rows) * experiment_num + consumable_need[int(ResourceCode.TIP_50UL)] += sample_tip_need + + for _substance, max_volume in filtering_diluent_tip_plan.items(): + if max_volume <= 0: + continue + consumable_need[int(ResourceCode.TIP_5ML)] = consumable_need.get(int(ResourceCode.TIP_5ML), 0) + 1 + + # 2) 库存统计 + filtered_resource_rows: List[JsonDict] = [] + for row in resource_rows: + layout_code_text = str(row.get("layout_code") or "").strip() + if layout_code_text.upper().startswith("TB"): + self._logger.debug("跳过过渡舱资源 %s", layout_code_text) + continue + filtered_resource_rows.append(row) + + tray_to_consumable = { + tray_code: consumable_code + for consumable_code, tray_code in CONSUMABLE_CODE_TO_TRAY_CODE.items() + } + + consumable_stock: Dict[int, int] = {} + reagent_stock: Dict[str, Dict[str, float]] = {} + substance_dead_volume: Dict[str, float] = {} + + for row in filtered_resource_rows: + tray_code = self._safe_int(row.get("resource_type")) + if tray_code is not None and tray_code in tray_to_consumable: + consumable_code = tray_to_consumable[tray_code] + consumable_stock[consumable_code] = consumable_stock.get(consumable_code, 0) + int(row.get("count", 0) or 0) + + bottle_dead_volume = 0.0 + if tray_code is not None and tray_code in container_dead_volume_map: + bottle_dead_volume = container_dead_volume_map[tray_code] + + for detail in row.get("substance_details") or []: + name = str(detail.get("substance") or "").strip() + if name == "": + continue + state = str(chemical_db.get(name, {}).get("physical_state", "") or "").lower() + kind, val = _pick_amount(detail, state) + if kind == "" or val <= 0: + continue + reagent_stock.setdefault(name, {"mg": 0.0, "ml": 0.0}) + reagent_stock[name][kind] += val + if bottle_dead_volume > 0: + # 按试剂瓶类型记录液体死体积, 取较大值保证保守 + previous_dead_volume = substance_dead_volume.get(name) + if previous_dead_volume is None or bottle_dead_volume > previous_dead_volume: + substance_dead_volume[name] = bottle_dead_volume + + if len(substance_dead_volume) == 0: + self._logger.debug("未找到液体死体积映射, 按默认只计需求") + else: + self._logger.debug("液体死体积映射 %s", substance_dead_volume) + + # 3) 对比 + reagent_report: List[JsonDict] = [] + missing_items: List[str] = [] + redundant_items: List[str] = [] + need_reagents: List[JsonDict] = [] + + for name, need_map in reagent_need.items(): + base_need_mg = need_map.get("mg", 0.0) + base_need_ml = need_map.get("ml", 0.0) + + if base_need_mg > 0: + need_mg = base_need_mg + powder_dead_weight + else: + need_mg = base_need_mg + + if base_need_ml > 0: + bottle_dead_volume = substance_dead_volume.get(name, 0.0) + need_ml = base_need_ml + bottle_dead_volume + else: + bottle_dead_volume = 0.0 + need_ml = base_need_ml + + state = reagent_kind.get(name, str(chemical_db.get(name, {}).get("physical_state", "") or "")).lower() + stock = reagent_stock.get(name, {"mg": 0.0, "ml": 0.0}) + avail_mg = stock.get("mg", 0.0) + avail_ml = stock.get("ml", 0.0) + density_val = chemical_db.get(name, {}).get("density (g/mL)") + + diff_text = "" + status_text = "冗余满足" + + need_reagents.append( + { + "substance": name, + "need_mg": round(need_mg, 1), + "need_ml": round(need_ml, 3), + "base_need_mg": round(base_need_mg, 1), + "base_need_ml": round(base_need_ml, 3), + } + ) + + if need_ml > 0: + total_ml = avail_ml + if total_ml < need_ml and avail_mg > 0: + total_ml += _convert_amount("mg", "ml", avail_mg, density_val) + diff_val = total_ml - need_ml + diff_text = f"{diff_val:.3f}mL" + if diff_val < 0: + status_text = "缺少" + missing_items.append(f"{name}:{abs(diff_val):.3f}mL") + else: + redundant_items.append(f"{name}:{diff_val:.3f}mL") + + elif need_mg > 0: + total_mg = avail_mg + if total_mg < need_mg and avail_ml > 0: + total_mg += _convert_amount("ml", "mg", avail_ml, density_val) + diff_val = total_mg - need_mg + diff_text = f"{diff_val:.1f}mg" + if diff_val < 0: + status_text = "缺少" + missing_items.append(f"{name}:{abs(diff_val):.1f}mg") + else: + redundant_items.append(f"{name}:{diff_val:.1f}mg") + + reagent_report.append( + { + "substance": name, + "need_mg": round(need_mg, 1), + "need_ml": round(need_ml, 3), + "available_mg": round(avail_mg, 1), + "available_ml": round(avail_ml, 3), + "status": status_text, + "diff": diff_text, + "base_need_mg": round(base_need_mg, 1), + "base_need_ml": round(base_need_ml, 3), + } + ) + + consumable_report: List[JsonDict] = [] + need_consumables: List[JsonDict] = [] + + for code, need_cnt in consumable_need.items(): + consumable_name = CONSUMABLE_CODE_DISPLAY_NAME.get(code, str(code)) + avail_cnt = consumable_stock.get(code, 0) + diff_cnt = avail_cnt - need_cnt + if diff_cnt < 0: + missing_items.append(f"{consumable_name}:{abs(diff_cnt)}件") + status = "lack" + else: + status = "satisfy" + redundant_items.append(f"{consumable_name}:{diff_cnt}件") + + need_consumables.append( + { + "code": code, + "name": consumable_name, + "need": int(need_cnt), + } + ) + consumable_report.append( + { + "code": code, + "name": consumable_name, + "need": int(need_cnt), + "available": int(avail_cnt), + "diff": int(diff_cnt), + "status": status, + } + ) + + ready_flag = len([item for item in missing_items if item]) == 0 + result = { + "ready": ready_flag, + "reagents": reagent_report, + "consumables": consumable_report, + "need_reagents": need_reagents, + "need_consumables": need_consumables, + "missing": missing_items, + "redundant": redundant_items, + } + + if ready_flag: + self._logger.info("资源核查通过") + + # 如果提供了task_id, 进行二次校验 + if task_id is not None: + self._logger.info("开始二次校验, 任务ID: %d", task_id) + try: + check_result = self._client.check_task_resource(task_id) + code = check_result.get("code") + + if code == 200: + self._logger.info("二次校验通过") + elif code == 1200: + # 资源不足, 校验失败 + msg = check_result.get("msg", "") + prompt_msg = check_result.get("prompt_msg", {}) + resource_type = prompt_msg.get("resource_type", "未知资源") + number = prompt_msg.get("number", 0) + + # 缺少数量为0时视为校验通过 + if number == 0: + self._logger.info("二次校验返回1200但缺少数量为0, 视为通过, 资源类型: %s", resource_type) + else: + self._logger.error("二次校验失败: %s, 资源类型: %s, 缺少数量: %s", msg, resource_type, number) + + # 更新结果状态为未通过 + result["ready"] = False + result["secondary_check_failed"] = True + result["secondary_check_message"] = f"二次校验失败: {resource_type} 缺少 {number}" + + return result + else: + # 其他错误码 + self._logger.warning("二次校验返回异常代码: %d, 消息: %s", code, check_result.get("msg", "")) + except Exception as e: + self._logger.error("二次校验过程中发生异常: %s", str(e)) + # 二次校验异常不影响主流程, 仅记录日志 + else: + self._logger.warning("资源核查未通过, 缺失项 %s", missing_items) + + return result + diff --git a/unilabos/devices/eit_synthesis_station/data/data_manager.py b/unilabos/devices/eit_synthesis_station/data/data_manager.py new file mode 100644 index 00000000..a3235b08 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/data_manager.py @@ -0,0 +1,542 @@ +""" +数据管理模块 - 负责保存和加载工站运行数据 + +功能: + - 保存设备状态、物料信息、任务数据等快照 + - 管理任务生命周期数据 + - 记录操作日志 + - 提供数据查询接口 +""" + +import json +import logging +from datetime import datetime, timedelta +from pathlib import Path +from typing import Any, Dict, List, Optional +import uuid + + +class DataManager: + """数据管理器 - 负责所有数据的持久化和查询""" + + def __init__(self, data_dir: Optional[Path] = None): + """ + 初始化数据管理器 + + 参数: + data_dir: 数据存储根目录,默认为当前目录下的 data/ + """ + if data_dir is None: + data_dir = Path(__file__).parent.parent / "data" + + self.data_dir = Path(data_dir) + self.snapshots_dir = self.data_dir / "snapshots" + self.tasks_dir = self.data_dir / "tasks" + self.operations_dir = self.data_dir / "operations" + + self._logger = logging.getLogger(self.__class__.__name__) + + # 确保目录存在 + self._ensure_directories() + + def _ensure_directories(self) -> None: + """确保所有必要的目录存在""" + self.snapshots_dir.mkdir(parents=True, exist_ok=True) + self.tasks_dir.mkdir(parents=True, exist_ok=True) + self.operations_dir.mkdir(parents=True, exist_ok=True) + + def _save_json(self, file_path: Path, data: Dict[str, Any]) -> None: + """ + 保存 JSON 数据到文件 + + 参数: + file_path: 文件路径 + data: 要保存的数据 + """ + try: + file_path.parent.mkdir(parents=True, exist_ok=True) + with file_path.open("w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + self._logger.debug(f"数据已保存到 {file_path}") + except Exception as e: + self._logger.error(f"保存数据到 {file_path} 失败: {e}") + + def _load_json(self, file_path: Path) -> Optional[Dict[str, Any]]: + """ + 从文件加载 JSON 数据 + + 参数: + file_path: 文件路径 + + 返回: + 加载的数据,如果文件不存在则返回 None + """ + try: + if not file_path.exists(): + return None + with file_path.open("r", encoding="utf-8") as f: + return json.load(f) + except Exception as e: + self._logger.error(f"加载数据从 {file_path} 失败: {e}") + return None + + # ==================== 快照管理 ==================== + + def save_device_status(self, data: Dict[str, Any]) -> None: + """ + 保存设备状态快照 + + 参数: + data: 设备状态数据,格式: + { + "timestamp": "2026-01-20T14:30:00Z", + "devices": [{"device_name": "ARM", "status": "AVAILABLE", ...}] + } + """ + if "timestamp" not in data: + data["timestamp"] = datetime.now().isoformat() + + file_path = self.snapshots_dir / "device_status.json" + self._save_json(file_path, data) + + def save_station_state(self, data: Dict[str, Any]) -> None: + """ + 保存工站状态快照 + + 参数: + data: 工站状态数据,格式: + { + "timestamp": "2026-01-20T14:30:00Z", + "state": "IDLE", + "state_code": 0 + } + """ + if "timestamp" not in data: + data["timestamp"] = datetime.now().isoformat() + + file_path = self.snapshots_dir / "station_state.json" + self._save_json(file_path, data) + + def save_glovebox_env(self, data: Dict[str, Any]) -> None: + """ + 保存手套箱环境快照 + + 参数: + data: 手套箱环境数据,格式: + { + "timestamp": "2026-01-20T14:30:00Z", + "oxygen_ppm": 10.5, + "humidity_ppm": 5.2, + "pressure_pa": 50.0 + } + """ + if "timestamp" not in data: + data["timestamp"] = datetime.now().isoformat() + + file_path = self.snapshots_dir / "glovebox_env.json" + self._save_json(file_path, data) + + def save_resource_info(self, data: Dict[str, Any]) -> None: + """ + 保存物料资源快照 + + 参数: + data: 物料资源数据,格式: + { + "timestamp": "2026-01-20T14:30:00Z", + "resources": [{"layout_code": "W-1-1", ...}] + } + """ + if "timestamp" not in data: + data["timestamp"] = datetime.now().isoformat() + + file_path = self.snapshots_dir / "resource_info.json" + self._save_json(file_path, data) + + def load_snapshot(self, snapshot_type: str) -> Optional[Dict[str, Any]]: + """ + 加载最新快照 + + 参数: + snapshot_type: 快照类型,可选值: + - "device_status": 设备状态 + - "station_state": 工站状态 + - "glovebox_env": 手套箱环境 + - "resource_info": 物料资源 + + 返回: + 快照数据,如果不存在则返回 None + """ + file_path = self.snapshots_dir / f"{snapshot_type}.json" + return self._load_json(file_path) + + # ==================== 任务数据管理 ==================== + + def create_task_record(self, task_id: str, task_info: Dict[str, Any]) -> None: + """ + 创建任务记录 + + 参数: + task_id: 任务ID + task_info: 任务信息,格式: + { + "task_id": "abc123", + "experiment_name": "Suzuki偶联反应", + "created_at": "2026-01-20T10:00:00Z", + "status": "UNSTARTED", + ... + } + """ + if "created_at" not in task_info: + task_info["created_at"] = datetime.now().isoformat() + + if "task_id" not in task_info: + task_info["task_id"] = task_id + + task_dir = self.tasks_dir / task_id + task_dir.mkdir(parents=True, exist_ok=True) + + file_path = task_dir / "task_info.json" + self._save_json(file_path, task_info) + + def update_task_status(self, task_id: str, status: str, **kwargs) -> None: + """ + 更新任务状态 + + 参数: + task_id: 任务ID + status: 新状态 + **kwargs: 其他要更新的字段,如 started_at, completed_at 等 + """ + task_dir = self.tasks_dir / task_id + file_path = task_dir / "task_info.json" + + # 加载现有数据 + task_info = self._load_json(file_path) + if task_info is None: + self._logger.warning(f"任务 {task_id} 不存在,创建新记录") + task_info = {"task_id": task_id} + + # 更新状态 + task_info["status"] = status + + # 更新时间戳 + if status == "RUNNING" and "started_at" not in task_info: + task_info["started_at"] = datetime.now().isoformat() + elif status in ["COMPLETED", "FAILED", "STOPPED"] and "completed_at" not in task_info: + task_info["completed_at"] = datetime.now().isoformat() + + # 更新其他字段 + task_info.update(kwargs) + + # 保存 + self._save_json(file_path, task_info) + + def save_task_payload(self, task_id: str, payload: Dict[str, Any]) -> None: + """ + 保存任务 Payload + + 参数: + task_id: 任务ID + payload: 任务Payload数据 + """ + task_dir = self.tasks_dir / task_id + task_dir.mkdir(parents=True, exist_ok=True) + + file_path = task_dir / "task_payload.json" + self._save_json(file_path, payload) + + def save_resource_check(self, task_id: str, check_result: Dict[str, Any]) -> None: + """ + 保存物料核算结果 + + 参数: + task_id: 任务ID + check_result: 物料核算结果,格式: + { + "timestamp": "2026-01-20T09:55:00Z", + "task_id": "abc123", + "check_result": "PASS", + "required_materials": [...], + "missing_materials": [], + ... + } + """ + if "timestamp" not in check_result: + check_result["timestamp"] = datetime.now().isoformat() + + if "task_id" not in check_result: + check_result["task_id"] = task_id + + task_dir = self.tasks_dir / task_id + task_dir.mkdir(parents=True, exist_ok=True) + + file_path = task_dir / "resource_check.json" + self._save_json(file_path, check_result) + + def save_unload_info(self, task_id: str, unload_data: Dict[str, Any]) -> None: + """ + 保存下料信息 + + 参数: + task_id: 任务ID + unload_data: 下料数据,格式: + { + "task_id": "abc123", + "unload_time": "2026-01-20T12:35:00Z", + "unloaded_trays": [...], + "empty_trays_unloaded": [...], + ... + } + """ + if "unload_time" not in unload_data: + unload_data["unload_time"] = datetime.now().isoformat() + + if "task_id" not in unload_data: + unload_data["task_id"] = task_id + + task_dir = self.tasks_dir / task_id + task_dir.mkdir(parents=True, exist_ok=True) + + file_path = task_dir / "unload_info.json" + self._save_json(file_path, unload_data) + + def save_batch_in_tray_log(self, log_data: Dict[str, Any]) -> None: + """ + 保存上料日志 + + 参数: + log_data: 上料日志数据,格式: + { + "start_time": "2026-01-20T12:00:00", + "end_time": "2026-01-20T12:05:00", + "resources": [ + { + "layout_code": "W-1-1", + "count": 2, + "resource_type": 220000023, + "resource_type_name": "125 mL试剂瓶托盘", + "substance_details": [...], + "task_id": None + } + ] + } + """ + file_path = self.operations_dir / "batch_in_tray.json" + self._save_json(file_path, log_data) + + def save_batch_out_tray_log(self, log_data: Dict[str, Any]) -> None: + """ + 保存下料日志 + + 参数: + log_data: 下料日志数据,格式: + { + "start_time": "2026-01-20T12:00:00", + "end_time": "2026-01-20T12:05:00", + "resources": [ + { + "layout_code": "W-1-1", + "count": 2, + "resource_type": 220000023, + "resource_type_name": "125 mL试剂瓶托盘", + "substance_details": [...], + "task_id": 123, + "dst_layout_code": "TB-1-1" + } + ] + } + """ + file_path = self.operations_dir / "batch_out_tray.json" + self._save_json(file_path, log_data) + + def load_task_info(self, task_id: str) -> Optional[Dict[str, Any]]: + """ + 加载任务信息 + + 参数: + task_id: 任务ID + + 返回: + 任务信息,如果不存在则返回 None + """ + file_path = self.tasks_dir / task_id / "task_info.json" + return self._load_json(file_path) + + def load_task_payload(self, task_id: str) -> Optional[Dict[str, Any]]: + """ + 加载任务 Payload + + 参数: + task_id: 任务ID + + 返回: + 任务Payload,如果不存在则返回 None + """ + file_path = self.tasks_dir / task_id / "task_payload.json" + return self._load_json(file_path) + + def load_resource_check(self, task_id: str) -> Optional[Dict[str, Any]]: + """ + 加载物料核算结果 + + 参数: + task_id: 任务ID + + 返回: + 物料核算结果,如果不存在则返回 None + """ + file_path = self.tasks_dir / task_id / "resource_check.json" + return self._load_json(file_path) + + def load_unload_info(self, task_id: str) -> Optional[Dict[str, Any]]: + """ + 加载下料信息 + + 参数: + task_id: 任务ID + + 返回: + 下料信息,如果不存在则返回 None + """ + file_path = self.tasks_dir / task_id / "unload_info.json" + return self._load_json(file_path) + + def query_recent_tasks(self, limit: int = 10) -> List[Dict[str, Any]]: + """ + 查询最近的任务 + + 参数: + limit: 返回的任务数量限制 + + 返回: + 任务信息列表,按创建时间倒序排列 + """ + tasks = [] + + if not self.tasks_dir.exists(): + return tasks + + # 遍历所有任务目录 + for task_dir in self.tasks_dir.iterdir(): + if not task_dir.is_dir(): + continue + + task_info_path = task_dir / "task_info.json" + if not task_info_path.exists(): + continue + + task_info = self._load_json(task_info_path) + if task_info: + tasks.append(task_info) + + # 按创建时间排序 + tasks.sort(key=lambda x: x.get("created_at", ""), reverse=True) + + return tasks[:limit] + + # ==================== 操作日志管理 ==================== + + def append_operation_log(self, operation_type: str, data: Dict[str, Any]) -> None: + """ + 追加操作记录 + + 参数: + operation_type: 操作类型,可选值: + - "batch_in": 上料操作 + - "batch_out": 下料操作 + data: 操作数据,格式: + { + "operation_id": "op_001", + "timestamp": "2026-01-20T09:00:00Z", + "operation_type": "batch_in", + "template_file": "batch_in_tray.xlsx", + "trays": [...], + "status": "success" + } + """ + if "timestamp" not in data: + data["timestamp"] = datetime.now().isoformat() + + if "operation_id" not in data: + data["operation_id"] = f"op_{uuid.uuid4().hex[:8]}" + + if "operation_type" not in data: + data["operation_type"] = operation_type + + file_path = self.operations_dir / f"{operation_type}_log.json" + + # 加载现有日志 + log_data = self._load_json(file_path) + if log_data is None: + log_data = {"operations": []} + + # 追加新记录 + log_data["operations"].append(data) + + # 保存 + self._save_json(file_path, log_data) + + def load_operation_log(self, operation_type: str) -> Optional[Dict[str, Any]]: + """ + 加载操作日志 + + 参数: + operation_type: 操作类型 ("batch_in" 或 "batch_out") + + 返回: + 操作日志数据,格式: + { + "operations": [...] + } + """ + file_path = self.operations_dir / f"{operation_type}_log.json" + return self._load_json(file_path) + + # ==================== 数据清理 ==================== + + def cleanup_old_tasks(self, retention_days: int = 90) -> int: + """ + 清理过期的任务数据 + + 参数: + retention_days: 保留天数,默认90天 + + 返回: + 清理的任务数量 + """ + if not self.tasks_dir.exists(): + return 0 + + cutoff_date = datetime.now() - timedelta(days=retention_days) + cleaned_count = 0 + + for task_dir in self.tasks_dir.iterdir(): + if not task_dir.is_dir(): + continue + + task_info_path = task_dir / "task_info.json" + if not task_info_path.exists(): + continue + + task_info = self._load_json(task_info_path) + if not task_info: + continue + + # 检查完成时间 + completed_at = task_info.get("completed_at") + if not completed_at: + continue + + try: + completed_date = datetime.fromisoformat(completed_at.replace("Z", "+00:00")) + if completed_date < cutoff_date: + # 删除任务目录 + import shutil + shutil.rmtree(task_dir) + cleaned_count += 1 + self._logger.info(f"已清理过期任务: {task_dir.name}") + except Exception as e: + self._logger.error(f"清理任务 {task_dir.name} 失败: {e}") + + return cleaned_count diff --git a/unilabos/devices/eit_synthesis_station/data/operations/batch_in_tray.json b/unilabos/devices/eit_synthesis_station/data/operations/batch_in_tray.json new file mode 100644 index 00000000..1cf9a16e --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/operations/batch_in_tray.json @@ -0,0 +1,14 @@ +{ + "start_time": "2026-01-23T14:51:55.771430", + "end_time": "2026-01-23T15:00:36.838516", + "resources": [ + { + "layout_code": "", + "count": 0, + "resource_type": null, + "resource_type_name": "", + "substance_details": [], + "task_id": null + } + ] +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/operations/batch_out_tray.json b/unilabos/devices/eit_synthesis_station/data/operations/batch_out_tray.json new file mode 100644 index 00000000..2ce601c7 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/operations/batch_out_tray.json @@ -0,0 +1,28 @@ +{ + "start_time": "2026-01-21T18:05:58.009159", + "end_time": "2026-01-21T18:07:29.271930", + "resources": [ + { + "layout_code": "W-2-5", + "count": 22, + "resource_type": 201000726, + "resource_type_name": "2 mL反应试管托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "cur_volume": "0.1mL", + "cur_weight": "0" + }, + { + "slot": 1, + "well": "A2", + "cur_volume": "0.1mL", + "cur_weight": "0" + } + ], + "task_id": 676, + "dst_layout_code": "TB-2-1" + } + ] +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/snapshots/device_status.json b/unilabos/devices/eit_synthesis_station/data/snapshots/device_status.json new file mode 100644 index 00000000..214339fb --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/snapshots/device_status.json @@ -0,0 +1,85 @@ +{ + "timestamp": "2026-01-20T16:00:16.104567", + "devices": [ + { + "device_name": "机械臂", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "加粉模块", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "热磁力搅拌模块", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "开盖模块", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "W1排货架", + "status": "OUTSIDE", + "status_code": 5 + }, + { + "device_name": "W-1-1、W-1-2", + "status": "HOME", + "status_code": 6 + }, + { + "device_name": "W-1-3、W-1-4", + "status": "OUTSIDE", + "status_code": 5 + }, + { + "device_name": "W-1-5、W-1-6", + "status": "OUTSIDE", + "status_code": 5 + }, + { + "device_name": "W-1-7、W-1-8", + "status": "OUTSIDE", + "status_code": 5 + }, + { + "device_name": "中转货架", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "过渡舱外门", + "status": "OPEN", + "status_code": 3 + }, + { + "device_name": "过渡舱内门", + "status": "CLOSE", + "status_code": 4 + }, + { + "device_name": "加磁子模块", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "闪滤模块", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "交换仓货架", + "status": "AVAILABLE", + "status_code": 0 + }, + { + "device_name": "手套箱箱体环境", + "status": "UNAVAILABLE", + "status_code": 2 + } + ] +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/snapshots/glovebox_env.json b/unilabos/devices/eit_synthesis_station/data/snapshots/glovebox_env.json new file mode 100644 index 00000000..7da682b7 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/snapshots/glovebox_env.json @@ -0,0 +1,6 @@ +{ + "timestamp": "2026-03-23T20:42:57.525415", + "pressure_pa": 0.4, + "humidity_ppm": 0.0, + "oxygen_ppm": 0.0 +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/snapshots/resource_info.json b/unilabos/devices/eit_synthesis_station/data/snapshots/resource_info.json new file mode 100644 index 00000000..1ebd5f1d --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/snapshots/resource_info.json @@ -0,0 +1,380 @@ +{ + "timestamp": "2026-03-23T20:43:57.875006", + "resources": [ + { + "layout_code": "N-4", + "count": 36, + "resource_type": 201000727, + "resource_type_name": "闪滤瓶内瓶托盘", + "substance_details": [] + }, + { + "layout_code": "T-1-2", + "count": 6, + "resource_type": 201000512, + "resource_type_name": "5 mL Tip 头托盘", + "substance_details": [] + }, + { + "layout_code": "T-1-3", + "count": 34, + "resource_type": 201000731, + "resource_type_name": "1 mL Tip 头托盘", + "substance_details": [] + }, + { + "layout_code": "T-2-1", + "count": 6, + "resource_type": 201000815, + "resource_type_name": "50 μL Tip 头托盘", + "substance_details": [] + }, + { + "layout_code": "T-2-2", + "count": 16, + "resource_type": 201000815, + "resource_type_name": "50 μL Tip 头托盘", + "substance_details": [] + }, + { + "layout_code": "W-1-1", + "count": 2, + "resource_type": 220000023, + "resource_type_name": "125 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "1,3,5-三异丙基苯(溶液,1mol/L in MeCN)", + "value": "38mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "乙腈", + "value": "63mL" + } + ] + }, + { + "layout_code": "W-1-2", + "count": 2, + "resource_type": 220000023, + "resource_type_name": "125 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "N,N-二甲基乙酰胺", + "value": "120mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "N,N-二甲基甲酰胺", + "value": "110mL" + } + ] + }, + { + "layout_code": "W-1-3", + "count": 2, + "resource_type": 220000023, + "resource_type_name": "125 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "1,4-二氧六环", + "value": "107.5mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "四氢呋喃", + "value": "114mL" + } + ] + }, + { + "layout_code": "W-1-4", + "count": 2, + "resource_type": 220000023, + "resource_type_name": "125 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "二氯甲烷", + "value": "86.7mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "二甲基亚砜", + "value": "111.5mL" + } + ] + }, + { + "layout_code": "W-1-7", + "count": 3, + "resource_type": 201000502, + "resource_type_name": "8 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "频哪醇硼烷", + "value": "6.26mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "喹啉", + "value": "7.712mL" + }, + { + "slot": 2, + "well": "A3", + "substance": "碘 (溶液,0.2 M in DCM)", + "value": "6.8mL" + } + ] + }, + { + "layout_code": "W-1-8", + "count": 1, + "resource_type": 201000503, + "resource_type_name": "40 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "乙酸乙酯", + "value": "28mL" + } + ] + }, + { + "layout_code": "W-2-2", + "count": 7, + "resource_type": 201000502, + "resource_type_name": "8 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "二氟溴甲基三甲基硅烷", + "value": "3.756mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "4-甲氧基苄醇 (溶液,0.4 M in CH2Cl2)", + "value": "5.5mL" + }, + { + "slot": 2, + "well": "A3", + "substance": "4-溴苄醇 (溶液,0.4 M in CH2Cl2)", + "value": "5.5mL" + }, + { + "slot": 3, + "well": "A4", + "substance": "4-氯苯甲醇 (溶液,0.4 M in CH2Cl2)", + "value": "5.5mL" + }, + { + "slot": 4, + "well": "B1", + "substance": "4-(羟基甲基)苯腈 (溶液,0.4 M in CH2Cl2)", + "value": "5.5mL" + }, + { + "slot": 5, + "well": "B2", + "substance": "4-(羟甲基)苯甲酸甲酯 (溶液,0.4 M in CH2Cl2)", + "value": "5.5mL" + }, + { + "slot": 6, + "well": "B3", + "substance": "4-甲基苄醇 (溶液,0.4 M in CH2Cl2)", + "value": "5.5mL" + } + ] + }, + { + "layout_code": "W-2-3", + "count": 2, + "resource_type": 201000502, + "resource_type_name": "8 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "2-溴吡啶", + "value": "7.628mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "1,3,5-三异丙基苯", + "value": "5.84mL" + } + ] + }, + { + "layout_code": "W-2-4", + "count": 5, + "resource_type": 201000502, + "resource_type_name": "8 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "氢氧化钾(溶液,1 M in H2O)", + "value": "6mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "苯乙酮", + "value": "7.724mL" + }, + { + "slot": 2, + "well": "A3", + "substance": "苯甲醛", + "value": "7.76mL" + }, + { + "slot": 3, + "well": "A4", + "substance": "氢氧化钠 (溶液,1 M in H2O)", + "value": "6mL" + }, + { + "slot": 4, + "well": "B1", + "substance": "氢氧化锂(溶液,1 M in H2O)", + "value": "6mL" + } + ] + }, + { + "layout_code": "W-2-7", + "count": 5, + "resource_type": 201000730, + "resource_type_name": "2 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "间甲氧基苯甲醇", + "value": "1.975mL" + }, + { + "slot": 1, + "well": "A2", + "substance": "3-溴苯甲醇", + "value": "1.976mL" + }, + { + "slot": 2, + "well": "A3", + "substance": "3-氯苯甲醇", + "value": "1.976mL" + }, + { + "slot": 3, + "well": "A4", + "substance": "3-苯氧基苄醇", + "value": "1.965mL" + }, + { + "slot": 4, + "well": "A5", + "substance": "3-甲基苄醇", + "value": "1.976mL" + } + ] + }, + { + "layout_code": "W-3-2", + "count": 1, + "resource_type": 201000503, + "resource_type_name": "40 mL试剂瓶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "甲醇", + "value": "16mL" + } + ] + }, + { + "layout_code": "W-4-1", + "count": 2, + "resource_type": 201000600, + "resource_type_name": "30 mL粉桶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "无水碳酸钾", + "value": "2834.1mg" + }, + { + "slot": 1, + "well": "B1", + "substance": "碳酸铯", + "value": "2609.1mg" + } + ] + }, + { + "layout_code": "W-4-2", + "count": 2, + "resource_type": 201000600, + "resource_type_name": "30 mL粉桶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "苯硼酸", + "value": "1648.4mg" + }, + { + "slot": 1, + "well": "B1", + "substance": "4-溴-2-羟基苯乙酮", + "value": "1484mg" + } + ] + }, + { + "layout_code": "W-4-3", + "count": 2, + "resource_type": 201000600, + "resource_type_name": "30 mL粉桶托盘", + "substance_details": [ + { + "slot": 0, + "well": "A1", + "substance": "碳酸钠", + "value": "1872.8mg" + }, + { + "slot": 1, + "well": "B1", + "substance": "四(三苯基膦)钯(0)", + "value": "1842.8mg" + } + ] + } + ] +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/snapshots/station_state.json b/unilabos/devices/eit_synthesis_station/data/snapshots/station_state.json new file mode 100644 index 00000000..3089955f --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/snapshots/station_state.json @@ -0,0 +1,5 @@ +{ + "timestamp": "2026-03-23T20:42:57.543858", + "state": "IDLE", + "state_code": 0 +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/666/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/666/task_info.json new file mode 100644 index 00000000..9f06720c --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/666/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "666", + "status": "UNSTARTED", + "created_at": "2026-01-20T16:43:49.945361" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/666/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/666/task_payload.json new file mode 100644 index 00000000..a460e4ab --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/666/task_payload.json @@ -0,0 +1,122 @@ +{ + "task_id": 0, + "task_name": "test_20260120", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-f35f2d5f", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-82ddff0f", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "1,4-二氧六环", + "chemical_id": 2967.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 0, + "unit_row": 3, + "unit_id": "unit-82ccbd67", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 40.0, + "is_heating": true, + "target_temperature": 30.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-60b3870e", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_filtering_sample", + "unit_column": 0, + "unit_row": 6, + "unit_id": "unit-50cf71f0", + "process_json": { + "single_press_num": 6, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.5, + "sampling_volume": 0.002 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 1, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/667/resource_check.json b/unilabos/devices/eit_synthesis_station/data/tasks/667/resource_check.json new file mode 100644 index 00000000..efc539a8 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/667/resource_check.json @@ -0,0 +1,167 @@ +{ + "ready": false, + "reagents": [ + { + "substance": "1,4-二氧六环", + "need_mg": 0.0, + "need_ml": 0.2, + "available_mg": 0.0, + "available_ml": 0.0, + "status": "缺少", + "diff": "-0.200mL", + "base_need_mg": 0.0, + "base_need_ml": 0.2 + }, + { + "substance": "水", + "need_mg": 0.0, + "need_ml": 15.2, + "available_mg": 0.0, + "available_ml": 25.97, + "status": "冗余满足", + "diff": "10.770mL", + "base_need_mg": 0.0, + "base_need_ml": 1.2 + } + ], + "consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 2, + "available": 0, + "diff": -2, + "status": "lack" + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 2, + "available": 0, + "diff": -2, + "status": "lack" + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 1, + "available": 24, + "diff": 23, + "status": "satisfy" + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 2, + "available": 36, + "diff": 34, + "status": "satisfy" + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 2, + "available": 0, + "diff": -2, + "status": "lack" + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 1, + "available": 0, + "diff": -1, + "status": "lack" + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 2, + "available": 12, + "diff": 10, + "status": "satisfy" + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 2, + "available": 0, + "diff": -2, + "status": "lack" + } + ], + "need_reagents": [ + { + "substance": "1,4-二氧六环", + "need_mg": 0.0, + "need_ml": 0.2, + "base_need_mg": 0.0, + "base_need_ml": 0.2 + }, + { + "substance": "水", + "need_mg": 0.0, + "need_ml": 15.2, + "base_need_mg": 0.0, + "base_need_ml": 1.2 + } + ], + "need_consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 2 + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 2 + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 1 + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 2 + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 2 + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 1 + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 2 + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 2 + } + ], + "missing": [ + "1,4-二氧六环:0.200mL", + "50uL枪头:2件", + "1mL枪头:2件", + "2mL反应管:2件", + "反应盖板:1件", + "闪滤外瓶:2件" + ], + "redundant": [ + "水:10.770mL", + "5mL枪头:23件", + "2mL反应管磁子:34件", + "闪滤内瓶:10件" + ], + "timestamp": "2026-01-20T16:46:13.280135", + "task_id": "667" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/667/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/667/task_info.json new file mode 100644 index 00000000..3b3ff219 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/667/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "667", + "status": "UNSTARTED", + "created_at": "2026-01-20T16:45:14.399720" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/667/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/667/task_payload.json new file mode 100644 index 00000000..be5d99c2 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/667/task_payload.json @@ -0,0 +1,230 @@ +{ + "task_id": 0, + "task_name": "test_20260120", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-e02e2262", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-8be4e7d2", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "1,4-二氧六环", + "chemical_id": 2967.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 0, + "unit_row": 3, + "unit_id": "unit-b6af9613", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 40.0, + "is_heating": true, + "target_temperature": 30.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-c4c56ea8", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_filtering_sample", + "unit_column": 0, + "unit_row": 6, + "unit_id": "unit-804af0b0", + "process_json": { + "single_press_num": 6, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.5, + "sampling_volume": 0.002 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-b1a14dff", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-f645f9b9", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "1,4-二氧六环", + "chemical_id": 2967.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 1, + "unit_row": 3, + "unit_id": "unit-61e8b92d", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 40.0, + "is_heating": true, + "target_temperature": 30.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 4, + "unit_id": "unit-3c6133db", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_filtering_sample", + "unit_column": 1, + "unit_row": 6, + "unit_id": "unit-418b9b66", + "process_json": { + "single_press_num": 6, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.5, + "sampling_volume": 0.002 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 2, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/668/resource_check.json b/unilabos/devices/eit_synthesis_station/data/tasks/668/resource_check.json new file mode 100644 index 00000000..913c2a97 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/668/resource_check.json @@ -0,0 +1,167 @@ +{ + "ready": false, + "reagents": [ + { + "substance": "1,4-二氧六环", + "need_mg": 0.0, + "need_ml": 0.2, + "available_mg": 0.0, + "available_ml": 0.0, + "status": "缺少", + "diff": "-0.200mL", + "base_need_mg": 0.0, + "base_need_ml": 0.2 + }, + { + "substance": "水", + "need_mg": 0.0, + "need_ml": 14.2, + "available_mg": 0.0, + "available_ml": 25.97, + "status": "冗余满足", + "diff": "11.770mL", + "base_need_mg": 0.0, + "base_need_ml": 0.2 + } + ], + "consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 2, + "available": 0, + "diff": -2, + "status": "lack" + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 0, + "available": 24, + "diff": 24, + "status": "satisfy" + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 2, + "available": 36, + "diff": 34, + "status": "satisfy" + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 2, + "available": 0, + "diff": -2, + "status": "lack" + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0, + "available": 12, + "diff": 12, + "status": "satisfy" + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + } + ], + "need_reagents": [ + { + "substance": "1,4-二氧六环", + "need_mg": 0.0, + "need_ml": 0.2, + "base_need_mg": 0.0, + "base_need_ml": 0.2 + }, + { + "substance": "水", + "need_mg": 0.0, + "need_ml": 14.2, + "base_need_mg": 0.0, + "base_need_ml": 0.2 + } + ], + "need_consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0 + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 2 + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 0 + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 2 + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 2 + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 0 + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0 + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0 + } + ], + "missing": [ + "1,4-二氧六环:0.200mL", + "1mL枪头:2件", + "2mL反应管:2件" + ], + "redundant": [ + "水:11.770mL", + "50uL枪头:0件", + "5mL枪头:24件", + "2mL反应管磁子:34件", + "反应盖板:0件", + "闪滤内瓶:12件", + "闪滤外瓶:0件" + ], + "timestamp": "2026-01-20T17:18:48.619442", + "task_id": "668" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/668/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/668/task_info.json new file mode 100644 index 00000000..69c5f138 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/668/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "668", + "status": "UNSTARTED", + "created_at": "2026-01-20T17:18:26.187546" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/668/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/668/task_payload.json new file mode 100644 index 00000000..68f06a10 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/668/task_payload.json @@ -0,0 +1,146 @@ +{ + "task_id": 0, + "task_name": "test_20260120", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-993ff524", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-b0f187a3", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "1,4-二氧六环", + "chemical_id": 2967.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-0e55b07d", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-530ff7bc", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-1ad8c873", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "1,4-二氧六环", + "chemical_id": 2967.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 4, + "unit_id": "unit-cadf5d98", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 2, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/669/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/669/task_info.json new file mode 100644 index 00000000..28f1e04c --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/669/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "669", + "status": "UNSTARTED", + "created_at": "2026-01-20T17:22:25.746950" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/669/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/669/task_payload.json new file mode 100644 index 00000000..95b95f59 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/669/task_payload.json @@ -0,0 +1,146 @@ +{ + "task_id": 0, + "task_name": "test_20260120", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-364f721a", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-d05c806a", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-26131ec2", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-c1af6518", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-122c7c3c", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 4, + "unit_id": "unit-3b7b5796", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "µL", + "L" + ] + }, + "substance": "水", + "chemical_id": 2966.0, + "add_volume": 0.1 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 2, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/670/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/670/task_info.json new file mode 100644 index 00000000..4c717d7c --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/670/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "670", + "status": "UNSTARTED", + "created_at": "2026-01-20T17:23:08.393932" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/670/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/670/task_payload.json new file mode 100644 index 00000000..f297b1e3 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/670/task_payload.json @@ -0,0 +1,96 @@ +{ + "task_id": 0, + "task_name": "test_20260120", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-b008e05a", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-0a907bac", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-7d5e2c8f", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-1fb3f2b9", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647.0, + "add_volume": 0.1 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 2, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/671/resource_check.json b/unilabos/devices/eit_synthesis_station/data/tasks/671/resource_check.json new file mode 100644 index 00000000..b41182e6 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/671/resource_check.json @@ -0,0 +1,147 @@ +{ + "ready": true, + "reagents": [ + { + "substance": "2-溴吡啶", + "need_mg": 0.0, + "need_ml": 1.2, + "available_mg": 0.0, + "available_ml": 5.488, + "status": "冗余满足", + "diff": "4.288mL", + "base_need_mg": 0.0, + "base_need_ml": 0.2 + } + ], + "consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 1, + "available": 93, + "diff": 92, + "status": "satisfy" + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 0, + "available": 24, + "diff": 24, + "status": "satisfy" + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 0, + "available": 36, + "diff": 36, + "status": "satisfy" + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 2, + "available": 24, + "diff": 22, + "status": "satisfy" + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0, + "available": 12, + "diff": 12, + "status": "satisfy" + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + } + ], + "need_reagents": [ + { + "substance": "2-溴吡啶", + "need_mg": 0.0, + "need_ml": 1.2, + "base_need_mg": 0.0, + "base_need_ml": 0.2 + } + ], + "need_consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0 + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 1 + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 0 + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 0 + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 2 + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 0 + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0 + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0 + } + ], + "missing": [], + "redundant": [ + "2-溴吡啶:4.288mL", + "50uL枪头:0件", + "1mL枪头:92件", + "5mL枪头:24件", + "2mL反应管磁子:36件", + "2mL反应管:22件", + "反应盖板:0件", + "闪滤内瓶:12件", + "闪滤外瓶:0件" + ], + "timestamp": "2026-01-21T17:59:58.606854", + "task_id": "671" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/671/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/671/task_info.json new file mode 100644 index 00000000..17a96199 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/671/task_info.json @@ -0,0 +1,7 @@ +{ + "task_id": "671", + "status": "STOPPED", + "created_at": "2026-01-20T17:23:42.589063", + "started_at": "2026-01-20T18:14:10.750846", + "completed_at": "2026-01-20T18:23:18.277355" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/671/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/671/task_payload.json new file mode 100644 index 00000000..09f16b3b --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/671/task_payload.json @@ -0,0 +1,62 @@ +{ + "task_id": 0, + "task_name": "test_20260120", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-5bd36ace", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647.0, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-0a69143d", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647.0, + "add_volume": 0.1 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 2, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/672/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/672/task_info.json new file mode 100644 index 00000000..529765c6 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/672/task_info.json @@ -0,0 +1,6 @@ +{ + "task_id": "672", + "status": "STOPPED", + "started_at": "2026-01-20T19:22:15.876290", + "completed_at": "2026-01-20T19:25:29.586103" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/673/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/673/task_info.json new file mode 100644 index 00000000..a3a73c5b --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/673/task_info.json @@ -0,0 +1,6 @@ +{ + "task_id": "673", + "status": "COMPLETED", + "started_at": "2026-01-20T21:11:46.082932", + "completed_at": "2026-01-20T21:16:17.409092" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/674/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/674/task_info.json new file mode 100644 index 00000000..fb35c20e --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/674/task_info.json @@ -0,0 +1,6 @@ +{ + "task_id": "674", + "status": "RUNNING", + "started_at": "2026-01-20T22:40:02.116505", + "completed_at": "2026-01-20T21:33:31.691696" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/675/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/675/task_info.json new file mode 100644 index 00000000..6fd04102 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/675/task_info.json @@ -0,0 +1,6 @@ +{ + "task_id": "675", + "status": "COMPLETED", + "started_at": "2026-01-20T22:41:31.191663", + "completed_at": "2026-01-20T22:45:59.984419" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/676/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/676/task_info.json new file mode 100644 index 00000000..91bd65ea --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/676/task_info.json @@ -0,0 +1,7 @@ +{ + "task_id": "676", + "status": "COMPLETED", + "created_at": "2026-01-21T17:51:15.535272", + "started_at": "2026-01-21T18:00:22.253108", + "completed_at": "2026-01-21T18:05:06.091807" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/676/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/676/task_payload.json new file mode 100644 index 00000000..0f21176d --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/676/task_payload.json @@ -0,0 +1,62 @@ +{ + "task_id": 0, + "task_name": "test_20260121", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-52843c05", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-717e2ad3", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "2-溴吡啶", + "chemical_id": 647, + "add_volume": 0.1 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 2, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/677/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/677/task_info.json new file mode 100644 index 00000000..8d0e18ea --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/677/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "677", + "status": "UNSTARTED", + "created_at": "2026-01-23T00:24:48.549462" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/677/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/677/task_payload.json new file mode 100644 index 00000000..d7a54ed0 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/677/task_payload.json @@ -0,0 +1,1082 @@ +{ + "task_id": 0, + "task_name": "仪器示范", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-30911870", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.1, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 5.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-fa5c0db4", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 2, + "unit_id": "unit-20c0755c", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-2d1cd7ab", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-a85c48f5", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.2, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 10.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-91430398", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 2, + "unit_id": "unit-958066a3", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.2 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 1, + "unit_row": 4, + "unit_id": "unit-18f1a222", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 2, + "unit_row": 0, + "unit_id": "unit-cdb12b53", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.3, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 15.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 2, + "unit_row": 1, + "unit_id": "unit-06e8163a", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 2, + "unit_row": 2, + "unit_id": "unit-cfea5af1", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.3 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 2, + "unit_row": 4, + "unit_id": "unit-f7615755", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 3, + "unit_row": 0, + "unit_id": "unit-2827590a", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 3, + "unit_row": 1, + "unit_id": "unit-90ff2bdd", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 3, + "unit_row": 2, + "unit_id": "unit-f75d7264", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.4 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 3, + "unit_row": 4, + "unit_id": "unit-ce7ec4a6", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 4, + "unit_row": 0, + "unit_id": "unit-5e97de8d", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.5, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 4, + "unit_row": 1, + "unit_id": "unit-49691b30", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 4, + "unit_row": 2, + "unit_id": "unit-1170f4b5", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.5 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 4, + "unit_row": 4, + "unit_id": "unit-ab503e55", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 5, + "unit_row": 0, + "unit_id": "unit-961d89fe", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.6, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 30.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 5, + "unit_row": 1, + "unit_id": "unit-2957a58f", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 5, + "unit_row": 2, + "unit_id": "unit-6414baae", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.6 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 5, + "unit_row": 4, + "unit_id": "unit-3db6cedf", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 6, + "unit_row": 0, + "unit_id": "unit-cc3622f3", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.7, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 35.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 6, + "unit_row": 1, + "unit_id": "unit-27beabc5", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 6, + "unit_row": 2, + "unit_id": "unit-a36a97ab", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.7 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 6, + "unit_row": 4, + "unit_id": "unit-1d83a32c", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 7, + "unit_row": 0, + "unit_id": "unit-d8d056b5", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.8, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 40.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 7, + "unit_row": 1, + "unit_id": "unit-087f8c37", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 7, + "unit_row": 2, + "unit_id": "unit-80936466", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.8 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 7, + "unit_row": 4, + "unit_id": "unit-97db263a", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 8, + "unit_row": 0, + "unit_id": "unit-7c275860", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.9, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 45.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 8, + "unit_row": 1, + "unit_id": "unit-72582020", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 8, + "unit_row": 2, + "unit_id": "unit-ba72d006", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.9 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 8, + "unit_row": 4, + "unit_id": "unit-0d45c094", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 9, + "unit_row": 0, + "unit_id": "unit-0c62dfcc", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 1.0, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 50.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 9, + "unit_row": 1, + "unit_id": "unit-63327a13", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 9, + "unit_row": 2, + "unit_id": "unit-b28d33c6", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 9, + "unit_row": 4, + "unit_id": "unit-9cb017b9", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 10, + "unit_row": 0, + "unit_id": "unit-167a8a5e", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 1.0, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 55.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 10, + "unit_row": 1, + "unit_id": "unit-12bfe757", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 10, + "unit_row": 2, + "unit_id": "unit-8cc75eb3", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 10, + "unit_row": 4, + "unit_id": "unit-ab56e98f", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 11, + "unit_row": 0, + "unit_id": "unit-8e8fcc12", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 1.0, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 60.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 11, + "unit_row": 1, + "unit_id": "unit-19d8094b", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 11, + "unit_row": 2, + "unit_id": "unit-cc44c32c", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.2 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 11, + "unit_row": 4, + "unit_id": "unit-e8c5ec26", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 12, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/678/resource_check.json b/unilabos/devices/eit_synthesis_station/data/tasks/678/resource_check.json new file mode 100644 index 00000000..787330f4 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/678/resource_check.json @@ -0,0 +1,167 @@ +{ + "ready": false, + "reagents": [ + { + "substance": "碳酸铯", + "need_mg": 410.0, + "need_ml": 0.0, + "available_mg": 1872.8, + "available_ml": 0.0, + "status": "冗余满足", + "diff": "1462.8mg", + "base_need_mg": 390.0, + "base_need_ml": 0.0 + }, + { + "substance": "乙腈", + "need_mg": 0.0, + "need_ml": 21.8, + "available_mg": 0.0, + "available_ml": 120.0, + "status": "冗余满足", + "diff": "98.200mL", + "base_need_mg": 0.0, + "base_need_ml": 7.8 + } + ], + "consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 1, + "available": 0, + "diff": -1, + "status": "lack" + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 12, + "available": 24, + "diff": 12, + "status": "satisfy" + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 12, + "available": 0, + "diff": -12, + "status": "lack" + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 1, + "available": 0, + "diff": -1, + "status": "lack" + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + } + ], + "need_reagents": [ + { + "substance": "碳酸铯", + "need_mg": 410.0, + "need_ml": 0.0, + "base_need_mg": 390.0, + "base_need_ml": 0.0 + }, + { + "substance": "乙腈", + "need_mg": 0.0, + "need_ml": 21.8, + "base_need_mg": 0.0, + "base_need_ml": 7.8 + } + ], + "need_consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0 + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 0 + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 1 + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 12 + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 12 + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 1 + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0 + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0 + } + ], + "missing": [ + "5mL枪头:1件", + "2mL反应管:12件", + "反应盖板:1件" + ], + "redundant": [ + "碳酸铯:1462.8mg", + "乙腈:98.200mL", + "50uL枪头:0件", + "1mL枪头:0件", + "2mL反应管磁子:12件", + "闪滤内瓶:0件", + "闪滤外瓶:0件" + ], + "timestamp": "2026-01-23T00:26:06.113630", + "task_id": "678" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/678/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/678/task_info.json new file mode 100644 index 00000000..24b9ef07 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/678/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "678", + "status": "UNSTARTED", + "created_at": "2026-01-23T00:26:02.635914" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/678/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/678/task_payload.json new file mode 100644 index 00000000..27b4ff74 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/678/task_payload.json @@ -0,0 +1,1082 @@ +{ + "task_id": 0, + "task_name": "仪器示范", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-a2469952", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.1, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 5.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-55b58e86", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 2, + "unit_id": "unit-f17b9dcd", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-b184925a", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-bd3adf9e", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.2, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 10.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-25792724", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 2, + "unit_id": "unit-f77c7f5b", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.2 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 1, + "unit_row": 4, + "unit_id": "unit-1b760c79", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 2, + "unit_row": 0, + "unit_id": "unit-47e69fa0", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.3, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 15.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 2, + "unit_row": 1, + "unit_id": "unit-8d838bd7", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 2, + "unit_row": 2, + "unit_id": "unit-e994786a", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.3 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 2, + "unit_row": 4, + "unit_id": "unit-f099caff", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 3, + "unit_row": 0, + "unit_id": "unit-8fe076c1", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 3, + "unit_row": 1, + "unit_id": "unit-49ac4d22", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 3, + "unit_row": 2, + "unit_id": "unit-787fb9c8", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.4 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 3, + "unit_row": 4, + "unit_id": "unit-76434ebb", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 4, + "unit_row": 0, + "unit_id": "unit-536e2e35", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.5, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 4, + "unit_row": 1, + "unit_id": "unit-744eb42c", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 4, + "unit_row": 2, + "unit_id": "unit-89b8a5b8", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.5 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 4, + "unit_row": 4, + "unit_id": "unit-865dc552", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 5, + "unit_row": 0, + "unit_id": "unit-c0ac321e", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.6, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 30.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 5, + "unit_row": 1, + "unit_id": "unit-d6873391", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 5, + "unit_row": 2, + "unit_id": "unit-03b40c5f", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.6 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 5, + "unit_row": 4, + "unit_id": "unit-a2cdc02a", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 6, + "unit_row": 0, + "unit_id": "unit-bc605d61", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.7, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 35.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 6, + "unit_row": 1, + "unit_id": "unit-bd99044f", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 6, + "unit_row": 2, + "unit_id": "unit-3442e990", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.7 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 6, + "unit_row": 4, + "unit_id": "unit-48263077", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 7, + "unit_row": 0, + "unit_id": "unit-9e97e070", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.8, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 40.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 7, + "unit_row": 1, + "unit_id": "unit-81f0948f", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 7, + "unit_row": 2, + "unit_id": "unit-36bfa319", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.8 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 7, + "unit_row": 4, + "unit_id": "unit-cdc531c7", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 8, + "unit_row": 0, + "unit_id": "unit-bc1f46ee", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.9, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 45.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 8, + "unit_row": 1, + "unit_id": "unit-0e3fc89a", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 8, + "unit_row": 2, + "unit_id": "unit-875068a4", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 0.9 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 8, + "unit_row": 4, + "unit_id": "unit-5387a6c8", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 9, + "unit_row": 0, + "unit_id": "unit-f9125770", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 1.0, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 50.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 9, + "unit_row": 1, + "unit_id": "unit-0449e80e", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 9, + "unit_row": 2, + "unit_id": "unit-a4c9a15c", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 9, + "unit_row": 4, + "unit_id": "unit-5bf99eb5", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 10, + "unit_row": 0, + "unit_id": "unit-b5187991", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 1.0, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 55.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 10, + "unit_row": 1, + "unit_id": "unit-5eef26d5", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 10, + "unit_row": 2, + "unit_id": "unit-1d8a27f2", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.1 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 10, + "unit_row": 4, + "unit_id": "unit-882c7151", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 11, + "unit_row": 0, + "unit_id": "unit-bffe70d1", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 1.0, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 60.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 11, + "unit_row": 1, + "unit_id": "unit-55f56d5c", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 11, + "unit_row": 2, + "unit_id": "unit-ce705b3d", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.2 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 11, + "unit_row": 4, + "unit_id": "unit-c3881631", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 12, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/679/resource_check.json b/unilabos/devices/eit_synthesis_station/data/tasks/679/resource_check.json new file mode 100644 index 00000000..0f5883f8 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/679/resource_check.json @@ -0,0 +1,167 @@ +{ + "ready": false, + "reagents": [ + { + "substance": "碳酸铯", + "need_mg": 260.0, + "need_ml": 0.0, + "available_mg": 1872.8, + "available_ml": 0.0, + "status": "冗余满足", + "diff": "1612.8mg", + "base_need_mg": 240.0, + "base_need_ml": 0.0 + }, + { + "substance": "乙腈", + "need_mg": 0.0, + "need_ml": 26.0, + "available_mg": 0.0, + "available_ml": 120.0, + "status": "冗余满足", + "diff": "94.000mL", + "base_need_mg": 0.0, + "base_need_ml": 12.0 + } + ], + "consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0, + "available": 96, + "diff": 96, + "status": "satisfy" + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 0, + "available": 96, + "diff": 96, + "status": "satisfy" + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 1, + "available": 24, + "diff": 23, + "status": "satisfy" + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 12, + "available": 24, + "diff": 12, + "status": "satisfy" + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 12, + "available": 0, + "diff": -12, + "status": "lack" + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 1, + "available": 1, + "diff": 0, + "status": "satisfy" + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0, + "available": 48, + "diff": 48, + "status": "satisfy" + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0, + "available": 0, + "diff": 0, + "status": "satisfy" + } + ], + "need_reagents": [ + { + "substance": "碳酸铯", + "need_mg": 260.0, + "need_ml": 0.0, + "base_need_mg": 240.0, + "base_need_ml": 0.0 + }, + { + "substance": "乙腈", + "need_mg": 0.0, + "need_ml": 26.0, + "base_need_mg": 0.0, + "base_need_ml": 12.0 + } + ], + "need_consumables": [ + { + "code": 220000304, + "name": "50uL枪头", + "need": 0 + }, + { + "code": 220000308, + "name": "1mL枪头", + "need": 0 + }, + { + "code": 214000037, + "name": "5mL枪头", + "need": 1 + }, + { + "code": 220000322, + "name": "2mL反应管磁子", + "need": 12 + }, + { + "code": 551000502, + "name": "2mL反应管", + "need": 12 + }, + { + "code": 211009427, + "name": "反应盖板", + "need": 1 + }, + { + "code": 220000320, + "name": "闪滤内瓶", + "need": 0 + }, + { + "code": 220000321, + "name": "闪滤外瓶", + "need": 0 + } + ], + "missing": [ + "2mL反应管:12件" + ], + "redundant": [ + "碳酸铯:1612.8mg", + "乙腈:94.000mL", + "50uL枪头:96件", + "1mL枪头:96件", + "5mL枪头:23件", + "2mL反应管磁子:12件", + "反应盖板:0件", + "闪滤内瓶:48件", + "闪滤外瓶:0件" + ], + "timestamp": "2026-01-23T08:29:42.038033", + "task_id": "679" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/679/task_info.json b/unilabos/devices/eit_synthesis_station/data/tasks/679/task_info.json new file mode 100644 index 00000000..9eca6818 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/679/task_info.json @@ -0,0 +1,5 @@ +{ + "task_id": "679", + "status": "UNSTARTED", + "created_at": "2026-01-23T08:29:40.411508" +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/data/tasks/679/task_payload.json b/unilabos/devices/eit_synthesis_station/data/tasks/679/task_payload.json new file mode 100644 index 00000000..54e0945d --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/data/tasks/679/task_payload.json @@ -0,0 +1,1082 @@ +{ + "task_id": 0, + "task_name": "流程测试", + "layout_list": [ + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 0, + "unit_id": "unit-d6d30755", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 0, + "unit_row": 1, + "unit_id": "unit-0937e400", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 0, + "unit_row": 2, + "unit_id": "unit-d65a247e", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 0, + "unit_row": 4, + "unit_id": "unit-32bbaadc", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 0, + "unit_id": "unit-6753b568", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 1, + "unit_row": 1, + "unit_id": "unit-23b1abb7", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 1, + "unit_row": 2, + "unit_id": "unit-ba50ed9b", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 1, + "unit_row": 4, + "unit_id": "unit-ca6f9ce7", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 2, + "unit_row": 0, + "unit_id": "unit-328d1b03", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 2, + "unit_row": 1, + "unit_id": "unit-2ae56b2d", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 2, + "unit_row": 2, + "unit_id": "unit-babcf836", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 2, + "unit_row": 4, + "unit_id": "unit-9611a147", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 3, + "unit_row": 0, + "unit_id": "unit-ec2b40b5", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 3, + "unit_row": 1, + "unit_id": "unit-8a025147", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 3, + "unit_row": 2, + "unit_id": "unit-0b6e3e14", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 3, + "unit_row": 4, + "unit_id": "unit-623d4fa2", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 4, + "unit_row": 0, + "unit_id": "unit-ccc6ca22", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 4, + "unit_row": 1, + "unit_id": "unit-48da4a77", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 4, + "unit_row": 2, + "unit_id": "unit-2b6108f3", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 4, + "unit_row": 4, + "unit_id": "unit-8d5c1047", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 5, + "unit_row": 0, + "unit_id": "unit-23729a3e", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 5, + "unit_row": 1, + "unit_id": "unit-1c9d8f85", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 5, + "unit_row": 2, + "unit_id": "unit-e5773265", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 5, + "unit_row": 4, + "unit_id": "unit-c751cf6e", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 6, + "unit_row": 0, + "unit_id": "unit-46ec8bbb", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 6, + "unit_row": 1, + "unit_id": "unit-23a7f3a7", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 6, + "unit_row": 2, + "unit_id": "unit-ce614d36", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 6, + "unit_row": 4, + "unit_id": "unit-f6e5fbe0", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 7, + "unit_row": 0, + "unit_id": "unit-e693224f", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 7, + "unit_row": 1, + "unit_id": "unit-24d948c0", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 7, + "unit_row": 2, + "unit_id": "unit-eb08c4f2", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 7, + "unit_row": 4, + "unit_id": "unit-bcf0ce3a", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 8, + "unit_row": 0, + "unit_id": "unit-ea7395b4", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 8, + "unit_row": 1, + "unit_id": "unit-e1a651ac", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 8, + "unit_row": 2, + "unit_id": "unit-b769e268", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 8, + "unit_row": 4, + "unit_id": "unit-c3be8580", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 9, + "unit_row": 0, + "unit_id": "unit-8aac5c95", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 9, + "unit_row": 1, + "unit_id": "unit-99fa93eb", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 9, + "unit_row": 2, + "unit_id": "unit-132e0be8", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 9, + "unit_row": 4, + "unit_id": "unit-7701ff12", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 10, + "unit_row": 0, + "unit_id": "unit-7605ece0", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 10, + "unit_row": 1, + "unit_id": "unit-28bf40f6", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 10, + "unit_row": 2, + "unit_id": "unit-4e7b544d", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 10, + "unit_row": 4, + "unit_id": "unit-20691fbf", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 11, + "unit_row": 0, + "unit_id": "unit-014739f7", + "unit_type": "exp_add_powder", + "process_json": { + "offset": 0.4, + "custom": { + "unit": "mg", + "unitOptions": [ + "mg", + "g" + ] + }, + "substance": "碳酸铯", + "chemical_id": 385, + "add_weight": 20.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_add_magnet", + "unit_column": 11, + "unit_row": 1, + "unit_id": "unit-eec96067", + "process_json": { + "custom": { + "unit": "" + } + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_column": 11, + "unit_row": 2, + "unit_id": "unit-db27d8db", + "unit_type": "exp_pipetting", + "process_json": { + "custom": { + "unit": "mL", + "unitOptions": [ + "mL", + "L" + ] + }, + "substance": "乙腈", + "chemical_id": 381, + "add_volume": 1.0 + } + }, + { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + "unit_type": "exp_magnetic_stirrer", + "unit_column": 11, + "unit_row": 4, + "unit_id": "unit-01cee149", + "process_json": { + "rotation_speed": 500, + "reaction_duration": 360, + "is_wait": true, + "custom": { + "unit": "" + }, + "temperature": 25.0, + "is_heating": true, + "target_temperature": 25.0 + } + } + ], + "task_setup": { + "subtype": null, + "experiment_num": 12, + "vessel": "551000502", + "added_slots": "" + }, + "is_audit_log": 1, + "is_copy": false +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/driver/__init__.py b/unilabos/devices/eit_synthesis_station/driver/__init__.py new file mode 100644 index 00000000..b77438f2 --- /dev/null +++ b/unilabos/devices/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/eit_synthesis_station/driver/api_client.py b/unilabos/devices/eit_synthesis_station/driver/api_client.py new file mode 100644 index 00000000..80265df9 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/driver/api_client.py @@ -0,0 +1,757 @@ +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: + """ + 功能: + 启动任务. 请求超时时间改为30s. + 参数: + task_id: 任务 id. + 返回: + Dict. + """ + return self._request("POST", "/api/StartTask", json_body={"task_id": task_id}, timeout_s=30) + + # 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) + + # 32. 查询资源设置参数 + def get_set_up(self) -> JsonDict: + """ + 功能: + 请求后台通用设置参数, 对应 GetSetUp 接口. + 参数: + 无 + 返回: + Dict, 接口原始响应. + """ + return self._request("POST", "/api/GetSetUp", json_body={}) + + # 33. 获取任务进度信息 + def get_task_op_info(self, task_id: int) -> JsonDict: + """ + 功能: + 获取任务的操作进度信息, 对应 GetTaskOpInfo + 参数: + task_id: int, 任务id + 返回: + Dict, 接口响应 + """ + return self._request("POST", "/api/GetTaskOpInfo", json_body={"task_id": task_id}) + + # 34. 进行物料核算 + def check_task_resource(self, task_id: int) -> JsonDict: + """ + 功能: + 检查任务资源, 对应 CheckTaskResource. code=1200 时仅警告不抛异常 + 参数: + task_id: int, 任务id + 返回: + Dict, 接口响应, 包含 code, msg 等字段. code=1200 时返回完整响应体 + """ + try: + return self._request("POST", "/api/CheckTaskResource", json_body={"task_id": task_id}) + except ApiError as e: + # code=1200 表示任务资源不足, 仅警告不抛异常, 返回完整响应 + if e.code == 1200: + # 返回完整的响应体, 包含 code, msg, prompt_msg 等字段 + return e.payload if e.payload else {"code": e.code, "msg": e.msg} + else: + # 其他错误码继续抛出异常 + raise + + # 35. 导出任务报告 + def export_task_report( + self, + task_ids: List[int], + file_type: str = "excel", + *, + timeout_s: Optional[float] = 60, + ) -> bytes: + """ + 功能: + 导出任务报告, 对应 ExportTaskReport. 返回二进制文件内容(Excel等) + 参数: + task_ids: List[int], 需要导出的任务id列表 + file_type: str, 文件类型, 默认 "excel" + timeout_s: Optional[float], 超时时间, 默认60秒 + 返回: + bytes, 文件二进制内容 + """ + if not task_ids: + raise ValidationError("task_ids 不能为空") + + url = self._url("/api/ExportTaskReport") + timeout = timeout_s if timeout_s is not None else self._settings.timeout_s + json_body = {"task_ids": task_ids, "file_type": file_type} + + headers: Dict[str, str] = {"Content-Type": "application/json"} + if self._access_token: + token_type = self._token_type or "Bearer" + headers["Authorization"] = f"{token_type} {self._access_token}" + + self._logger.debug( + "HTTP request (file download), url=%s, json=%s", + url, self._mask_sensitive(json_body), + ) + + try: + resp = self._session.request( + method="POST", + url=url, + headers=headers, + 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: + raise RequestError( + f"HTTP错误, status={resp.status_code}, url={url}, body={resp.text}", + status_code=resp.status_code, + ) + + self._logger.debug( + "文件下载完成, url=%s, content_length=%s", + url, len(resp.content), + ) + return resp.content + + # 36. 单独控制W1货架 + def single_control_w1_shelf(self, station: str, action: str, op: str, num: int) -> JsonDict: + """ + 功能: + 单独控制W1货架的推出或复位操作 + 参数: + station: str, 站点编码, 例如 "FSY" + action: str, 动作类型, "home" 表示复位, "outside" 表示推出 + op: str, 操作类型, 与action保持一致 + num: int, 货架编号, 1表示W-1-1和W-1-2, 3表示W-1-3和W-1-4, 5表示W-1-5和W-1-6, 7表示W-1-7和W-1-8 + 返回: + Dict, 接口响应 + """ + if action not in ("home", "outside"): + raise ValidationError("action 必须是 'home' 或 'outside'") + if num not in (1, 3, 5, 7): + raise ValidationError("num 必须是 1, 3, 5 或 7") + + body = {"action": action, "op": op, "num": num} + return self._request( + "POST", + "/api/SingleControlW1Shelf", + json_body=body, + params={"station": station}, + ) + +if __name__ == "__main__": + + settings = Settings.from_env() + client = ApiClient(settings) + resp = client.login('admin','admin') + token_type = str(resp.get("token_type", "Bearer")) + access_token = str(resp.get("access_token", "")) + client.set_token(token_type, access_token) + resp = client.check_task_resource(665) + print(resp) \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/driver/exceptions.py b/unilabos/devices/eit_synthesis_station/driver/exceptions.py new file mode 100644 index 00000000..eca0ae6c --- /dev/null +++ b/unilabos/devices/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/eit_synthesis_station/eit_synthesis_station.yaml b/unilabos/devices/eit_synthesis_station/eit_synthesis_station.yaml new file mode 100644 index 00000000..8fb59ab0 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/eit_synthesis_station.yaml @@ -0,0 +1,572 @@ +eit_synthesis_station: + category: + - eit_synthesis_station + class: + action_value_mappings: + align_chemicals_with_file: + feedback: {} + goal: {} + goal_default: + auto_delete: true + file_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + auto_delete: + default: true + type: boolean + file_path: + type: string + required: + - file_path + type: object + result: {} + required: + - goal + title: align_chemicals_with_file参数 + type: object + type: UniLabJsonCommand + auto_unload_trays_to_agv: + feedback: {} + goal: {} + goal_default: + batch_out_file: null + block: true + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + batch_out_file: + type: string + block: + default: true + type: boolean + required: [] + type: object + result: {} + required: + - goal + title: auto_unload_trays_to_agv参数 + type: object + type: UniLabJsonCommand + batch_in_tray_by_file: + feedback: {} + goal: {} + goal_default: + file_path: unilabos/devices/eit_synthesis_station/sheet/batch_in_tray.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + type: string + required: + - file_path + type: object + result: {} + required: + - goal + title: batch_in_tray_by_file参数 + type: object + type: UniLabJsonCommand + batch_in_tray_with_agv_transfer: + feedback: {} + goal: {} + goal_default: + block: true + file_path: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + block: + default: true + type: boolean + file_path: + type: string + required: [] + type: object + result: {} + required: + - goal + title: batch_in_tray_with_agv_transfer参数 + type: object + type: UniLabJsonCommand + batch_out_empty_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_empty_trays参数 + type: object + type: UniLabJsonCommand + batch_out_task_and_empty_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: string + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_task_and_empty_trays参数 + type: object + type: UniLabJsonCommand + batch_out_task_and_chemical_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: string + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_task_and_chemical_trays参数 + type: object + type: UniLabJsonCommand + batch_out_task_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: string + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_task_trays参数 + type: object + type: UniLabJsonCommand + batch_out_tray: + feedback: {} + goal: {} + goal_default: + layout_list: null + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + layout_list: + items: + type: object + type: array + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: integer + timeout_s: + default: 900.0 + type: number + required: + - layout_list + type: object + result: {} + required: + - goal + title: batch_out_tray参数 + type: object + type: UniLabJsonCommand + check_resource_for_task: + feedback: {} + goal: {} + goal_default: + auto_generate_batch_file: true + chemical_db_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + template_path: unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + auto_generate_batch_file: + default: true + type: boolean + chemical_db_path: + type: string + template_path: + type: string + required: + - template_path + - chemical_db_path + type: object + result: {} + required: + - goal + title: check_resource_for_task参数 + type: object + type: UniLabJsonCommand + create_task_by_file: + feedback: {} + goal: {} + goal_default: + chemical_db_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + template_path: unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + chemical_db_path: + type: string + template_path: + type: string + required: + - template_path + - chemical_db_path + type: object + result: {} + required: + - goal + title: create_task_by_file参数 + type: object + type: UniLabJsonCommand + device_init: + feedback: {} + goal: {} + goal_default: + device_id: null + poll_interval_s: 1.0 + timeout_s: 600.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + device_id: + type: string + poll_interval_s: + default: 1.0 + type: number + timeout_s: + default: 600.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: device_init参数 + type: object + type: UniLabJsonCommand + poll_analysis_run: + feedback: {} + goal: {} + goal_default: + poll_interval: 30.0 + task_id: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + poll_interval: + default: 30.0 + type: number + task_id: + type: string + required: [] + type: object + result: {} + required: + - goal + title: poll_analysis_run参数 + type: object + type: UniLabJsonCommand + run_analysis: + feedback: {} + goal: {} + goal_default: + task_id: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + task_id: + type: string + required: [] + type: object + result: {} + required: + - goal + title: run_analysis参数 + type: object + type: UniLabJsonCommand + start_task: + feedback: {} + goal: {} + goal_default: + check_glovebox_env: true + oxygen_limit_ppm: 10.0 + task_id: null + water_limit_ppm: 10.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + check_glovebox_env: + default: true + type: boolean + oxygen_limit_ppm: + default: 10.0 + type: number + task_id: + type: string + water_limit_ppm: + default: 10.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: start_task参数 + type: object + type: UniLabJsonCommand + submit_experiment_task: + feedback: {} + goal: {} + goal_default: + auto_magnet: true + chemical_db_path: null + diluent_name: '' + duration: '8' + fixed_order: false + internal_std_name: '' + reaction_type: heat + rows: null + stir_speed: '500' + stir_time_after_std: '' + target_temp: '30' + task_name: Unilab_Auto_Job + temperature: '40' + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + auto_magnet: + default: true + type: boolean + chemical_db_path: + type: string + diluent_name: + default: '' + type: string + duration: + default: '8' + type: string + fixed_order: + default: false + type: boolean + internal_std_name: + default: '' + type: string + reaction_type: + default: heat + type: string + rows: + type: array + stir_speed: + default: '500' + type: string + stir_time_after_std: + default: '' + type: string + target_temp: + default: '30' + type: string + task_name: + default: Unilab_Auto_Job + type: string + temperature: + default: '40' + type: string + required: + - chemical_db_path + type: object + result: {} + required: + - goal + title: submit_experiment_task参数 + type: object + type: UniLabJsonCommand + module: unilabos.devices.eit_synthesis_station.manager.station_manager:SynthesisStationManager + status_types: {} + type: python + config_info: [] + description: '' + handles: [] + icon: '' + init_param_schema: + config: + properties: + settings: + type: string + required: [] + type: object + data: + properties: {} + required: [] + type: object + registry_type: device + version: 1.0.0 diff --git a/unilabos/devices/eit_synthesis_station/main.py b/unilabos/devices/eit_synthesis_station/main.py new file mode 100644 index 00000000..227091ec --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/main.py @@ -0,0 +1,1045 @@ +# -*- coding: utf-8 -*- +from .manager.station_manager import SynthesisStationManager +from .config.setting import Settings +from .config.constants import TaskStatus, StationState +from pathlib import Path +import json +import logging +import traceback + +logger = logging.getLogger("InteractiveCLI") + +# ===================== 模块常量 ===================== + +ROOT = Path(__file__).resolve().parent +DEFAULT_TASK_TPL = ROOT / "sheet" / "reaction_template.xlsx" +DEFAULT_CHEM_DB = ROOT / "sheet" / "chemical_list.xlsx" +DEFAULT_TEMPLATE_IN = ROOT / "sheet" / "batch_in_tray.xlsx" + +# 任务状态码 -> 中文名称 +_TASK_STATUS_LABEL = { + TaskStatus.UNSTARTED: "未开始", + TaskStatus.RUNNING: "运行中", + TaskStatus.COMPLETED: "已完成", + TaskStatus.PAUSED: "已暂停", + TaskStatus.FAILED: "失败", + TaskStatus.STOPPED: "已停止", + TaskStatus.PAUSING: "暂停中", + TaskStatus.STOPPING: "停止中", + TaskStatus.WAITING: "等待中", + TaskStatus.HOLDING: "挂起", +} + +# 工站状态码 -> 中文名称 +_STATION_STATE_LABEL = { + StationState.IDLE: "空闲/待机", + StationState.RUNNING: "运行中", + StationState.PAUSED: "已暂停", + StationState.PAUSING: "暂停中", + StationState.STOPPING: "停止中", + StationState.HOLDING: "挂起/保持", +} + +# 运行时缓存: 最近操作的任务ID +_last_task_id = None + + +# ===================== 工具函数 ===================== + +def _input_with_default(prompt, default=None): + """ + 功能: + 带默认值的输入提示, 用户直接回车即采用默认值 + 参数: + prompt: str, 提示文本 + default: 默认值, None 时不显示默认值 + 返回: + str, 用户输入或默认值的字符串形式 + """ + if default is not None: + display = f"{prompt} [默认: {default}]: " + else: + display = f"{prompt}: " + val = input(display).strip() + if val == "" and default is not None: + return str(default) + return val + + +def _input_file_path(prompt, default_path): + """ + 功能: + 输入文件路径, 带默认值 + 参数: + prompt: str, 提示文本 + default_path: Path 或 str, 默认路径 + 返回: + str, 文件路径字符串 + """ + return _input_with_default(prompt, str(default_path)) + + +def _input_task_id(allow_none=True): + """ + 功能: + 提示用户输入任务ID, 支持默认值和留空 + 参数: + allow_none: bool, 是否允许留空(由系统自动选取) + 返回: + int | None, 任务ID或None + """ + global _last_task_id + hint = "请输入任务ID" + if allow_none is True: + hint += "(留空则自动选取)" + val = _input_with_default(hint, _last_task_id) + if val == "" or val == "None": + return None + try: + tid = int(val) + _last_task_id = tid + return tid + except ValueError: + print("输入的任务ID格式不正确, 将自动选取") + return None + + +def _input_positive_float(prompt): + """ + 功能: + 提示用户输入大于 0 的数值, 输入非法时循环重试. + 参数: + prompt: str, 提示文本. + 返回: + float, 用户输入的正数. + """ + while True: + val = input(f"{prompt}: ").strip() + if val == "": + print("输入不能为空") + continue + try: + numeric_value = float(val) + except ValueError: + print("请输入数字") + continue + if numeric_value <= 0: + print("请输入大于0的数值") + continue + return numeric_value + + +def _pause(): + """按回车键继续""" + input("\n按回车键继续...") + + +def _safe_run(func, *args, **kwargs): + """ + 功能: + 安全执行函数, 捕获异常并友好提示 + 参数: + func: 可调用对象 + *args, **kwargs: 透传参数 + 返回: + func 的返回值, 异常时返回 None + """ + try: + result = func(*args, **kwargs) + return result + except KeyboardInterrupt: + print("\n操作已被用户中断") + except Exception as exc: + logger.debug("异常详情:\n%s", traceback.format_exc()) + print(f"\n操作失败: {exc}") + _pause() + return None + + +def _print_menu(title, options): + """ + 功能: + 打印格式化菜单 + 参数: + title: str, 菜单标题 + options: list[tuple[str, str]], (编号, 描述) 列表 + 返回: + None + """ + width = 48 + print("\n" + "=" * width) + print(f" {title}") + print("=" * width) + for num, desc in options: + print(f" {num}. {desc}") + print("=" * width) + + +def _print_result(result): + """ + 功能: + 格式化打印 API 返回结果 + 参数: + result: 任意可 JSON 序列化的对象 + 返回: + None + """ + if result is None: + return + try: + print(json.dumps(result, ensure_ascii=False, indent=2)) + except (TypeError, ValueError): + print(result) + + +def _print_chemical_append_summary(result, *, source_label="查询"): + """ + 功能: + 以精简摘要输出化学品追加成功结果, 避免直接打印完整结果字典. + 参数: + result: dict | None, 化学品追加结果. + source_label: str, 成功来源标识, 例如 在线查询 或 SMILES 查询. + 返回: + None + """ + if isinstance(result, dict) is False: + return + + row_data = result.get("row_data") or {} + if isinstance(row_data, dict) is False: + row_data = {} + + cas_number = str(row_data.get("cas_number") or "").strip() + display_name = str( + row_data.get("substance") + or row_data.get("substance_chinese_name") + or row_data.get("substance_english_name") + or "" + ).strip() + row_index = result.get("row_index") + substance = str( + row_data.get("substance") + or row_data.get("substance_chinese_name") + or "" + ).strip() + physical_state = str(row_data.get("physical_state") or "").strip() + physical_form = str(row_data.get("physical_form") or "").strip() + + print( + f"已成功添加化合物: CAS={cas_number}, 名称={display_name}, " + f"substance={substance}, physical_state={physical_state}, " + f"physical_form={physical_form}, 行号={row_index}" + ) + + +def _print_prepared_chemical_summary(result): + """ + 功能: + 输出溶液或 beads 派生条目的精简摘要与配制结果. + 参数: + result: dict | None, prepare_solution_or_beads 的返回结果. + 返回: + None + """ + if isinstance(result, dict) is False: + return + + base_row_data = result.get("base_row_data") or {} + derived_row_data = result.get("derived_row_data") or {} + recipe = result.get("recipe") or {} + + base_name = str( + base_row_data.get("base_substance") + or base_row_data.get("substance") + or base_row_data.get("substance_chinese_name") + or base_row_data.get("substance_english_name") + or "" + ).strip() + derived_name = str( + derived_row_data.get("substance") + or derived_row_data.get("substance_chinese_name") + or derived_row_data.get("substance_english_name") + or "" + ).strip() + base_created = bool(result.get("base_created")) + derived_row_index = result.get("derived_row_index") + instruction_text = str(recipe.get("instruction_text") or "").strip() + + base_source_text = "新补录母体" if base_created is True else "复用现有母体" + print(f"母体条目: 名称={base_name}, 来源={base_source_text}") + print(f"已成功添加派生条目: 名称={derived_name}, 行号={derived_row_index}") + if instruction_text != "": + print(f"配制结果: {instruction_text}") + + solute_volume_ml = recipe.get("solute_volume_ml") + if solute_volume_ml is not None: + print(f"液体母体估算体积: {solute_volume_ml:.6g} mL") + + +def _update_task_id(result): + """ + 功能: + 从 create_task 返回值提取 task_id 并缓存 + 参数: + result: dict, create_task_by_file 的返回值 + 返回: + result 原样返回 + """ + global _last_task_id + if isinstance(result, dict): + # 尝试从多种可能的返回结构中提取 task_id + tid = result.get("task_id") or result.get("id") + if tid is None: + inner = result.get("result", {}) + if isinstance(inner, dict): + tid = inner.get("task_id") or inner.get("id") + if tid is not None: + try: + _last_task_id = int(tid) + print(f"已记录任务ID: {_last_task_id}") + except (ValueError, TypeError): + pass + return result + + +# ===================== 工作流执行器 ===================== + +def _run_workflow(steps, quick=False): + """ + 功能: + 按顺序执行工作流步骤, 每步可跳过或终止; + quick=True 时跳过逐步确认, 直接连续执行 + 参数: + steps: list[tuple[str, callable]], (步骤名, 执行函数) 列表 + quick: bool, 是否跳过逐步确认直接执行 + 返回: + None + """ + for i, (name, action) in enumerate(steps, 1): + print(f"\n--- 步骤 {i}/{len(steps)}: {name} ---") + if quick is False: + confirm = input("继续执行? (y/n/q) [默认: y]: ").strip().lower() + if confirm == "n": + print(f"已跳过: {name}") + continue + if confirm == "q": + print("工作流已终止") + return + result = _safe_run(action) + if result is None: + if quick is True: + # 快速模式下步骤失败, 询问是否继续 + continue + else: + retry = input("该步骤可能未成功, 是否继续后续步骤? (y/n) [默认: n]: ").strip().lower() + if retry != "y": + print("工作流已终止") + return + print("\n工作流执行完毕!") + _pause() + + +# ===================== 1. 快速工作流 ===================== + +def _menu_quick_workflow(manager): + """快速工作流子菜单""" + options = [ + ("1", "完整合成流程(AGV上料)"), + ("2", "完整合成流程(手动上料)"), + ("3", "上传任务流程"), + ("4", "agv上料+提交合成任务+分析执行流程"), + ("5", "等待任务完成+分析执行流程"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("快速工作流", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + + # 收集所有工作流共用的文件路径 + if choice in ("1", "2", "3"): + task_tpl = _input_file_path("任务模板路径", DEFAULT_TASK_TPL) + chem_db = _input_file_path("化学品库路径", DEFAULT_CHEM_DB) + + if choice == "1": + # 完整合成流程(AGV上料) + steps = [ + ("对齐化学品库", lambda: manager.align_chemicals_with_file(chem_db)), + ("上传任务到工站", lambda: _update_task_id(manager.create_task_by_file(task_tpl, chem_db))), + ("物料核算", lambda: manager.check_resource_for_task(task_tpl, chem_db)), + ("AGV上料", lambda: manager.batch_in_tray_with_agv_transfer()), + ("启动任务", lambda: manager.start_task()), + ("等待任务完成", lambda: manager.wait_task_with_ops()), + ("下料(任务物料+空托盘)", lambda: manager.batch_out_task_and_empty_trays()), + ("AGV自动下料", lambda: manager.auto_unload_trays_to_agv()), + ("谱图数据处理", lambda: manager.poll_analysis_run()), + ] + _run_workflow(steps, quick=True) + + elif choice == "2": + # 完整合成流程(手动上料) + template_in = _input_file_path("上料文件路径", DEFAULT_TEMPLATE_IN) + steps = [ + ("对齐化学品库", lambda: manager.align_chemicals_with_file(chem_db)), + ("上传任务到工站", lambda: _update_task_id(manager.create_task_by_file(task_tpl, chem_db))), + ("物料核算", lambda: manager.check_resource_for_task(task_tpl, chem_db)), + ("手动上料", lambda: manager.batch_in_tray_by_file(template_in)), + ("启动任务", lambda: manager.start_task()), + ("等待任务完成", lambda: manager.wait_task_with_ops()), + ("下料(任务物料+空托盘)", lambda: manager.batch_out_task_and_empty_trays()), + ("AGV自动下料", lambda: manager.auto_unload_trays_to_agv()), + ("提交分析任务", lambda: manager.run_analysis()), + ("谱图数据处理", lambda: manager.poll_analysis_run()), + ] + _run_workflow(steps, quick=True) + + elif choice == "3": + # 提交任务流程 + steps = [ + ("对齐化学品库", lambda: manager.align_chemicals_with_file(chem_db)), + ("上传任务到工站", lambda: _update_task_id(manager.create_task_by_file(task_tpl, chem_db))), + ("物料核算", lambda: manager.check_resource_for_task(task_tpl, chem_db)), + ] + _run_workflow(steps, quick=True) + + elif choice == "4": + # AGV执行流程 + task_tpl = _input_file_path("任务模板路径", DEFAULT_TASK_TPL) + chem_db = _input_file_path("化学品库路径", DEFAULT_CHEM_DB) + steps = [ + ("AGV上料", lambda: manager.batch_in_tray_with_agv_transfer()), + ("启动任务", lambda: manager.start_task()), + ("等待任务完成", lambda: manager.wait_task_with_ops()), + ("下料(任务物料+空托盘)", lambda: manager.batch_out_task_and_empty_trays()), + ("AGV自动下料", lambda: manager.auto_unload_trays_to_agv()), + ("谱图数据处理", lambda: manager.poll_analysis_run()), + ] + _run_workflow(steps, quick=True) + + elif choice == "5": + # 执行流程 + steps = [ + ("等待任务完成", lambda: manager.wait_task_with_ops()), + ("下料(任务物料+空托盘)", lambda: manager.batch_out_task_and_empty_trays()), + ("AGV自动下料", lambda: manager.auto_unload_trays_to_agv()), + ("谱图数据处理", lambda: manager.poll_analysis_run()), + ] + _run_workflow(steps, quick=True) + + else: + print("无效选择, 请重新输入") + + +# ===================== 2. 全流程分步进行 ===================== + +def _menu_step_by_step(manager): + """全流程分步进行子菜单""" + options = [ + ("1", "对齐化学品库"), + ("2", "上传任务到工站"), + ("3", "物料核算"), + ("4", "AGV上料"), + ("5", "二次物料核算"), + ("6", "启动任务"), + ("7", "等待任务完成"), + ("8", "下料(任务物料+空托盘)"), + ("9", "AGV自动下料"), + ("10", "提交分析任务"), + ("11", "谱图数据处理"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("全流程分步进行", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + chem_db = _input_file_path("化学品库路径", DEFAULT_CHEM_DB) + _safe_run(manager.align_chemicals_with_file, chem_db) + elif choice == "2": + task_tpl = _input_file_path("任务模板路径", DEFAULT_TASK_TPL) + chem_db = _input_file_path("化学品库路径", DEFAULT_CHEM_DB) + result = _safe_run(manager.create_task_by_file, task_tpl, chem_db) + _update_task_id(result) + elif choice == "3" or choice == "5": + task_tpl = _input_file_path("任务模板路径", DEFAULT_TASK_TPL) + chem_db = _input_file_path("化学品库路径", DEFAULT_CHEM_DB) + _safe_run(manager.check_resource_for_task, task_tpl, chem_db) + elif choice == "4": + _safe_run(manager.batch_in_tray_with_agv_transfer) + elif choice == "6": + tid = _input_task_id() + _safe_run(manager.start_task, tid) + elif choice == "7": + tid = _input_task_id() + _safe_run(manager.wait_task_with_ops, tid) + elif choice == "8": + tid = _input_task_id() + _safe_run(manager.batch_out_task_and_empty_trays, tid) + elif choice == "9": + _safe_run(manager.auto_unload_trays_to_agv) + elif choice == "10": + tid = _input_task_id() + _safe_run(manager.run_analysis, tid) + elif choice == "11": + tid = _input_task_id() + _safe_run(manager.poll_analysis_run, tid) + else: + print("无效选择, 请重新输入") + + +# ===================== 3. 工站状态查询 ===================== + +def _menu_status_query(manager): + """工站状态查询子菜单""" + options = [ + ("1", "查询站内物料信息"), + ("2", "查询设备状态"), + ("3", "查询工站运行状态"), + ("4", "查询手套箱环境"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("工站状态查询", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + result = _safe_run(manager.get_resource_info) + _print_result(result) + _pause() + elif choice == "2": + result = _safe_run(manager.list_device_status) + _print_result(result) + _pause() + elif choice == "3": + result = _safe_run(manager.station_state) + if result is not None: + label = _STATION_STATE_LABEL.get(result, f"未知({result})") + print(f"工站状态: {label} (代码: {result})") + _pause() + elif choice == "4": + result = _safe_run(manager.get_glovebox_env) + _print_result(result) + _pause() + else: + print("无效选择, 请重新输入") + + +# ===================== 4. 化学品库管理 ===================== + +def _menu_chemical_library(manager): + """化学品库管理子菜单""" + options = [ + ("1", "导出化学品到CSV"), + ("2", "从CSV导入化学品"), + ("3", "化学品库去重"), + ("4", "化学品库数据校验"), + ("5", "在线查询并添加化学品"), + ("6", "SMILES 在线查询并添加化学品"), + ("7", "配置溶液或beads并添加到化学品库"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("化学品库管理", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + path = _input_with_default("导出文件路径", "chemicals_list_export.csv") + _safe_run(manager.export_chemical_list_to_file, path) + elif choice == "2": + path = _input_with_default("导入CSV文件路径", "add_chemical_list.csv") + _safe_run(manager.sync_chemicals_from_file, path) + elif choice == "3": + path = _input_file_path("化学品库文件路径", DEFAULT_CHEM_DB) + _safe_run(manager.deduplicate_chemical_library_by_file, path) + elif choice == "4": + path = _input_file_path("化学品库文件路径", DEFAULT_CHEM_DB) + result = _safe_run(manager.check_chemical_library_by_file, path) + _print_result(result) + _pause() + elif choice == "5": + query = input("请输入 CAS 号或化合物中英文名称: ").strip() + if query: + result = _safe_run( + manager.lookup_and_append_chemical, query, str(DEFAULT_CHEM_DB), + ) + if result is None: + print("未查询到化合物信息或该化合物已存在, 请检查后重试") + else: + _print_chemical_append_summary(result, source_label="在线查询") + else: + print("输入不能为空") + _pause() + elif choice == "6": + smiles = input("请输入 SMILES 结构式: ").strip() + if smiles: + result = _safe_run( + manager.lookup_and_append_chemical_by_smiles, smiles, str(DEFAULT_CHEM_DB), + ) + if result is None: + print("未查询到 SMILES 对应的化合物信息或该化合物已存在, 请检查后重试") + else: + _print_chemical_append_summary(result, source_label="SMILES 查询") + else: + print("输入不能为空") + _pause() + elif choice == "7": + identifier = input("请输入 CAS 或 SMILES: ").strip() + if identifier == "": + print("输入不能为空") + _pause() + continue + + prepared_form = input("请选择派生形态(solution/beads): ").strip().lower() + if prepared_form not in {"solution", "beads"}: + print("派生形态仅支持 solution 或 beads") + _pause() + continue + + if prepared_form == "solution": + solvent_name = input("请输入溶剂名称: ").strip() + concentration_mol_l = _input_positive_float("请输入目标浓度(mol/L)") + target_volume_ml = _input_positive_float("请输入目标定容体积(mL)") + result = _safe_run( + manager.prepare_solution_or_beads, + identifier, + prepared_form, + solvent_name=solvent_name, + active_content=concentration_mol_l, + target_volume_ml=target_volume_ml, + excel_path=str(DEFAULT_CHEM_DB), + ) + else: + wt_percent = _input_positive_float("请输入载量(wt%)") + target_active_mmol = _input_positive_float("请输入目标活性(mmol)") + result = _safe_run( + manager.prepare_solution_or_beads, + identifier, + prepared_form, + active_content=wt_percent, + target_active_mmol=target_active_mmol, + excel_path=str(DEFAULT_CHEM_DB), + ) + + if result is None: + print("未完成派生条目添加, 请检查输入或确认该条目是否已存在") + else: + _print_prepared_chemical_summary(result) + _pause() + else: + print("无效选择, 请重新输入") + + +# ===================== 5. 任务管理 ===================== + +def _show_all_tasks(manager): + """ + 功能: + 格式化显示所有任务列表, 并更新 _last_task_id 缓存 + 参数: + manager: SynthesisStationManager 实例 + 返回: + None + """ + global _last_task_id + resp = manager.get_all_tasks() + # 兼容多种返回结构 + task_list = resp + if isinstance(resp, dict): + task_list = resp.get("task_list") or resp.get("result", {}).get("task_list", []) + if not task_list: + print("暂无任务") + return + + print(f"\n{'任务ID':<10} {'任务名称':<30} {'状态':<10}") + print("-" * 55) + for task in task_list: + tid = task.get("task_id", "?") + name = task.get("task_name", "未知") + status_code = task.get("status", -1) + status_text = _TASK_STATUS_LABEL.get(status_code, f"未知({status_code})") + print(f"{tid:<10} {name:<30} {status_text:<10}") + + print(f"\n共 {len(task_list)} 个任务") + + # 缓存最新的任务ID + if task_list: + latest = task_list[0].get("task_id") + if latest is not None: + _last_task_id = int(latest) + + +def _menu_task_management(manager): + """任务管理子菜单""" + options = [ + ("1", "查看所有任务"), + ("2", "查看任务详情"), + ("3", "停止任务"), + ("4", "取消任务"), + ("5", "删除任务"), + ("6", "导出任务报告"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("任务管理", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + _safe_run(_show_all_tasks, manager) + elif choice == "2": + tid = _input_task_id(allow_none=False) + if tid is not None: + result = _safe_run(manager.get_task_info, tid) + _print_result(result) + _pause() + elif choice == "3": + tid = _input_task_id(allow_none=False) + if tid is not None: + _safe_run(manager.stop_task, tid) + elif choice == "4": + tid = _input_task_id(allow_none=False) + if tid is not None: + _safe_run(manager.cancel_task, tid) + elif choice == "5": + tid = _input_task_id(allow_none=False) + if tid is not None: + confirm = input(f"确认删除任务 {tid}? (y/n) [默认: n]: ").strip().lower() + if confirm == "y": + _safe_run(manager.delete_task, tid) + else: + print("已取消删除") + elif choice == "6": + tid = _input_task_id(allow_none=False) + if tid is not None: + _safe_run(manager.export_task_report, tid) + else: + print("无效选择, 请重新输入") + + +# ===================== 6. 上料操作 ===================== + +def _menu_load(manager): + """上料操作子菜单""" + options = [ + ("1", "手动上料"), + ("2", "AGV上料"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("上料操作", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + template_in = _input_file_path("上料文件路径", DEFAULT_TEMPLATE_IN) + _safe_run(manager.batch_in_tray_by_file, template_in) + elif choice == "2": + _safe_run(manager.batch_in_tray_with_agv_transfer) + else: + print("无效选择, 请重新输入") + + +# ===================== 7. 下料操作 ===================== + +def _menu_unload(manager): + """下料操作子菜单""" + options = [ + ("1", "下料(任务物料+空托盘)"), + ("2", "下料(仅任务托盘)"), + ("3", "下料(任务+化学品托盘)"), + ("4", "下料(仅空托盘)"), + ("5", "AGV自动下料"), + ("6", "手动下料(指定layout)"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("下料操作", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + tid = _input_task_id() + _safe_run(manager.batch_out_task_and_empty_trays, tid) + elif choice == "2": + tid = _input_task_id() + _safe_run(manager.batch_out_task_trays, tid) + elif choice == "3": + tid = _input_task_id() + _safe_run(manager.batch_out_task_and_chemical_trays, tid) + elif choice == "4": + _safe_run(manager.batch_out_empty_trays) + elif choice == "5": + _safe_run(manager.auto_unload_trays_to_agv) + elif choice == "6": + # 手动下料: 逐条输入 layout_code 和 dst_layout_code + layout_list = [] + print("请逐条输入下料位置对(输入空行结束):") + while True: + src = input(" layout_code (源位置, 如 T-1-2): ").strip() + if src == "": + break + dst = input(" dst_layout_code (目标位置, 如 TB-2-2): ").strip() + if dst == "": + break + layout_list.append({"layout_code": src, "dst_layout_code": dst}) + if layout_list: + print(f"将下料 {len(layout_list)} 个位置:") + for item in layout_list: + print(f" {item['layout_code']} -> {item['dst_layout_code']}") + confirm = input("确认执行? (y/n) [默认: y]: ").strip().lower() + if confirm != "n": + _safe_run(manager.batch_out_tray, layout_list) + else: + print("未输入任何下料位置, 已取消") + else: + print("无效选择, 请重新输入") + + +# ===================== 8. 分析操作 ===================== + +def _menu_analysis(manager): + """分析操作子菜单""" + options = [ + ("1", "提交分析任务"), + ("2", "谱图数据处理"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("分析操作", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + tid = _input_task_id() + _safe_run(manager.run_analysis, tid) + elif choice == "2": + tid = _input_task_id() + _safe_run(manager.poll_analysis_run, tid) + else: + print("无效选择, 请重新输入") + + +# ===================== 9. 其它操作 ===================== + +def _menu_other(manager): + """其它操作子菜单""" + options = [ + ("1", "设备初始化"), + ("2", "控制过渡舱门"), + ("3", "控制W1货架"), + ("4", "异常通知监控"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("其它操作", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + _safe_run(manager.device_init) + elif choice == "2": + print(" 1. 打开过渡舱门") + print(" 2. 关闭过渡舱门") + op_choice = input("请选择: ").strip() + if op_choice == "1": + _safe_run(manager.open_close_door, "open") + elif op_choice == "2": + _safe_run(manager.open_close_door, "close") + else: + print("无效选择") + elif choice == "3": + print("货架位置:") + print(" 1. W-1-1 (控制 W-1-1 和 W-1-2)") + print(" 2. W-1-3 (控制 W-1-3 和 W-1-4)") + print(" 3. W-1-5 (控制 W-1-5 和 W-1-6)") + print(" 4. W-1-7 (控制 W-1-7 和 W-1-8)") + pos_choice = input("请选择货架位置: ").strip() + pos_map = {"1": "W-1-1", "2": "W-1-3", "3": "W-1-5", "4": "W-1-7"} + position = pos_map.get(pos_choice) + if position is None: + print("无效选择") + continue + + print("动作类型:") + print(" 1. 推出 (outside)") + print(" 2. 复位 (home)") + act_choice = input("请选择动作: ").strip() + act_map = {"1": "outside", "2": "home"} + action = act_map.get(act_choice) + if action is None: + print("无效选择") + continue + + _safe_run(manager.control_w1_shelf, position, action) + elif choice == "4": + _menu_notification(manager) + else: + print("无效选择, 请重新输入") + + +# ===================== 10. 异常通知监控 ===================== + +def _menu_notification(manager): + """异常通知监控子菜单""" + import datetime as _dt + + options = [ + ("1", "启动通知监控"), + ("2", "停止通知监控"), + ("3", "查看监控状态"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("异常通知监控", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + _safe_run(manager.start_notification_monitor) + elif choice == "2": + _safe_run(manager.stop_notification_monitor) + elif choice == "3": + status = _safe_run(manager.notification_monitor_status) + if status is not None: + running_text = "运行中" if status.get("running") else "已停止" + email_text = "可用" if status.get("email_available") else "未配置" + last_poll = status.get("last_poll_time") + if last_poll is not None: + last_poll_text = _dt.datetime.fromtimestamp(last_poll).strftime("%Y-%m-%d %H:%M:%S") + else: + last_poll_text = "无" + + print(f"\n 监控状态: {running_text}") + print(f" 邮件渠道: {email_text}") + print(f" 已处理通知数: {status.get('total_processed', 0)}") + print(f" 去重记录数: {status.get('processed_ids_count', 0)}") + print(f" 上次轮询: {last_poll_text}") + _pause() + else: + print("无效选择, 请重新输入") + + +# ===================== 10. 标签打印 ===================== + +def _menu_label_print(manager): + """标签打印子菜单""" + options = [ + ("1", "打印上料试剂标签"), + ("2", "打印任务编号标签"), + ("3", "交互式自由打印"), + ("0", "返回上级菜单"), + ] + + while True: + _print_menu("标签打印", options) + choice = input("请选择操作: ").strip() + + if choice == "0": + return + elif choice == "1": + # 从 batch_in_tray.xlsx 提取试剂名并打印 + _safe_run(manager.print_reagent_labels) + elif choice == "2": + # 输入任务ID, 打印反应管+样品编号标签 + tid = _input_task_id(allow_none=False) + if tid is not None: + _safe_run(manager.print_task_number_labels, tid) + elif choice == "3": + # 交互式自由打印 (复用 print_text.py 的 main 函数) + try: + from .printer.print_text import main as _print_text_main + _print_text_main() + except Exception as exc: + logger.error("交互式打印启动失败: %s", exc) + print(f"启动失败: {exc}") + _pause() + else: + print("无效选择, 请重新输入") + + +# ===================== 主入口 ===================== + +def interactive(): + """ + 功能: + 启动交互式命令行菜单, 作为 main.py 的默认入口 + 参数: + 无 + 返回: + 无 + """ + settings = Settings.from_env() + manager = SynthesisStationManager(settings) + + print("\n正在连接合成工站...") + print(f" 地址: {settings.base_url}") + print(f" 用户: {settings.username}") + + top_menu = [ + ("1", "快速工作流"), + ("2", "全流程分步进行"), + ("3", "工站状态查询"), + ("4", "化学品库管理"), + ("5", "任务管理"), + ("6", "上料操作"), + ("7", "下料操作"), + ("8", "分析操作"), + ("9", "其它操作"), + ("10", "标签打印"), + ("0", "退出"), + ] + + dispatch = { + "1": _menu_quick_workflow, + "2": _menu_step_by_step, + "3": _menu_status_query, + "4": _menu_chemical_library, + "5": _menu_task_management, + "6": _menu_load, + "7": _menu_unload, + "8": _menu_analysis, + "9": _menu_other, + "10": _menu_label_print, + } + + while True: + _print_menu("EIT 合成工站 交互控制台", top_menu) + choice = input("请选择操作: ").strip() + + if choice == "0" or choice.lower() == "q": + break + + handler = dispatch.get(choice) + if handler is None: + print("无效选择, 请重新输入") + continue + + handler(manager) + + +if __name__ == "__main__": + interactive() diff --git a/unilabos/devices/eit_synthesis_station/manager/__init__.py b/unilabos/devices/eit_synthesis_station/manager/__init__.py new file mode 100644 index 00000000..82f0dd2d --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/manager/__init__.py @@ -0,0 +1 @@ +from .station_manager import SynthesisStationManager \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/manager/station_manager.py b/unilabos/devices/eit_synthesis_station/manager/station_manager.py new file mode 100644 index 00000000..1eb23ec9 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/manager/station_manager.py @@ -0,0 +1,3997 @@ + # -*- coding: utf-8 -*- +import csv +import re +import logging +import shutil +import pandas as pd +import openpyxl +from datetime import datetime +from openpyxl import Workbook,load_workbook +from openpyxl.worksheet.datavalidation import DataValidation +from openpyxl.workbook.defined_name import DefinedName +from openpyxl.styles import Font, Alignment, NamedStyle +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 ( + CONSUMABLE_ALIAS_TO_CODE, + CONSUMABLE_CODE_DISPLAY_NAME, + CONSUMABLE_CODE_TO_TRAY_CODE, + ResourceCode, + TRAY_CODE_DISPLAY_NAME, + TraySpec, +) +from .synchronizer import EITSynthesisWorkstation + +from ..driver.exceptions import ValidationError,ApiError +from ..utils.file_utils import safe_excel_write, safe_workbook_save +from ..chem_tools.chemical_append_utils import ( + build_append_row_data, + build_append_row_data_for_smiles, + build_prepared_chemical_row_data, + build_duplicate_check_specs, + collect_missing_append_headers, + get_excel_write_value, + save_chemicalbook_record, +) + +logger = logging.getLogger("StationManager") + +JsonDict = Dict[str, Any] + +# 模块根目录, 用于构建相对路径 +MODULE_ROOT = Path(__file__).resolve().parent.parent + +class SynthesisStationManager(EITSynthesisWorkstation, SynthesisStationController): + """ + 功能: + 上层面向用户的管理器,继承自 SynthesisStationController。 + 负责处理 CSV/Excel 文件读取、生成模板,将文件内容转换为中间格式(List/Dict), + 然后调用父类方法执行具体的业务逻辑。 + """ + + def __init__( + self, + settings: Optional[Settings] = None, + config: Optional[Dict[str, Any]] = None, + deck: Optional[Any] = None, + **kwargs, + ): + settings = settings or Settings.from_env() + configure_logging(settings.log_level) + SynthesisStationController.__init__(self, settings) + EITSynthesisWorkstation.__init__( + self, + config=config, + deck=deck, + controller=self, + **kwargs, + ) + self._reset_task_queue_state() + + def _reset_task_queue_state(self) -> None: + """ + 功能: + 重置“先入队后执行”的任务缓存状态。 + """ + self._queued_layout_list: List[JsonDict] = [] + self._queued_task_name: str = "Queued_Auto_Task" + self._queued_task_setup: JsonDict = { + "subtype": None, + "experiment_num": 0, + "vessel": "551000502", + "added_slots": "", + } + self._queued_common_fields: JsonDict = { + "layout_code": "", + "src_layout_code": "", + "resource_type": "551000502", + "status": 0, + "tray_QR_code": "", + "QR_code": "", + } + self._workflow_context: JsonDict = { + "chemical_db": None, + "reaction_scale_mmol": 0.0, + "weighing_error_pct": 1.0, + "max_error_mg": 1.0, + "experiment_num": None, + } + self._workflow_row_counters: Dict[int, int] = {} + # begin_task 后启用:试剂/磁子先入队,add_task 时经 build_task_payload 与 Excel 路径一致编排 + self._workflow_deferred: bool = False + self._workflow_build_params: JsonDict = {} + self._workflow_reagent_queue: List[JsonDict] = [] + + def _configure_queued_task( + self, + *, + task_name: Optional[str] = None, + task_setup: Optional[JsonDict] = None, + common_fields: Optional[JsonDict] = None) -> JsonDict: + """ + 功能: + 配置入队任务的名称、task_setup 和 unit 公共字段。 + 参数: + task_name: 可选任务名称。 + task_setup: 可选 task_setup 覆盖字段。 + common_fields: 可选 unit 通用字段覆盖。 + 返回: + Dict, 当前队列配置摘要。 + """ + if task_name is not None and str(task_name).strip() != "": + self._queued_task_name = str(task_name).strip() + + if isinstance(task_setup, dict): + self._queued_task_setup.update(task_setup) + + if isinstance(common_fields, dict): + self._queued_common_fields.update(common_fields) + + return self._get_queued_task_status() + + def _get_queued_task_status(self) -> JsonDict: + """ + 功能: + 获取当前入队任务的状态摘要。 + 返回: + Dict, 包含任务名、unit 数量、实验列统计与配置。 + """ + if getattr(self, "_workflow_deferred", False): + return { + "task_name": self._queued_task_name, + "deferred_layout": True, + "queued_reagents": len(self._workflow_reagent_queue), + "workflow_build_params": dict(self._workflow_build_params), + "task_setup": self._queued_task_setup.copy(), + "common_fields": self._queued_common_fields.copy(), + } + + used_columns: set[int] = set() + used_rows: set[int] = set() + for unit in self._queued_layout_list: + col = unit.get("unit_column") + row = unit.get("unit_row") + if isinstance(col, int): + used_columns.add(col) + if isinstance(row, int): + used_rows.add(row) + + return { + "task_name": self._queued_task_name, + "queued_units": len(self._queued_layout_list), + "experiment_columns": sorted(used_columns), + "unit_rows": sorted(used_rows), + "task_setup": self._queued_task_setup.copy(), + "common_fields": self._queued_common_fields.copy(), + } + + def _preview_queued_task_payload( + self, + *, + experiment_num: Optional[int] = None, + is_audit_log: int = 1, + is_copy: bool = False) -> JsonDict: + """ + 功能: + 预览当前入队单元将生成的任务 payload(不提交)。 + 参数: + experiment_num: 可选实验数量,未传时按 unit_column 自动推断。 + is_audit_log: 审计日志标记,默认 1。 + is_copy: 复制标记,默认 False。 + 返回: + Dict, 可直接用于 add_task 的 payload。 + """ + if getattr(self, "_workflow_deferred", False): + return self._materialize_workflow_payload_from_excel_model( + is_audit_log=is_audit_log, + is_copy=is_copy, + ) + + if len(self._queued_layout_list) == 0: + raise ValidationError("当前队列为空,请先调用 begin_task 和 add_* 接口入队") + + inferred_exp_num = 0 + for unit in self._queued_layout_list: + col = unit.get("unit_column") + if isinstance(col, int): + inferred_exp_num = max(inferred_exp_num, col + 1) + + final_experiment_num = int(experiment_num) if experiment_num is not None else int(inferred_exp_num) + if final_experiment_num <= 0: + raise ValidationError("无法推断 experiment_num,请传入 experiment_num") + + task_setup = self._queued_task_setup.copy() + task_setup["experiment_num"] = final_experiment_num + + return { + "task_id": 0, + "task_name": self._queued_task_name, + "layout_list": [item.copy() for item in self._queued_layout_list], + "task_setup": task_setup, + "is_audit_log": int(is_audit_log), + "is_copy": bool(is_copy), + } + + def _execute_queued_task( + self, + *, + experiment_num: Optional[int] = None, + is_audit_log: int = 1, + is_copy: bool = False, + auto_start: bool = False, + check_glovebox_env: bool = True, + water_limit_ppm: float = 10.0, + oxygen_limit_ppm: float = 10.0, + clear_on_success: bool = True) -> JsonDict: + """ + 功能: + 将当前队列统一构建为 payload 并一次性提交到工站,可选自动启动任务。 + 参数: + experiment_num: 可选实验数量,未传时按 unit_column 自动推断。 + is_audit_log: 审计日志标记,默认 1。 + is_copy: 复制标记,默认 False。 + auto_start: 提交成功后是否自动启动。 + check_glovebox_env: 自动启动时是否校验手套箱环境。 + water_limit_ppm: 自动启动水含量阈值。 + oxygen_limit_ppm: 自动启动氧含量阈值。 + clear_on_success: 成功后是否自动清空队列。 + 返回: + Dict, 包含 payload、add_task 响应、task_id 以及可选 start_task 响应。 + """ + payload = self._preview_queued_task_payload( + experiment_num=experiment_num, + is_audit_log=is_audit_log, + is_copy=is_copy, + ) + + add_resp = self._submit_task_payload(payload) + task_id = add_resp.get("task_id") or add_resp.get("result", {}).get("task_id") or add_resp.get("data", {}).get("task_id") + + result: JsonDict = { + "task_id": task_id, + "add_task_response": add_resp, + "payload": payload, + } + + if auto_start is True: + if task_id is None: + raise ValidationError(f"任务创建成功但未解析到 task_id, add_resp={add_resp}") + start_resp = SynthesisStationController.start_task( + self, + int(task_id), + check_glovebox_env=check_glovebox_env, + water_limit_ppm=float(water_limit_ppm), + oxygen_limit_ppm=float(oxygen_limit_ppm), + ) + result["start_task_response"] = start_resp + + if clear_on_success is True: + self._reset_task_queue_state() + + return result + + def _submit_task_payload(self, payload: JsonDict) -> JsonDict: + """ + 功能: + 直接调用底层 AddTask 接口提交 payload。 + """ + logger.info("发送给工站的 AddTask payload: %s", payload) + return SynthesisStationController.add_task(self, payload) + + # ---------- 前端工作流接口 ---------- + def load_chemical_db_from_file(self, chemical_db_path: str) -> Dict[str, Dict[str, Any]]: + """ + 功能: + 从 Excel/CSV 化学品库文件加载化学品字典,供前端工作流接口复用。 + 参数: + chemical_db_path: 化学品库文件路径。 + 返回: + Dict[str, Dict[str, Any]], 以化学品名称为键的化学品信息表。 + """ + c_path = Path(chemical_db_path) + if c_path.exists() is False: + raise FileNotFoundError(f"未找到化学品库文件: {c_path}") + + 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: JsonDict, *keys: str, default: Any = None) -> Any: + for key in keys: + if key in row and pd.notna(row[key]): + return row[key] + 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 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(), + "density (g/mL)": _pick(row, "density (g/ml)", "density(g/ml)", "density_g_ml", "density", default=None), + "physical_form": str(_pick(row, "physical_form", default="") or "").strip().lower(), + "active_content": _pick(row, "active_content", "active_content(mmol/ml or wt%)", "active_content(mol/l or wt%)", default=""), + } + return chemical_db + + def _resolve_workflow_chemical_db( + self, + *, + chemical_db: Optional[Dict[str, Dict[str, Any]]] = None, + chemical_db_path: Optional[str] = None) -> Dict[str, Dict[str, Any]]: + if isinstance(chemical_db, dict): + return chemical_db + if chemical_db_path is not None and str(chemical_db_path).strip() != "": + return self.load_chemical_db_from_file(str(chemical_db_path)) + current_db = self._workflow_context.get("chemical_db") + if isinstance(current_db, dict): + return current_db + raise ValidationError("必须提供 chemical_db 或 chemical_db_path,或先调用 begin_task 设置化学品库") + + def _get_workflow_chemical_info( + self, + name: str, + *, + chemical_db: Optional[Dict[str, Dict[str, Any]]] = None, + chemical_db_path: Optional[str] = None) -> Tuple[Dict[str, Dict[str, Any]], Dict[str, Any]]: + name_text = str(name).strip() + if name_text == "": + raise ValidationError("化学品名称不能为空") + db = self._resolve_workflow_chemical_db(chemical_db=chemical_db, chemical_db_path=chemical_db_path) + if name_text not in db: + raise ValidationError(f"化学品库中未找到 '{name_text}'") + return db, db[name_text] + + def _experiment_to_col(self, experiment_no: int) -> int: + exp_no = int(experiment_no) + if exp_no <= 0: + raise ValidationError("experiment_no 必须为大于 0 的整数") + return exp_no - 1 + + def _allocate_workflow_row(self, experiment_no: int) -> int: + exp_no = int(experiment_no) + current_row = self._workflow_row_counters.get(exp_no, 0) + self._workflow_row_counters[exp_no] = current_row + 1 + return current_row + + def _default_experiment_no(self) -> int: + """ + 功能: + 前端编排式接口默认使用单实验模式,统一落在实验编号 1。 + """ + return 1 + + @staticmethod + def _format_reagent_amount_cell(amount: float, unit: str) -> str: + """将数值与单位拼成与 Excel 单元格相近的字符串,供 _split_amount_unit 解析。""" + u = str(unit).strip().replace("µ", "μ") + try: + x = float(amount) + if x == int(x): + num = str(int(x)) + else: + num = str(x).rstrip("0").rstrip(".") + except Exception: + num = str(amount) + return f"{num}{u}" + + def _build_headers_and_data_rows_from_reagent_queue(self) -> Tuple[List[str], List[List[Any]]]: + """ + 功能: + 将试剂/磁子入队记录还原为 build_task_payload 所需的表头与数据行(按实验编号连续展开行)。 + """ + per_exp: Dict[int, List[Tuple[str, str]]] = {} + for op in self._workflow_reagent_queue: + if op.get("op") == "reagent": + exp = int(op["experiment_no"]) + cell = self._format_reagent_amount_cell(float(op["amount"]), str(op["unit"])) + per_exp.setdefault(exp, []).append((str(op["name"]).strip(), cell)) + elif op.get("op") == "magnet": + exp = int(op["experiment_no"]) + per_exp.setdefault(exp, []).append(("加磁子", "")) + + if not per_exp: + raise ValidationError("请先使用 add_reagent / add_reagent_list / add_magnet 入队至少一条操作") + + max_exp_no = max(per_exp.keys()) + if max_exp_no < 1: + raise ValidationError("experiment_no 必须为从 1 开始的整数") + + num_slots = max(len(per_exp[e]) for e in per_exp) + if num_slots <= 0: + raise ValidationError("试剂列为空") + + headers: List[str] = ["实验编号"] + for i in range(1, num_slots + 1): + headers.append(f"试剂名称_{i}") + headers.append(f"试剂量_{i}") + + data_rows: List[List[Any]] = [] + for exp_no in range(1, max_exp_no + 1): + slots = per_exp.get(exp_no, []) + row: List[Any] = [str(exp_no)] + for idx in range(num_slots): + if idx < len(slots): + row.append(slots[idx][0]) + row.append(slots[idx][1]) + else: + row.append("") + row.append("") + data_rows.append(row) + + return headers, data_rows + + def _materialize_workflow_payload_from_excel_model( + self, + *, + is_audit_log: int = 1, + is_copy: bool = False, + ) -> JsonDict: + """ + 功能: + 使用与 create_task_by_file 相同的 build_task_payload 生成最终 AddTask 请求体。 + """ + headers, data_rows = self._build_headers_and_data_rows_from_reagent_queue() + db = self._workflow_context.get("chemical_db") + if not isinstance(db, dict): + raise ValidationError("化学品库未初始化,请先调用 begin_task") + + params = dict(self._workflow_build_params) + params.setdefault("实验名称", self._queued_task_name) + + payload = self.build_task_payload(params, headers, data_rows, db) + payload["is_audit_log"] = int(is_audit_log) + payload["is_copy"] = bool(is_copy) + return payload + + def _submit_workflow_payload_with_name_conflict_retry(self, task_payload: JsonDict) -> JsonDict: + """与 create_task_by_file 一致:任务名冲突(409)时自动加时间戳重试。""" + try: + return self._submit_task_payload(task_payload) + except ApiError as exc: + if getattr(exc, "code", None) != 409: + raise + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + task_name = task_payload.get("task_name") or self._workflow_build_params.get("实验名称") + new_task_name = f"{task_name}_{timestamp}" + task_payload["task_name"] = new_task_name + self._workflow_build_params["实验名称"] = new_task_name + logger.info("任务名称重复, 自动重命名为: %s", new_task_name) + try: + return self._submit_task_payload(task_payload) + except ApiError as retry_exc: + logger.error("重命名后任务提交仍失败: %s", retry_exc) + raise + + def _execute_deferred_workflow( + self, + *, + experiment_num: Optional[int] = None, + is_audit_log: int = 1, + is_copy: bool = False, + auto_start: bool = False, + check_glovebox_env: bool = True, + water_limit_ppm: float = 10.0, + oxygen_limit_ppm: float = 10.0, + clear_on_success: bool = True, + ) -> JsonDict: + payload = self._materialize_workflow_payload_from_excel_model( + is_audit_log=is_audit_log, + is_copy=is_copy, + ) + if experiment_num is not None: + payload.setdefault("task_setup", {})["experiment_num"] = int(experiment_num) + + add_resp = self._submit_workflow_payload_with_name_conflict_retry(payload) + task_id = add_resp.get("task_id") or add_resp.get("result", {}).get("task_id") or add_resp.get("data", {}).get("task_id") + + result: JsonDict = { + "task_id": task_id, + "add_task_response": add_resp, + "payload": payload, + } + + if auto_start is True: + if task_id is None: + raise ValidationError(f"任务创建成功但未解析到 task_id, add_resp={add_resp}") + start_resp = SynthesisStationController.start_task( + self, + int(task_id), + check_glovebox_env=check_glovebox_env, + water_limit_ppm=float(water_limit_ppm), + oxygen_limit_ppm=float(oxygen_limit_ppm), + ) + result["start_task_response"] = start_resp + + if clear_on_success is True: + self._reset_task_queue_state() + + return result + + def begin_task( + self, + *, + task_name: str = "Auto_task", + task_id: int = 0, + chemical_db: Optional[Dict[str, Dict[str, Any]]] = None, + chemical_db_path: Optional[str] = None, + fixed_addition_order: bool = False, + auto_magnet: bool = True, + weighing_error_pct: float = 3.0, + max_error_mg: float = 1.0) -> JsonDict: + """ + 功能: + 初始化编排式任务上下文。参数与 Excel 模板左侧"实验设定 / 称量设定 / 加料设定"一一对应。 + 参数: + task_name: 实验名称。 + task_id: 实验ID(默认 0 表示新建)。 + chemical_db/chemical_db_path: 化学品库对象或路径,至少提供一个。 + fixed_addition_order: 固定加料顺序(对应 Excel "是/否"),默认 False。 + auto_magnet: 自动加磁子(对应 Excel "是/否"),默认 True。 + weighing_error_pct: 称量误差(%)。 + max_error_mg: 最大称量误差(mg)。 + 返回: + Dict, 当前任务上下文摘要。 + """ + self._reset_task_queue_state() + db = self._resolve_workflow_chemical_db(chemical_db=chemical_db, chemical_db_path=chemical_db_path) + task_setup: JsonDict = { + "subtype": None, + "vessel": "551000502", + "added_slots": "", + } + self._configure_queued_task( + task_name=str(task_name).strip(), + task_setup=task_setup, + ) + self._workflow_context.update({ + "chemical_db": db, + "reaction_scale_mmol": 0.0, + "weighing_error_pct": float(weighing_error_pct), + "max_error_mg": float(max_error_mg), + "experiment_num": None, + }) + self._workflow_row_counters = {} + self._workflow_deferred = True + self._workflow_reagent_queue = [] + self._workflow_build_params = { + "实验名称": str(task_name).strip(), + "实验ID": int(task_id), + "固定加料顺序": "是" if fixed_addition_order else "否", + "自动加磁子": "是" if auto_magnet else "否", + "称量误差(%)": float(weighing_error_pct), + "最大称量误差(mg)": float(max_error_mg), + } + return self._get_queued_task_status() + + def add_reagent( + self, + *, + name: str, + amount: float, + unit: str, + experiment_no: Optional[int] = None) -> JsonDict: + """ + 功能: + 以 Excel 里的“试剂 / 试剂量”语义入队(默认实验编号 1);在 add_task 时与 create_task_by_file 相同经 build_task_payload 编排。 + 参数: + name: 试剂名称。 + amount: 用量数值。 + unit: 用量单位,如 eq/mmol/mg/g/μL/mL。 + experiment_no: 实验编号(从 1 开始);缺省为 1。 + 化学品库来源: 统一使用 begin_task 设置的 chemical_db/chemical_db_path。 + 返回: + Dict, 当前入队试剂/磁子条数与实验编号。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + self._get_workflow_chemical_info(name) + exp_no = int(experiment_no) if experiment_no is not None else self._default_experiment_no() + self._workflow_reagent_queue.append({ + "op": "reagent", + "experiment_no": exp_no, + "name": str(name).strip(), + "amount": float(amount), + "unit": str(unit).strip(), + }) + return { + "queued_reagents": len(self._workflow_reagent_queue), + "experiment_no": exp_no, + } + + def add_reagent_list( + self, + *, + experiments: Optional[List[JsonDict]] = None, + formulation: Optional[List[JsonDict]] = None, + ) -> JsonDict: + """ + 功能: + 一次入队多个实验编号的配方(顺序与 Excel 中各实验试剂从左到右一致)。 + 兼容前端批量配方输入(formulation/materials/liquids)风格。 + 参数: + experiments: 每项形如 {"experiment_no": 1, "reagents": [{"name": "...", "amount": 1.0, "unit": "mmol"}, ...]}。 + formulation: 前端配方列表,每项形如 + {"experiment_no": 1, "materials": [{"name": "...", "mass": 1.0, "unit": "g"}]} + 或 {"liquids": [{"name": "...", "volume": 50, "unit": "uL"}]}。 + 返回: + Dict, 当前入队试剂/磁子总条数。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + + normalized_experiments: List[JsonDict] = [] + + for idx, block in enumerate(experiments or [], start=1): + exp_no = int(block.get("experiment_no", idx)) + normalized_experiments.append({ + "experiment_no": exp_no, + "reagents": block.get("reagents") or [], + }) + + for idx, item in enumerate(formulation or [], start=1): + exp_no = int(item.get("experiment_no", item.get("experimentNo", idx))) + reagent_list = item.get("reagents") or item.get("materials") or item.get("liquids") or [] + normalized_reagents: List[JsonDict] = [] + for reagent in reagent_list: + amount_value = reagent.get("amount") + if amount_value is None: + amount_value = reagent.get("mass", reagent.get("volume")) + normalized_reagents.append({ + "name": reagent.get("name"), + "amount": amount_value, + "unit": reagent.get("unit", "g"), + }) + normalized_experiments.append({"experiment_no": exp_no, "reagents": normalized_reagents}) + + if not normalized_experiments: + raise ValidationError("add_reagent_list 至少需要 experiments 或 formulation 其中一种输入") + + logger.info("add_reagent_list 归一化结果: %s", normalized_experiments) + + for block in normalized_experiments: + exp_no = int(block["experiment_no"]) + for r in block.get("reagents") or []: + reagent_name = str(r.get("name", "")).strip() + if not reagent_name: + raise ValidationError("add_reagent_list 的试剂 name 不能为空") + amount_value = r.get("amount") + if amount_value is None: + raise ValidationError(f"试剂 {reagent_name} 缺少 amount/mass/volume 字段") + reagent_unit = str(r.get("unit", "g")).strip() or "g" + self._get_workflow_chemical_info(reagent_name) + self._workflow_reagent_queue.append({ + "op": "reagent", + "experiment_no": exp_no, + "name": reagent_name, + "amount": float(amount_value), + "unit": reagent_unit, + }) + return {"queued_reagents": len(self._workflow_reagent_queue)} + + def add_magnet(self, *, experiment_no: Optional[int] = None) -> JsonDict: + """ + 功能: + 以“加磁子”语义入队(默认实验编号 1);编排规则与 Excel 一致。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + exp_no = int(experiment_no) if experiment_no is not None else self._default_experiment_no() + self._workflow_reagent_queue.append({"op": "magnet", "experiment_no": exp_no}) + return {"queued_reagents": len(self._workflow_reagent_queue), "experiment_no": exp_no} + + def add_reaction( + self, + *, + reaction_scale_mmol: float = 0.2, + reactor_type: str = "heat", + reaction_time_h: float = 8.0, + reaction_temp_c: Optional[float] = 40.0, + stir_speed_rpm: int = 500, + target_temp_c: Optional[float] = None, + wait_target_temp: bool = False) -> JsonDict: + """ + 功能: + 设置 Excel 模板中"反应设定"区域的全部参数。 + 参数: + reaction_scale_mmol: 反应规模(mmol)。 + reactor_type: 反应器类型(如 "heat")。 + reaction_time_h: 反应时间,单位小时(写入 build_task_payload 时转为 "8h" 格式)。 + reaction_temp_c: 反应温度(°C)。 + stir_speed_rpm: 转速(rpm)。 + target_temp_c: 搅拌后目标温度(°C);None 表示不设置。 + wait_target_temp: 等待目标温度(对应 Excel "是/否")。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + self._workflow_build_params["反应规模(mmol)"] = float(reaction_scale_mmol) + self._workflow_context["reaction_scale_mmol"] = float(reaction_scale_mmol) + self._workflow_build_params["反应器类型"] = str(reactor_type).strip() + self._workflow_build_params["反应时间(min/h)"] = f"{float(reaction_time_h)}h" + self._workflow_build_params["转速(rpm)"] = int(stir_speed_rpm) + self._workflow_build_params["等待目标温度"] = "是" if wait_target_temp else "否" + if reaction_temp_c is not None: + self._workflow_build_params["反应温度(°C)"] = float(reaction_temp_c) + else: + self._workflow_build_params.pop("反应温度(°C)", None) + if target_temp_c is not None: + self._workflow_build_params["搅拌后目标温度(°C)"] = float(target_temp_c) + else: + self._workflow_build_params.pop("搅拌后目标温度(°C)", None) + return {"workflow_build_params": dict(self._workflow_build_params)} + + def add_internal_standard( + self, + *, + name: str, + amount_ul_or_mg: float) -> JsonDict: + """ + 功能: + 设置 Excel 模板中"内标设定"的内标种类与内标用量(μL/mg)。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + self._get_workflow_chemical_info(name) + self._workflow_build_params["内标种类"] = str(name).strip() + self._workflow_build_params["内标用量(μL/mg)"] = float(amount_ul_or_mg) + return {"workflow_build_params": dict(self._workflow_build_params)} + + def add_stir( + self, + *, + time_min: float) -> JsonDict: + """ + 功能: + 设置 Excel 模板中"加入内标后搅拌时间(min)"。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + self._workflow_build_params["加入内标后搅拌时间(min)"] = float(time_min) + return {"workflow_build_params": dict(self._workflow_build_params)} + + def add_dilution( + self, + *, + diluent_name: str, + dilution_volume_ul: float) -> JsonDict: + """ + 功能: + 设置 Excel 模板中"稀释设定"(稀释液种类、稀释量(μL))。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + self._get_workflow_chemical_info(diluent_name) + self._workflow_build_params["稀释液种类"] = str(diluent_name).strip() + self._workflow_build_params["稀释量(μL)"] = float(dilution_volume_ul) + return {"workflow_build_params": dict(self._workflow_build_params)} + + def add_filter( + self, + *, + filter_liquid_name: str, + filter_volume_ul: float, + sampling_volume_ul: float, + filter_experiment_numbers: str = "全部") -> JsonDict: + """ + 功能: + 设置 Excel 模板中"闪滤设定"(闪滤液种类、闪滤液用量(μL)、取样量(μL)、闪滤实验编号)。 + 参数: + filter_liquid_name: 闪滤液种类。 + filter_volume_ul: 闪滤液用量(μL)。 + sampling_volume_ul: 取样量(μL)。 + filter_experiment_numbers: 闪滤实验编号,"全部" 或 "1-12,24,28" 格式。 + """ + if getattr(self, "_workflow_deferred", False) is False: + raise ValidationError("请先调用 begin_task 初始化编排上下文") + self._get_workflow_chemical_info(filter_liquid_name) + self._workflow_build_params["闪滤液种类"] = str(filter_liquid_name).strip() + self._workflow_build_params["闪滤液用量(μL)"] = float(filter_volume_ul) + self._workflow_build_params["取样量(μL)"] = float(sampling_volume_ul) + self._workflow_build_params["闪滤实验编号"] = str(filter_experiment_numbers).strip() + return {"workflow_build_params": dict(self._workflow_build_params)} + + def add_task( + self, + *, + payload: Optional[JsonDict] = None) -> JsonDict: + """ + 功能: + 作为前端工作流的“添加任务”节点,仅执行 add_task,不做 start_task。 + 兼容旧接口:若传入 payload,则直连底层 AddTask 提交。 + 参数: + payload: 可选。传入时直接调用底层 AddTask。 + 返回: + Dict, 包含 payload、add_task 响应与 task_id。 + """ + if payload is not None: + return self._submit_task_payload(payload) + + if getattr(self, "_workflow_deferred", False): + experiment_num = self._workflow_context.get("experiment_num") + return self._execute_deferred_workflow( + experiment_num=int(experiment_num) if experiment_num is not None else None, + auto_start=False, + clear_on_success=True, + ) + + experiment_num = self._workflow_context.get("experiment_num") + return self._execute_queued_task( + experiment_num=int(experiment_num) if experiment_num is not None else None, + auto_start=False, + clear_on_success=True, + ) + + def execute_task(self, task_id: int | None = None): + return super().start_task(task_id, check_glovebox_env=True, water_limit_ppm=10.0, oxygen_limit_ppm=10.0) + + def login(self) -> tuple: + """ + 功能: + 登录并缓存 token, 登录成功后根据配置自动启动异常通知监控. + 参数: + 无. + 返回: + Tuple[str, str], (token_type, access_token). + """ + result = super().login() + # 登录成功后自动启动异常通知邮件监控 (CLI 启动路径) + try: + if self._settings.notification.enabled: + self.start_notification_monitor() + logger.info("异常通知邮件监控已启动") + except Exception as e: + logger.warning("异常通知监控启动失败, 不影响主流程: %s", e) + return result + + def _read_table_file_with_required_columns( + self, + path: Path, + *, + required_columns: Optional[List[str]] = None, + preferred_sheet_name: Optional[str] = None) -> pd.DataFrame: + """ + 功能: + 读取 CSV/Excel 文件. + 当为 Excel 且存在多工作表时, 根据必需列选择工作表. + 参数: + path: 文件路径. + required_columns: 必需列名列表, None 表示直接读取默认表. + preferred_sheet_name: 优先工作表名. + 返回: + DataFrame, 读取后的表格数据. + """ + if path.suffix.lower() not in [".xlsx", ".xls"]: + return pd.read_csv(path) + + if required_columns is None: + return pd.read_excel(path) + + required = {str(col).strip().lower() for col in required_columns} + all_sheets = pd.read_excel(path, sheet_name=None) + + candidate_sheet_names: List[str] = [] + if preferred_sheet_name is not None and preferred_sheet_name in all_sheets: + candidate_sheet_names.append(preferred_sheet_name) + for sheet_name in all_sheets.keys(): + if sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(sheet_name) + + matched_sheet_names: List[str] = [] + for sheet_name in candidate_sheet_names: + df = all_sheets[sheet_name] + normalized_columns = {str(col).strip().lower() for col in df.columns} + if required.issubset(normalized_columns): + matched_sheet_names.append(sheet_name) + + if len(matched_sheet_names) > 0: + selected_sheet_name = matched_sheet_names[0] + if len(matched_sheet_names) > 1: + logger.warning( + "Excel命中多个候选工作表, 将按优先顺序使用 [%s], 其余候选: %s, 文件: %s", + selected_sheet_name, + matched_sheet_names[1:], + path, + ) + logger.info("Excel解析使用工作表: %s, 文件: %s", selected_sheet_name, path) + return all_sheets[selected_sheet_name] + + fallback_sheet_name = next(iter(all_sheets.keys())) + logger.warning( + "Excel未命中必需列%s, 回退到第一张工作表: %s, 文件: %s", + sorted(required), + fallback_sheet_name, + path, + ) + return all_sheets[fallback_sheet_name] + + # ---------- 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: + safe_excel_write(out_df, 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) + else: + cell.font = Font(name="微软雅黑") + + 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 + + safe_workbook_save(wb, 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: + safe_excel_write(new_df, path, index=False) + self._beautify_excel_database(path) # 保存后再美化 + + logger.info(f"化学品对齐完成并回写文件: {path}") + + @staticmethod + def _has_any_append_core_value(row_data: Dict[str, Any]) -> bool: + """ + 功能: + 判断追加行是否至少包含一个可用于识别化合物的核心字段. + 参数: + row_data: Dict[str, Any], 准备写入 Excel 的行数据. + 返回: + bool, True 表示至少包含 CAS, 英文名, 中文名中的一个. + """ + return any([ + str(row_data.get("cas_number") or "").strip() != "", + str(row_data.get("substance_english_name") or "").strip() != "", + str(row_data.get("substance") or "").strip() != "", + ]) + + @staticmethod + def _format_append_row_summary(row_data: Dict[str, Any]) -> str: + """ + 功能: + 提取 chemical list 关键字段, 生成统一的日志摘要文本. + 参数: + row_data: Dict[str, Any], 追加到 chemical list 的行数据. + 返回: + str, 包含 substance, physical_state, physical_form 的摘要文本. + """ + substance = str( + row_data.get("substance") + or row_data.get("substance_chinese_name") + or "" + ).strip() + physical_state = str(row_data.get("physical_state") or "").strip() + physical_form = str(row_data.get("physical_form") or "").strip() + return ( + f"substance={substance}, " + f"physical_state={physical_state}, " + f"physical_form={physical_form}" + ) + + def _resolve_append_excel_path(self, excel_path: Optional[str]) -> Path: + """ + 功能: + 解析化学品追加流程使用的目标 Excel 路径. + 参数: + excel_path: Optional[str], 用户指定路径, None 时使用默认库文件. + 返回: + Path, 已解析的 Excel 路径. + 异常: + FileNotFoundError: 目标文件不存在时抛出. + """ + path = Path(excel_path) if excel_path is not None else MODULE_ROOT / "sheet" / "chemical_list.xlsx" + if path.exists() is False: + raise FileNotFoundError(f"化学品库文件不存在: {path}") + return path + + @staticmethod + def _build_append_header_map(worksheet: Any) -> Dict[str, int]: + """ + 功能: + 从化学品库工作表首行构建表头到列号的映射. + 参数: + worksheet: Any, openpyxl 工作表对象. + 返回: + Dict[str, int], 表头名称到列号的映射. + """ + header_map: Dict[str, int] = {} + for col_idx in range(1, worksheet.max_column + 1): + header_val = worksheet.cell(row=1, column=col_idx).value + if header_val is None: + continue + header_map[str(header_val).strip()] = col_idx + return header_map + + @staticmethod + def _build_append_row_snapshot( + worksheet: Any, + header_map: Dict[str, int], + row_index: int) -> Dict[str, Any]: + """ + 功能: + 从化学品库工作表中提取单行数据快照, 统一补齐常用别名字段. + 参数: + worksheet: Any, openpyxl 工作表对象. + header_map: Dict[str, int], 表头名称到列号映射. + row_index: int, 目标行号. + 返回: + Dict[str, Any], 单行字段字典. + """ + row_data: Dict[str, Any] = {} + for column_name, column_index in header_map.items(): + row_data[column_name] = worksheet.cell(row=row_index, column=column_index).value + + substance_value = str(row_data.get("substance") or "").strip() + chinese_name_value = str(row_data.get("substance_chinese_name") or "").strip() + if substance_value == "" and chinese_name_value != "": + row_data["substance"] = chinese_name_value + if chinese_name_value == "" and substance_value != "": + row_data["substance_chinese_name"] = substance_value + base_substance = row_data.get("substance") or row_data.get("substance_chinese_name") or "" + physical_form = str(row_data.get("physical_form") or "").strip().lower() + if physical_form in {"solution", "beads"} and " (" in str(base_substance): + base_substance = str(base_substance).split(" (", 1)[0].strip() + row_data["base_substance"] = base_substance + return row_data + + def _find_existing_chemical_row( + self, + *, + excel_path: Optional[str] = None, + cas_number: str = "", + substance_english_name: str = "", + substance: str = "") -> Optional[Dict[str, Any]]: + """ + 功能: + 按 CAS, 英文名, 中文名顺序在化学品库中查找已有条目, 优先返回 neat 行. + 参数: + excel_path: Optional[str], 化学品库文件路径. + cas_number: str, 候选 CAS 号. + substance_english_name: str, 候选英文名. + substance: str, 候选中文名或展示名. + 返回: + Optional[Dict[str, Any]], 命中时返回包含 row_data 与 row_index 的结果字典. + """ + path = self._resolve_append_excel_path(excel_path) + wb = load_workbook(path, data_only=True) + try: + ws = wb.active + header_map = self._build_append_header_map(ws) + lookup_specs = [] + normalized_cas = str(cas_number or "").strip() + normalized_english_name = str(substance_english_name or "").strip() + normalized_substance = str(substance or "").strip() + if normalized_cas != "": + lookup_specs.append((("cas_number",), normalized_cas)) + if normalized_english_name != "": + lookup_specs.append((("substance_english_name",), normalized_english_name)) + if normalized_substance != "": + lookup_specs.append((("substance", "substance_chinese_name"), normalized_substance)) + + for candidate_columns, target_value in lookup_specs: + matched_rows: List[Dict[str, Any]] = [] + for row_idx in range(2, ws.max_row + 1): + for candidate_column in candidate_columns: + if candidate_column not in header_map: + continue + existing_value = str( + ws.cell(row=row_idx, column=header_map[candidate_column]).value or "" + ).strip() + if existing_value != target_value: + continue + row_data = self._build_append_row_snapshot(ws, header_map, row_idx) + matched_rows.append({ + "row_index": row_idx, + "row_data": row_data, + }) + break + + if len(matched_rows) == 0: + continue + + neat_rows = [ + row_item + for row_item in matched_rows + if str(row_item["row_data"].get("physical_form") or "").strip().lower() == "neat" + ] + if len(neat_rows) > 0: + return neat_rows[0] + return matched_rows[0] + finally: + wb.close() + + return None + + @staticmethod + def _parse_positive_float(value: Any, field_name: str) -> float: + """ + 功能: + 将输入值解析为大于 0 的浮点数. + 参数: + value: Any, 待解析的数值. + field_name: str, 字段中文名, 用于异常提示. + 返回: + float, 解析后的正数. + 异常: + ValidationError: 字段为空, 非数字或不大于 0 时抛出. + """ + text_value = str(value or "").strip() + if text_value == "": + raise ValidationError(f"{field_name}不能为空") + + try: + numeric_value = float(text_value) + except (TypeError, ValueError) as exc: + raise ValidationError(f"{field_name}必须为数字") from exc + + if numeric_value <= 0: + raise ValidationError(f"{field_name}必须大于0") + return numeric_value + + @staticmethod + def _format_preparation_number(value: float) -> str: + """ + 功能: + 将配液或称量结果格式化为紧凑展示文本. + 参数: + value: float, 原始数值. + 返回: + str, 去除多余尾零后的文本. + """ + return format(float(value), ".6g") + + def _build_solution_recipe( + self, + base_row_data: Dict[str, Any], + concentration_mol_l: float, + target_volume_ml: float, + solvent_name: str) -> Dict[str, Any]: + """ + 功能: + 根据母体化合物信息生成溶液配置结果. + 参数: + base_row_data: Dict[str, Any], 母体化合物行数据. + concentration_mol_l: float, 目标浓度, 单位 mol/L. + target_volume_ml: float, 目标定容体积, 单位 mL. + solvent_name: str, 溶剂名称. + 返回: + Dict[str, Any], 包含质量, 体积与展示文案的结果字典. + 异常: + ValidationError: 缺少分子量时抛出. + """ + molecular_weight = self._parse_positive_float( + base_row_data.get("molecular_weight"), + "母体化合物分子量", + ) + normalized_solvent_name = str(solvent_name or "").strip() + if normalized_solvent_name == "": + raise ValidationError("溶剂名称不能为空") + + solute_moles = concentration_mol_l * target_volume_ml / 1000.0 + solute_mass_g = solute_moles * molecular_weight + instruction_text = ( + f"称取/加入溶质 {self._format_preparation_number(solute_mass_g)} g, " + f"用 {normalized_solvent_name} 溶解后定容至 " + f"{self._format_preparation_number(target_volume_ml)} mL" + ) + + solute_volume_ml = None + density_text = str(base_row_data.get("density (g/mL)") or "").strip() + physical_state = str(base_row_data.get("physical_state") or "").strip().lower() + if physical_state == "liquid" and density_text != "": + try: + density_value = self._parse_positive_float(density_text, "母体化合物密度") + solute_volume_ml = solute_mass_g / density_value + except ValidationError: + logger.warning("母体液体密度无效, 跳过溶质量取体积估算: density=%s", density_text) + + return { + "prepared_form": "solution", + "solute_moles": solute_moles, + "solute_mass_g": solute_mass_g, + "solute_volume_ml": solute_volume_ml, + "target_volume_ml": target_volume_ml, + "active_content": concentration_mol_l, + "solvent_name": normalized_solvent_name, + "instruction_text": instruction_text, + } + + def _build_beads_recipe( + self, + base_row_data: Dict[str, Any], + wt_percent: float, + target_active_mmol: float) -> Dict[str, Any]: + """ + 功能: + 根据母体化合物信息生成 beads 称量结果. + 参数: + base_row_data: Dict[str, Any], 母体化合物行数据. + wt_percent: float, 有效成分质量分数. + target_active_mmol: float, 目标活性摩尔数, 单位 mmol. + 返回: + Dict[str, Any], 包含 beads 质量与展示文案的结果字典. + 异常: + ValidationError: 缺少分子量时抛出. + """ + molecular_weight = self._parse_positive_float( + base_row_data.get("molecular_weight"), + "母体化合物分子量", + ) + active_mass_g = target_active_mmol / 1000.0 * molecular_weight + beads_mass_g = active_mass_g / (wt_percent / 100.0) + instruction_text = ( + f"称取 beads {self._format_preparation_number(beads_mass_g)} g, " + f"其中有效成分约为 {self._format_preparation_number(target_active_mmol)} mmol" + ) + return { + "prepared_form": "beads", + "active_mass_g": active_mass_g, + "beads_mass_g": beads_mass_g, + "target_active_mmol": target_active_mmol, + "active_content": wt_percent, + "instruction_text": instruction_text, + } + + def _resolve_prepared_base_chemical( + self, + identifier: str, + excel_path: Optional[str] = None) -> Optional[Dict[str, Any]]: + """ + 功能: + 为溶液或 beads 配置流程解析母体化合物, 不存在时自动补录 neat 条目. + 参数: + identifier: str, CAS 或 SMILES. + excel_path: Optional[str], 化学品库文件路径. + 返回: + Optional[Dict[str, Any]], 成功时返回母体行数据与来源信息, 失败时返回 None. + """ + from ..chem_tools.chemical_lookup import is_cas_number, lookup_chemical_by_smiles + + normalized_identifier = str(identifier or "").strip() + if normalized_identifier == "": + raise ValidationError("CAS 或 SMILES 不能为空") + + if is_cas_number(normalized_identifier) is True: + existing_result = self._find_existing_chemical_row( + excel_path=excel_path, + cas_number=normalized_identifier, + ) + if existing_result is not None: + existing_result["base_created"] = False + existing_result["chemicalbook_status"] = "" + existing_result["chemicalbook_record_path"] = "" + return existing_result + + append_result = self.lookup_and_append_chemical(normalized_identifier, excel_path) + if append_result is None: + return None + append_result["base_created"] = True + return append_result + + lookup_info = lookup_chemical_by_smiles(normalized_identifier) + if lookup_info is None: + return None + + existing_result = self._find_existing_chemical_row( + excel_path=excel_path, + cas_number=str(getattr(lookup_info, "cas_number", "") or "").strip(), + substance_english_name=str(getattr(lookup_info, "substance_english_name", "") or "").strip(), + substance=str(getattr(lookup_info, "substance", "") or "").strip(), + ) + if existing_result is not None: + existing_result["base_created"] = False + existing_result["chemicalbook_status"] = "" + existing_result["chemicalbook_record_path"] = "" + return existing_result + + append_result = self.lookup_and_append_chemical_by_smiles(normalized_identifier, excel_path) + if append_result is None: + return None + append_result["base_created"] = True + return append_result + + @staticmethod + def _find_duplicate_append_row( + worksheet: Any, + header_map: Dict[str, int], + row_data: Dict[str, Any], + ) -> Optional[Tuple[str, str, str, int]]: + """ + 功能: + 按约定优先级在 Excel 中查找重复化合物. + 参数: + worksheet: Any, openpyxl 工作表对象. + header_map: Dict[str, int], 表头名称到列号映射. + row_data: Dict[str, Any], 准备写入的行数据. + 返回: + Optional[Tuple[str, str, str, int]], 命中时返回 + (字段标签, 目标值, 表头名, 行号), 否则返回 None. + """ + duplicate_specs = build_duplicate_check_specs(row_data) + for candidate_columns, target_value, label_text in duplicate_specs: + matched_column_name = None + matched_column_index = None + for candidate_column in candidate_columns: + if candidate_column in header_map: + matched_column_name = candidate_column + matched_column_index = header_map[candidate_column] + break + + if matched_column_index is None: + continue + + for row_idx in range(2, worksheet.max_row + 1): + existing_value = str(worksheet.cell(row=row_idx, column=matched_column_index).value or "").strip() + if existing_value == target_value: + return label_text, target_value, str(matched_column_name), row_idx + return None + + def _append_chemical_row_to_excel( + self, + row_data: Dict[str, Any], + excel_path: Optional[str] = None, + ) -> Optional[int]: + """ + 功能: + 将单条化学品行数据追加到 Excel 末尾, 并执行重复检查. + 参数: + row_data: Dict[str, Any], 准备写入的行数据. + excel_path: Optional[str], 目标 Excel 文件路径. + 返回: + Optional[int], 成功时返回新行行号, 命中重复时返回 None. + """ + path = self._resolve_append_excel_path(excel_path) + + wb = load_workbook(path) + try: + ws = wb.active + header_map = self._build_append_header_map(ws) + + missing_headers = collect_missing_append_headers(header_map) + if len(missing_headers) > 0: + logger.warning("化学品追加时发现缺失表头: %s", ", ".join(missing_headers)) + + duplicate_result = self._find_duplicate_append_row(ws, header_map, row_data) + if duplicate_result is not None: + label_text, target_value, column_name, row_idx = duplicate_result + summary_text = self._format_append_row_summary(row_data) + logger.warning( + "化合物已存在, %s=%s, 表头=%s, 行号=%d, %s, 跳过添加", + label_text, + target_value, + column_name, + row_idx, + summary_text, + ) + return None + + # 仅写入有值字段, 并设置与已有数据行一致的字体和对齐格式. + data_font = Font(name="微软雅黑") + data_alignment = Alignment(horizontal="center", vertical="center") + + new_row = ws.max_row + 1 + for column_name, column_index in header_map.items(): + value = get_excel_write_value(row_data, column_name) + if value is None: + continue + if isinstance(value, str) is True and value == "": + continue + cell = ws.cell(row=new_row, column=column_index, value=value) + cell.font = data_font + cell.alignment = data_alignment + + safe_workbook_save(wb, path) + return new_row + finally: + wb.close() + + def _fetch_chemicalbook_append_artifacts( + self, + resolved_cas: str, + ) -> Tuple[Optional[Dict[str, Any]], str, str]: + """ + 功能: + 根据已解析的 CAS 获取 ChemicalBook 结构化结果及 sidecar 路径. + 参数: + resolved_cas: str, 已解析出的 CAS 号. + 返回: + Tuple[Optional[Dict[str, Any]], str, str], 依次为 + chemicalbook_record, chemicalbook_status, chemicalbook_record_path. + """ + from ..chem_tools.chemicalbook_scraper import fetch_chemicalbook_by_cas + + normalized_cas = str(resolved_cas or "").strip() + if normalized_cas == "": + return None, "", "" + + chemicalbook_record = None + chemicalbook_status = "" + chemicalbook_record_path = "" + try: + chemicalbook_record = fetch_chemicalbook_by_cas(normalized_cas) + chemicalbook_status = str(chemicalbook_record.get("status") or "") + except Exception as exc: + logger.warning("ChemicalBook 结构化抓取异常: CAS=%s, err=%s", normalized_cas, exc) + + if chemicalbook_record is not None: + try: + chemicalbook_record_path = save_chemicalbook_record(chemicalbook_record) + except OSError as exc: + logger.warning("ChemicalBook sidecar 保存失败: CAS=%s, err=%s", normalized_cas, exc) + chemicalbook_record_path = "" + + return chemicalbook_record, chemicalbook_status, chemicalbook_record_path + + def lookup_and_append_chemical( + self, query: str, excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 在线查询化合物信息并追加到化学品库 Excel 文件末尾. + 查询分为两层: 先用多源核心查询获取可用基础信息, 再用 ChemicalBook + 结构化抓取补充全量数据, 并将原始结果保存为 sidecar JSON. + 参数: + query: str, CAS 号或化合物中英文名称. + excel_path: Optional[str], 目标 Excel 文件路径, 默认为 sheet/chemical_list.xlsx. + 返回: + Optional[Dict[str, Any]], 成功返回稳定结果字典, 包含 row_data, row_index, + chemicalbook_status, chemicalbook_record_path. 查询失败或重复时返回 None. + """ + from ..chem_tools.chemical_lookup import is_cas_number, lookup_chemical + + normalized_query = str(query or "").strip() + if normalized_query == "": + logger.warning("化学品追加失败, 查询参数为空") + return None + + # 先获取多源核心字段 (PubChem + Common Chemistry) + info = lookup_chemical(normalized_query) + + resolved_cas = "" + if info is not None and str(info.cas_number or "").strip() != "": + resolved_cas = str(info.cas_number).strip() + elif is_cas_number(normalized_query) is True: + resolved_cas = normalized_query + + chemicalbook_record, chemicalbook_status, chemicalbook_record_path = self._fetch_chemicalbook_append_artifacts( + resolved_cas, + ) + + if info is None and chemicalbook_record is None: + logger.warning("在线查询未找到化合物: %s", normalized_query) + return None + + row_data = build_append_row_data( + query=resolved_cas, + lookup_info=info, + chemicalbook_record=chemicalbook_record, + ) + if self._has_any_append_core_value(row_data) is False: + logger.warning("化学品追加失败, 未获取到可用核心字段: %s", normalized_query) + return None + + new_row = self._append_chemical_row_to_excel(row_data=row_data, excel_path=excel_path) + if new_row is None: + return None + + summary_text = self._format_append_row_summary(row_data) + logger.info( + "已追加化合物到 Excel: CAS=%s, 英文名=%s, %s, 行号=%d", + row_data.get("cas_number"), + row_data.get("substance_english_name"), + summary_text, + new_row, + ) + + return { + "row_data": row_data, + "row_index": new_row, + "chemicalbook_status": chemicalbook_status, + "chemicalbook_record_path": chemicalbook_record_path, + } + + def lookup_and_append_chemical_by_smiles( + self, smiles: str, excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 根据单个完整 SMILES 在线查询化合物信息, 并追加到化学品库 Excel 文件末尾. + 查询顺序固定为 PubChem 结构查询, 拿到 CAS 后再补 Common Chemistry + 与 ChemicalBook. + 参数: + smiles: str, 单个完整 SMILES 结构式. + excel_path: Optional[str], 目标 Excel 文件路径, 默认为 sheet/chemical_list.xlsx. + 返回: + Optional[Dict[str, Any]], 成功返回稳定结果字典, 包含 row_data, row_index, + chemicalbook_status, chemicalbook_record_path. 查询失败或重复时返回 None. + """ + from ..chem_tools.chemical_lookup import lookup_chemical_by_smiles + + normalized_smiles = str(smiles or "").strip() + if normalized_smiles == "": + logger.warning("SMILES 化学品追加失败, 查询参数为空") + return None + + info = lookup_chemical_by_smiles(normalized_smiles) + if info is None: + logger.warning("SMILES 在线查询未找到化合物: %s", normalized_smiles) + return None + + resolved_cas = str(info.cas_number or "").strip() + chemicalbook_record, chemicalbook_status, chemicalbook_record_path = self._fetch_chemicalbook_append_artifacts( + resolved_cas, + ) + + row_data = build_append_row_data_for_smiles( + lookup_info=info, + chemicalbook_record=chemicalbook_record, + ) + if self._has_any_append_core_value(row_data) is False: + logger.warning("SMILES 化学品追加失败, 未获取到可用核心字段: %s", normalized_smiles) + return None + + new_row = self._append_chemical_row_to_excel(row_data=row_data, excel_path=excel_path) + if new_row is None: + return None + + summary_text = self._format_append_row_summary(row_data) + logger.info( + "已通过 SMILES 追加化合物到 Excel: SMILES=%s, CAS=%s, 英文名=%s, %s, 行号=%d", + normalized_smiles, + row_data.get("cas_number"), + row_data.get("substance_english_name"), + summary_text, + new_row, + ) + + return { + "row_data": row_data, + "row_index": new_row, + "chemicalbook_status": chemicalbook_status, + "chemicalbook_record_path": chemicalbook_record_path, + } + + def prepare_solution_or_beads( + self, + identifier: str, + prepared_form: str, + *, + solvent_name: str = "", + active_content: Any, + target_volume_ml: Optional[Any] = None, + target_active_mmol: Optional[Any] = None, + excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 根据 CAS 或 SMILES 解析母体化合物, 按指定形态生成溶液或 beads 条目并追加到化学品库. + 参数: + identifier: str, 母体化合物 CAS 或 SMILES. + prepared_form: str, 派生形态, 支持 solution 或 beads. + solvent_name: str, solution 使用的溶剂名称. + active_content: Any, solution 时表示 mol/L, beads 时表示 wt%. + target_volume_ml: Optional[Any], solution 目标定容体积, 单位 mL. + target_active_mmol: Optional[Any], beads 目标活性摩尔数, 单位 mmol. + excel_path: Optional[str], 目标 Excel 文件路径. + 返回: + Optional[Dict[str, Any]], 成功返回母体信息, 派生条目信息与配制结果摘要. + 派生条目重复或母体无法解析时返回 None. + 异常: + ValidationError: 形态或数值参数非法时抛出. + """ + normalized_form = str(prepared_form or "").strip().lower() + if normalized_form not in {"solution", "beads"}: + raise ValidationError("派生形态仅支持 solution 或 beads") + + base_result = self._resolve_prepared_base_chemical( + identifier=identifier, + excel_path=excel_path, + ) + if base_result is None: + logger.warning("未找到可用于配置的母体化合物: %s", identifier) + return None + + base_row_data = dict(base_result.get("row_data") or {}) + if base_row_data.get("base_substance") in (None, ""): + base_row_data["base_substance"] = ( + base_row_data.get("substance") + or base_row_data.get("substance_chinese_name") + or base_row_data.get("substance_english_name") + or "" + ) + + normalized_active_content = self._parse_positive_float(active_content, "活性含量") + if normalized_form == "solution": + normalized_target_volume_ml = self._parse_positive_float(target_volume_ml, "目标定容体积") + derived_row_data = build_prepared_chemical_row_data( + base_row_data=base_row_data, + prepared_form="solution", + active_content=normalized_active_content, + solvent_name=solvent_name, + ) + recipe = self._build_solution_recipe( + base_row_data=base_row_data, + concentration_mol_l=normalized_active_content, + target_volume_ml=normalized_target_volume_ml, + solvent_name=solvent_name, + ) + else: + normalized_target_active_mmol = self._parse_positive_float(target_active_mmol, "目标活性 mmol") + derived_row_data = build_prepared_chemical_row_data( + base_row_data=base_row_data, + prepared_form="beads", + active_content=normalized_active_content, + ) + recipe = self._build_beads_recipe( + base_row_data=base_row_data, + wt_percent=normalized_active_content, + target_active_mmol=normalized_target_active_mmol, + ) + + derived_row_index = self._append_chemical_row_to_excel( + row_data=derived_row_data, + excel_path=excel_path, + ) + if derived_row_index is None: + logger.warning( + "派生条目已存在, 跳过添加: identifier=%s, substance=%s", + identifier, + derived_row_data.get("substance"), + ) + return None + + summary_text = self._format_append_row_summary(derived_row_data) + logger.info( + "已完成溶液或 beads 配置: identifier=%s, 母体新建=%s, %s, 行号=%d", + identifier, + base_result.get("base_created"), + summary_text, + derived_row_index, + ) + + return { + "base_row_data": base_row_data, + "base_row_index": base_result.get("row_index"), + "base_created": bool(base_result.get("base_created")), + "derived_row_data": derived_row_data, + "derived_row_index": derived_row_index, + "recipe": recipe, + "chemicalbook_status": base_result.get("chemicalbook_status", ""), + "chemicalbook_record_path": base_result.get("chemicalbook_record_path", ""), + } + + # ---------- 2. 上料动作 ---------- + + def _read_batch_in_records(self, file_path: str) -> List[Dict[str, str]]: + """ + 功能: + 读取上料表格文件(xlsx/csv), 返回标准化记录列表. + 参数: + file_path: str, 上料文件路径. + 返回: + List[Dict[str, str]], 包含 position, tray_type, content, + shelf_position, storage 字段的记录列表. + 异常: + FileNotFoundError: 文件不存在时自动生成模板并抛出. + """ + path = Path(file_path) + if not path.exists(): + logger.warning(f"未找到{file_path}. 自动生成模板文件") + self._generate_batch_in_tray_template(path.with_suffix(".xlsx")) + raise FileNotFoundError(f"上料文件不存在: {file_path}") + + if path.suffix == ".xlsx": + wb = openpyxl.load_workbook(path) + try: + ws, header_row, header_map = self._select_batch_in_sheet(wb) + records = self._iter_batch_in_records(ws, header_row, header_map) + finally: + wb.close() + return records + else: + # CSV 回退: 构造与 xlsx 相同的 dict 结构 + df = pd.read_csv(path) + df = df.fillna("") + records: List[Dict[str, str]] = [] + for _, row in df.iterrows(): + records.append({ + "position": str(row[0]).strip(), + "tray_type": str(row[1]).strip(), + "content": str(row[2]).strip(), + "shelf_position": str(row[3]).strip() if len(row) > 3 else "", + "storage": str(row[4]).strip() if len(row) > 4 else "", + }) + return records + + 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) + try: + ws, header_row, header_map = self._select_batch_in_sheet(wb) + batch_records = self._iter_batch_in_records(ws, header_row, header_map) + finally: + wb.close() + + for record in batch_records: + rows.append((record["position"], record["tray_type"], record["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 {} + + # 执行上料 + resp = self.batch_in_tray(payload) + + return resp + + def prepare_batch_in_with_agv_manifest( + self, + file_path: str = None, + *, + chamber_capacity: int = 8, + ) -> JsonDict: + """ + 功能: + 将 batch_in_tray.xlsx 转换为面向 UniLab 编排的标准上料 manifest。 + 不再支持多轮上料:超过 chamber_capacity 行的记录只取前 chamber_capacity 行。 + 参数: + file_path: 上料文件路径, 默认为 sheet/batch_in_tray.xlsx + chamber_capacity: 过渡舱单轮最大容纳托盘数, 默认 8(不暴露给前端) + 返回: + Dict, 包含 manifest、batch_in_payload 等信息。 + """ + if file_path is None: + file_path = str(MODULE_ROOT / "sheet" / "batch_in_tray.xlsx") + + try: + all_records = self._read_batch_in_records(file_path) + except FileNotFoundError: + return { + "success": False, + "manifest_type": "eit_synthesis_batch_in", + "total_trays": 0, + "rounds": [], + "errors": ["上料文件不存在"], + "message": "上料文件不存在", + "manifest": {}, + "batch_in_payload": [], + } + + if not all_records: + logger.warning("上料记录为空") + return { + "success": False, + "manifest_type": "eit_synthesis_batch_in", + "total_trays": 0, + "rounds": [], + "errors": ["上料记录为空"], + "message": "上料记录为空", + "manifest": {}, + "batch_in_payload": [], + } + + # 取消多轮:超过 chamber_capacity 行只取前 chamber_capacity 行 + if len(all_records) > chamber_capacity: + logger.warning( + "上料记录 %d 行超过上限 %d,只取前 %d 行", + len(all_records), chamber_capacity, chamber_capacity, + ) + all_records = all_records[:chamber_capacity] + + total_records = len(all_records) + all_errors: List[str] = [] + + # 单轮校验 + validate_errors = self._validate_agv_transfer_round_records( + all_records, round_num=1, + ) + if validate_errors: + all_errors.extend(validate_errors) + return { + "success": False, + "manifest_type": "eit_synthesis_batch_in", + "total_trays": total_records, + "rounds": [{"round_num": 1, "success": False, "phase": "validate_round", "errors": validate_errors}], + "errors": all_errors, + "message": "上料记录校验失败", + "manifest": {}, + "batch_in_payload": [], + } + + # 单轮构建 AGV 转运任务 + round_tasks, build_errors = self._build_agv_transfer_tasks(all_records) + all_errors.extend(build_errors) + if build_errors: + return { + "success": False, + "manifest_type": "eit_synthesis_batch_in", + "total_trays": total_records, + "rounds": [{"round_num": 1, "success": False, "phase": "build_agv_tasks", "errors": build_errors}], + "errors": all_errors, + "message": "构建 AGV 转运任务失败", + "manifest": {}, + "batch_in_payload": [], + } + + # 构建手套箱上料 payload + round_rows = [ + (r["position"], r["tray_type"], r["content"]) + for r in all_records + ] + payload = self.build_batch_in_tray_payload(round_rows) + + round_result = { + "round_num": 1, + "success": True, + "manifest_type": "eit_synthesis_batch_in_round", + "requires_outer_door_open": True, + "record_range": [1, total_records], + "transfer_tasks": round_tasks, + "batch_in_payload": payload, + "batch_in_rows": round_rows, + "records": all_records, + "loaded_count": len(round_rows), + } + + return { + "success": True, + "manifest_type": "eit_synthesis_batch_in", + "total_trays": total_records, + "rounds": [round_result], + "errors": all_errors, + "message": "已生成上料 manifest", + # 顶层快捷字段:前端 handles 直接引用 + "manifest": round_result, + "batch_in_payload": payload, + } + + def execute_batch_in_payload( + self, + batch_in_payload: List[JsonDict], + *, + task_id: Optional[int] = None, + poll_interval_s: float = 1.0, + timeout_s: float = 900.0, + ) -> JsonDict: + """ + 功能: + 执行单轮手套箱本地上料 payload,不再由该函数负责 AGV 转运。 + 参数: + batch_in_payload: 标准 batch_in_tray payload。 + task_id: 可选任务 ID,仅用于日志记录。 + poll_interval_s: 轮询间隔。 + timeout_s: 超时时间。 + 返回: + Dict, batch_in_tray 接口响应。 + """ + if not isinstance(batch_in_payload, list) or len(batch_in_payload) == 0: + raise ValidationError("batch_in_payload 不能为空") + return self.batch_in_tray( + batch_in_payload, + task_id=task_id, + poll_interval_s=poll_interval_s, + timeout_s=timeout_s, + ) + + def batch_in_tray_with_agv_transfer( + self, + file_path: str = None, + *, + block: bool = True, + chamber_capacity: int = 8, + ) -> JsonDict: + """ + 功能: + 兼容旧脚本入口:根据上料文件执行“开门 -> AGV 转运 -> 手套箱上料”。 + 新的前端/调度接口推荐改为先调用 `prepare_batch_in_with_agv_manifest`, + 再分别调用 AGV `transfer_manifest` 与手套箱 `execute_batch_in_payload`。 + 参数: + file_path: 上料文件路径, 默认为 sheet/batch_in_tray.xlsx + block: 是否阻塞等待 AGV 转运完成 + chamber_capacity: 过渡舱单次最大容纳托盘数, 默认 8 + 返回: + Dict, 包含多轮次的聚合结果。 + """ + manifest = self.prepare_batch_in_with_agv_manifest( + file_path, + chamber_capacity=chamber_capacity, + ) + if not manifest.get("success"): + return { + "success": False, + "total_trays": manifest.get("total_trays", 0), + "transferred_trays": 0, + "loaded_trays": 0, + "rounds": manifest.get("rounds", []), + "charging_result": None, + "errors": manifest.get("errors", []), + "message": manifest.get("message", "生成上料 manifest 失败"), + } + + import sys + sys.path.append(str(Path(__file__).resolve().parent.parent.parent)) + from eit_agv.controller.agv_controller import AGVController + agv_controller = AGVController() + + total_records = int(manifest.get("total_trays", 0)) + rounds_result: List[JsonDict] = [] + all_errors: List[str] = [] + total_transferred = 0 + total_loaded = 0 + overall_success = True + + for round_manifest in manifest.get("rounds", []): + round_num = int(round_manifest.get("round_num", len(rounds_result) + 1)) + record_range = round_manifest.get("record_range") or ["?", "?"] + logger.info( + f"===== 第 {round_num} 轮上料开始 " + f"(记录 {record_range[0]}~{record_range[1]}/{total_records}) =====" + ) + + door_errors = self._ensure_outer_door_open() + if door_errors: + all_errors.extend(door_errors) + rounds_result.append( + { + "round_num": round_num, + "success": False, + "phase": "open_door", + "errors": door_errors, + } + ) + overall_success = False + break + + round_tasks = round_manifest.get("transfer_tasks") or [] + transferred = 0 + batches: List[JsonDict] = [] + if round_tasks: + agv_result = agv_controller.transfer_manifest( + round_manifest, + block=block, + auto_run_analysis=False, + ) + transferred = int(agv_result.get("transferred_trays", 0)) + batches = agv_result.get("batches", []) + transfer_errors = agv_result.get("errors", []) + total_transferred += transferred + all_errors.extend(transfer_errors) + + if transfer_errors: + rounds_result.append( + { + "round_num": round_num, + "success": False, + "phase": "agv_transfer", + "transferred": transferred, + "batches": batches, + "errors": transfer_errors, + } + ) + overall_success = False + break + + round_rows = round_manifest.get("batch_in_rows") or [] + payload = round_manifest.get("batch_in_payload") or [] + + in_tray_result = None + if payload: + try: + in_tray_result = self.execute_batch_in_payload(payload) + total_loaded += len(round_rows) + logger.info(f"第 {round_num} 轮上料完成") + except Exception as e: + error_msg = f"第 {round_num} 轮上料异常: {e}" + logger.error(error_msg) + all_errors.append(error_msg) + rounds_result.append( + { + "round_num": round_num, + "success": False, + "phase": "in_tray", + "transferred": transferred, + "batches": batches, + "in_tray_result": None, + "errors": [error_msg], + } + ) + overall_success = False + break + else: + logger.warning(f"第 {round_num} 轮上料 payload 为空, 跳过上料") + + rounds_result.append( + { + "round_num": round_num, + "success": True, + "phase": "completed", + "transferred": transferred, + "batches": batches, + "in_tray_result": in_tray_result, + "loaded_count": len(round_rows), + } + ) + logger.info(f"===== 第 {round_num} 轮上料完成 =====") + + final_charging_result = None + try: + final_charging_result = agv_controller.go_to_charging_station() + if final_charging_result is not None: + logger.info("AGV 已确认在充电站") + else: + logger.warning("AGV 返回充电站失败") + except Exception as e: + logger.error(f"AGV 返回充电站时发生异常: {e}") + + overall_success = overall_success and len(all_errors) == 0 + return { + "success": overall_success, + "total_trays": total_records, + "transferred_trays": total_transferred, + "loaded_trays": total_loaded, + "rounds": rounds_result, + "charging_result": final_charging_result, + "errors": all_errors, + "message": ( + "全部轮次完成" if overall_success + else f"执行过程中出现错误, 完成 {len([r for r in rounds_result if r.get('success')])} 轮" + ), + } + + def _validate_agv_transfer_round_records( + self, + records: List[Dict[str, str]], + *, + round_num: int, + ) -> List[str]: + """ + 功能: + 校验单轮 AGV 上料记录是否满足唯一性要求. + 同一轮次内 position 与 shelf_position 都必须唯一, 且不能为空. + 参数: + records: List[Dict[str, str]], 单轮上料记录列表. + round_num: int, 当前轮次编号. + 返回: + List[str], 校验失败时的错误信息列表. + """ + errors: List[str] = [] + positions: List[str] = [] + shelf_positions: List[str] = [] + + for index, record in enumerate(records, start=1): + position = str(record.get("position", "")).strip() + shelf_position = str(record.get("shelf_position", "")).strip() + + if position == "": + errors.append(f"第 {round_num} 轮第 {index} 条记录缺少 position, 无法执行 AGV 上料") + else: + positions.append(position) + + if shelf_position == "": + errors.append(f"第 {round_num} 轮第 {index} 条记录缺少 shelf_position, 无法执行 AGV 上料") + else: + shelf_positions.append(shelf_position) + + duplicate_positions = self._collect_duplicate_texts(positions) + if duplicate_positions: + errors.append( + "第 " + f"{round_num} 轮存在重复的 position: {', '.join(duplicate_positions)}. " + "同一轮次不能复用 TB 位, 请检查 batch_in_tray.xlsx 顺序或重新生成上料文件" + ) + + duplicate_shelf_positions = self._collect_duplicate_texts(shelf_positions) + if duplicate_shelf_positions: + errors.append( + "第 " + f"{round_num} 轮存在重复的 shelf_position: {', '.join(duplicate_shelf_positions)}. " + "同一轮次不能复用同一个货架位, 请检查 batch_in_tray.xlsx 顺序或重新生成上料文件" + ) + + return errors + + 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", "shelf_position", "storage"]) + ws.column_dimensions["B"].width = 60 + ws.column_dimensions["C"].width = 80 + ws.column_dimensions["D"].width = 15 + ws.column_dimensions["E"].width = 50 + + # 位置下拉,包含 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" + + # 定义命名区域, 避免跨 sheet 验证被 Excel 写成 x14 扩展 + options_name = "tray_type_options" + options_ref = f"validation_meta!$A$1:$A${len(tray_display)}" + wb.defined_names.add(DefinedName(options_name, attr_text=options_ref)) + + dv_tray = DataValidation( + type="list", + formula1=f"={options_name}", + showInputMessage=True, + ) + ws.add_data_validation(dv_tray) + dv_tray.add("B2:B101") + + ws["C1"] = "content(耗材填数量; 物质填: A1|名称|2mL; B2|名称|5mg)" + ws["D1"] = "shelf_position" + ws["E1"] = "storage(格式: 物质|位置; 多个用;隔开)" + safe_workbook_save(wb, file_path) + logger.info(f"已生成上料模板: {file_path}") + + def _find_header_in_sheet(self, worksheet: Any, header_keyword: str) -> Tuple[Optional[int], Optional[int]]: + """ + 功能: + 在单个工作表的前 50 行和前 50 列中查找目标表头. + 参数: + worksheet: openpyxl 工作表对象. + header_keyword: 需要匹配的表头关键词, 例如"实验编号". + 返回: + Tuple[Optional[int], Optional[int]], 命中时返回(行号, 列号), 未命中返回(None, None). + """ + max_scan_row = min(worksheet.max_row, 50) + max_scan_col = min(worksheet.max_column, 50) + + for row_index in range(1, max_scan_row + 1): + for col_index in range(1, max_scan_col + 1): + cell_value = worksheet.cell(row_index, col_index).value + if isinstance(cell_value, str) is False: + continue + + # 去除空白字符, 兼容用户在表头中插入空格或换行. + normalized_text = cell_value.replace(" ", "").replace("\n", "").replace("\r", "").replace("\t", "").strip() + if header_keyword in normalized_text: + return row_index, col_index + + return None, None + + def _select_task_template_sheet( + self, + workbook: Workbook, + header_keyword: str = "实验编号", + ) -> Tuple[Optional[Any], Optional[int], Optional[int]]: + """ + 功能: + 从任务模板工作簿中选择包含目标表头的工作表. + 选择顺序为: "实验方案设定" -> 当前激活工作表 -> 其余工作表. + 参数: + workbook: openpyxl Workbook 对象. + header_keyword: 需要匹配的表头关键词. + 返回: + Tuple[Optional[Any], Optional[int], Optional[int]], 命中时返回(工作表, 行号, 列号), 未命中返回(None, None, None). + """ + candidate_sheet_names: List[str] = [] + preferred_sheet_name = "实验方案设定" + if preferred_sheet_name in workbook.sheetnames: + candidate_sheet_names.append(preferred_sheet_name) + + active_sheet_name = workbook.active.title + if active_sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(active_sheet_name) + + for sheet_name in workbook.sheetnames: + if sheet_name not in candidate_sheet_names: + candidate_sheet_names.append(sheet_name) + + for sheet_name in candidate_sheet_names: + worksheet = workbook[sheet_name] + header_row, exp_no_col = self._find_header_in_sheet(worksheet, header_keyword) + if header_row is not None and exp_no_col is not None: + if sheet_name != active_sheet_name: + logger.info( + "模板解析使用工作表: %s, 当前激活工作表: %s", + sheet_name, + active_sheet_name, + ) + return worksheet, header_row, exp_no_col + + return None, None, None + + # ---------- 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 = self._read_table_file_with_required_columns( + c_path, + required_columns=["substance"], + ) + 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(), + "density (g/mL)": _pick(row, "density (g/ml)", "density(g/ml)", "density_g_ml", "density", default=None), + "physical_form": str(_pick(row, "physical_form", default="") or "").strip().lower(), + "active_content": _pick(row, "active_content","active_content(mmol/ml or wt%)" ,"active_content(mol/l or wt%)", default="" ), + } + + # 3. 读取任务模板 -> params(Dict), headers(List), data_rows(List[List]) + wb = load_workbook(t_path, data_only=True) + ws, header_row, exp_no_col = self._select_task_template_sheet(wb, header_keyword="实验编号") + if ws is None or header_row is None or exp_no_col is None: + raise ValueError(f"模板中未找到'实验编号'表头, 可用工作表: {wb.sheetnames}") + + # 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. 提交任务信息到工站 + try: + resp = self._submit_task_payload(task_payload) + except ApiError as exc: + if getattr(exc, "code", None) == 409: + # 自动重命名: 在任务名称后添加当前日期时间(精确到秒) + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + + task_name = task_payload.get("task_name") or params.get("实验名称") + new_task_name = f"{task_name}_{timestamp}" + + task_payload["task_name"] = new_task_name + logger.info(f"任务名称重复, 自动重命名为: {new_task_name}") + + # 重试提交 + try: + resp = self._submit_task_payload(task_payload) + except ApiError as retry_exc: + logger.error(f"重命名后任务提交仍失败: {retry_exc}") + raise + else: + raise + + # 6. 提交任务信息到工站 + task_id = resp.get("task_id") + + # 7. 回写任务ID和任务名称到模板 + try: + task_id_int = int(task_id) + id_updated = False + name_updated = False + # 获取实际提交的任务名称(可能经过重命名) + final_task_name = task_payload.get("task_name") + + for r in range(1, ws.max_row + 1): + key_val = ws.cell(r, 1).value + if key_val is None: + continue + key_str = str(key_val).strip() + # 回写实验ID + if key_str == "实验ID": + ws.cell(r, 2, value=task_id_int) + id_updated = True + # 回写任务名称(重命名后同步更新模板) + if key_str == "实验名称" and final_task_name is not None: + ws.cell(r, 2, value=final_task_name) + name_updated = True + + if id_updated or name_updated: + safe_workbook_save(wb, t_path) + if id_updated: + logger.info("已将任务ID写入模板文件: %s", t_path) + if name_updated: + logger.info("已将任务名称同步写入模板文件: %s", final_task_name) + else: + logger.warning("未找到'实验ID'位置, 未回写任务ID") + except Exception as exc: + logger.warning("任务ID回写失败: %s", exc) + + # 8. 将模板文件拷贝到 data/tasks// 并重命名为任务ID + try: + task_dir = MODULE_ROOT / "data" / "tasks" / str(task_id) + task_dir.mkdir(parents=True, exist_ok=True) # 创建任务文件夹(若已存在则忽略) + dest_path = task_dir / f"{task_id}_experiment_plan{t_path.suffix}" # 目标路径: _experiment_plan.xlsx + shutil.copy2(t_path, dest_path) # 拷贝并保留元数据 + logger.info("已将模板文件拷贝至任务目录: %s", dest_path) + except Exception as exc: + logger.warning("模板文件拷贝至任务目录失败: %s", exc) + + 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="Microsoft YaHei", charset=134, family=2, scheme="minor", sz=11) + title_font = Font(name="Microsoft YaHei", charset=134, family=2, scheme="minor", sz=11, bold=True) + center = Alignment(horizontal="center", vertical="center") + + # 覆盖默认 Normal 样式,保证空白单元格也用微软雅黑 + for style in getattr(wb, "_named_styles", []): + if getattr(style, "name", "").lower() == "normal": + style.font = base_font + break + + # --- 1. 定义左侧参数配置数据 (行2开始, A列和B列) --- + left_params = [ + ("实验设定", ""), + ("实验名称", "Auto_task"), + ("实验ID", 0), + ("反应设定", ""), + ("反应规模(mmol)", "0.2"), + ("反应器类型", "heat"), + ("反应时间(min/h)", "8h"), + ("反应温度(°C)", 40), + ("转速(rpm)", 500), + ("搅拌后⽬标温度(°C)", 30), + ("等待目标温度", "否"), + ("称量设定", ""), + ("称量误差(%)", 3), + ("最大称量误差(mg)", 1), + ("加料设定", ""), + ("固定加料顺序", "否"), + ("自动加磁子", "是"), + ("内标设定", ""), + ("内标种类", "1,3,5-三异丙基苯(溶液,1mol/L in MeCN)"), + ("内标用量(μL/mg)", 100), + ("加入内标后搅拌时间(min)", 5), + ("稀释设定", ""), + ("稀释液种类", "乙腈"), + ("稀释量(μL)", 500), + ("闪滤设定", ""), + ("闪滤液种类", "乙腈"), + ("闪滤液用量(μL)", 500), + ("取样量(μL)", 1), + ("闪滤实验编号", "全部"), # 空/"全部"=全部实验闪滤; 支持 "1-12,24,28" 格式 + ("", ""), # 空行 + ] + left_param_rows = len(left_params) + + # --- 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. 底部注释 (跟随参数行, 预留一行空白) --- + note_row = left_param_rows + 2 + note_text = "注:试剂量支持单位:(eq,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 # 合并后的单元格居中 + + max_template_row = max(note_row, 25) + + # --- 6. 字体铺满 (A1:M*) --- + for r in range(1, max_template_row + 1): + 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, max_template_row + 1): + for c in range(3, 13): # C..L + ws.cell(r, c).alignment = center + + # A 列:参数行和注释行居中 + a_rows = [] + b_rows = [] + for idx, (param_name, default_val) in enumerate(left_params): + row_idx = idx + 1 + if param_name != "": + a_rows.append(row_idx) + if param_name != "" and default_val != "": + b_rows.append(row_idx) + for r in a_rows + [note_row]: + ws.cell(r, 1).alignment = center + + # B 列:只有有值的参数行居中(标题行/空白行/合并后的 B 不处理) + for r in b_rows: + 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 + + safe_workbook_save(wb, path) + logger.info(f"已生成任务模板: {path}") + + # ---------- 4. 物料核算 ---------- + def check_resource_for_task(self, template_path: str, chemical_db_path: str, auto_generate_batch_file: bool = True) -> JsonDict: + """ + 功能: + 读取实验模板与化学品库, 构建任务 Payload, 获取站内资源并比对是否满足实验需求。 + 参数: + template_path: 实验模板文件路径(xlsx/csv)。 + chemical_db_path: 化学品库文件路径(xlsx/csv)。 + auto_generate_batch_file: 是否自动生成上料文件, 默认为 True。 + 返回: + Dict, analyze_resource_readiness 的结果, 包含需求、库存、缺失与冗余信息。 + """ + t_path = Path(template_path) + c_path = Path(chemical_db_path) + + if not t_path.exists(): + raise FileNotFoundError(f"未找到实验模板文件: {t_path}") + if not c_path.exists(): + raise FileNotFoundError(f"未找到化学品库文件: {c_path}") + + chem_df = self._read_table_file_with_required_columns( + c_path, + required_columns=["substance"], + ) + 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(), + "density (g/mL)": _pick(row, "density (g/ml)", "density(g/ml)", "density_g_ml", "density", default=None), + "physical_form": str(_pick(row, "physical_form", default="") or "").strip().lower(), + "active_content": _pick(row, "active_content", "active_content(mmol/ml or wt%)", "active_content(mol/l or wt%)", default=""), + } + + wb = load_workbook(t_path, data_only=True) + ws, header_row, exp_no_col = self._select_task_template_sheet(wb, header_keyword="实验编号") + if ws is None or header_row is None or exp_no_col is None: + raise ValidationError(f"模板中未找到'实验编号'表头, 可用工作表: {wb.sheetnames}") + + params: Dict[str, Any] = {} + exp_name = ws.cell(1, 2).value + if exp_name is not None and str(exp_name).strip() != "": + params["实验名称"] = str(exp_name).strip() + + task_id = None # 用于存储实验ID + 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 key_str == "": + continue + if key_str.startswith("注:") or key_str.startswith("注"): + continue + if val is None or (isinstance(val, str) and val.strip() == ""): + continue + + # 识别实验ID参数并提取整数值 + if key_str == "实验ID": + try: + task_id = int(val) + self._logger.info("从模板中读取到实验ID: %d", task_id) + except (ValueError, TypeError): + self._logger.warning("实验ID格式无效: %s, 将跳过二次校验", val) + + params[key_str] = val + + 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() + if s.startswith("试剂") and "量" not in s and s != "试剂名称": + reagent_idx += 1 + headers.append(f"试剂名称_{reagent_idx}") + continue + 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) + + 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() == ""): + 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 + row_vals.append("" if v is None else v) + data_rows.append(row_vals) + + task_payload = self.build_task_payload(params, headers, data_rows, chemical_db) + resource_rows = self.get_resource_info() + result = self.analyze_resource_readiness(task_payload, resource_rows, chemical_db, task_id=task_id) + + # 自动保存物料核算结果 + if self._data_manager and task_id: + self._data_manager.save_resource_check(str(task_id), result) + + # 自动生成上料文件 + if auto_generate_batch_file and task_id: + self.auto_generate_batch_in_tray_from_resource_check(task_id) + + return result + + def auto_generate_batch_in_tray_from_resource_check(self, task_id: Optional[int] = None) -> None: + """ + 功能: + 根据资源核查结果自动修改上料文件, 考虑料盘规格, 优先填满一个料盘再使用下一个 + 参数: + task_id: 任务ID, 如果为None则自动搜索data/tasks中id最大且状态为UNSTARTED的任务 + 返回: + None + """ + import json + import math + + # 1. 确定任务ID + if task_id is None: + tasks_dir = MODULE_ROOT / "data/tasks" + if not tasks_dir.exists(): + raise FileNotFoundError(f"任务目录不存在: {tasks_dir}") + + # 获取所有任务文件夹, 按ID降序排列 + task_folders = [] + for folder in tasks_dir.iterdir(): + if folder.is_dir() and folder.name.isdigit(): + task_folders.append(int(folder.name)) + + if not task_folders: + raise FileNotFoundError("未找到任何任务文件夹") + + task_folders.sort(reverse=True) + + # 查找第一个状态为UNSTARTED的任务 + found_task = None + for tid in task_folders: + task_info_path = tasks_dir / str(tid) / "task_info.json" + if task_info_path.exists(): + with open(task_info_path, "r", encoding="utf-8") as f: + task_info = json.load(f) + if task_info.get("status") == "UNSTARTED": + found_task = tid + break + + if found_task is None: + raise ValueError("未找到状态为UNSTARTED的任务") + + task_id = found_task + logger.info(f"自动选择任务ID: {task_id}") + + # 2. 读取resource_check.json + resource_check_path = MODULE_ROOT / "data" / "tasks" / str(task_id) / "resource_check.json" + if not resource_check_path.exists(): + raise FileNotFoundError(f"未找到资源核查文件: {resource_check_path}") + + with open(resource_check_path, "r", encoding="utf-8") as f: + resource_check = json.load(f) + + missing_list = resource_check.get("missing", []) + if not missing_list: + logger.info("没有缺失的物资, 无需生成上料文件") + return + + # 3. 读取chemical_list.xlsx + chemical_list_path = MODULE_ROOT / "sheet" / "chemical_list.xlsx" + if not chemical_list_path.exists(): + raise FileNotFoundError(f"未找到化学品列表文件: {chemical_list_path}") + + chem_df = self._read_table_file_with_required_columns( + chemical_list_path, + required_columns=["substance", "physical_state", "storage_location"], + ) + chem_df = chem_df.fillna("") + + # 创建物质名到信息的映射 + chemical_dict = {} + for _, row in chem_df.iterrows(): + substance = str(row.get("substance", "")).strip() + if substance: + chemical_dict[substance] = { + "physical_state": str(row.get("physical_state", "")).strip().lower(), + "storage_location": str(row.get("storage_location", "")).strip() + } + + # 4. 准备料盘规格信息和位置管理 + # TB 位共 8 个, 上料完成后空出可复用; 货架共 3 行 × 4 列 = 12 个位置 + position_list = ["TB-2-1", "TB-2-2", "TB-2-3", "TB-2-4", + "TB-1-1", "TB-1-2", "TB-1-3", "TB-1-4", + "TB-2-1", "TB-2-2", "TB-2-3", "TB-2-4"] + shelf_position_list = ["3-1", "3-2", "3-3", "3-4", + "2-1", "2-2", "2-3", "2-4", + "1-1", "1-2", "1-3", "1-4"] + + # 料盘类型到规格的映射 + tray_spec_map = { + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): TraySpec.REAGENT_BOTTLE_TRAY_2ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): TraySpec.REAGENT_BOTTLE_TRAY_8ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): TraySpec.REAGENT_BOTTLE_TRAY_40ML, + int(ResourceCode.POWDER_BUCKET_TRAY_30ML): TraySpec.POWDER_BUCKET_TRAY_30ML, + int(ResourceCode.REACTION_TUBE_TRAY_2ML): TraySpec.REACTION_TUBE_TRAY_2ML, + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML): TraySpec.TEST_TUBE_MAGNET_TRAY_2ML, + int(ResourceCode.REACTION_SEAL_CAP_TRAY): TraySpec.REACTION_SEAL_CAP_TRAY, + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY): TraySpec.FLASH_FILTER_INNER_BOTTLE_TRAY, + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): TraySpec.FLASH_FILTER_OUTER_BOTTLE_TRAY, + int(ResourceCode.TIP_TRAY_50UL): TraySpec.TIP_TRAY_50UL, + int(ResourceCode.TIP_TRAY_1ML): TraySpec.TIP_TRAY_1ML, + int(ResourceCode.TIP_TRAY_5ML): TraySpec.TIP_TRAY_5ML, + } + + # 跟踪每种料盘类型的使用情况: + # {tray_type_code: [(position, shelf_position, current_slot_index, max_slots, alloc_idx)]} + tray_usage = {} + # 按索引追踪已分配的位置, 避免 TB 位复用时误判 + allocated_position_indices: set = set() + + def _get_slot_name(slot_index: int, cols: int, rows: int) -> str: + """ + 功能: + 根据坑位索引生成坑位名称, 按行优先排列 + 参数: + slot_index: 坑位索引(从0开始) + cols: 列数 + rows: 行数 + 返回: + 坑位名称, 如 "A1", "A2", "B1" 等 + """ + row_idx = slot_index // cols + col_idx = slot_index % cols + row_letter = chr(ord('A') + row_idx) + return f"{row_letter}{col_idx + 1}" + + def _allocate_slot(tray_type_code: int, tray_type_name: str) -> Tuple[str, str, str]: + """ + 功能: + 为指定料盘类型分配一个坑位. + 优先填满已有的未满料盘, 否则从位置列表中分配新位置. + 使用索引追踪已分配位置, 支持 TB 位跨轮次复用. + 参数: + tray_type_code: 料盘类型代码 + tray_type_name: 料盘类型名称 + 返回: + (position, shelf_position, slot_name) 元组 + """ + # 获取料盘规格 + spec = tray_spec_map.get(tray_type_code) + if spec is None: + raise ValueError(f"未找到料盘规格: {tray_type_code}") + + cols, rows = spec + max_slots = cols * rows + + # 检查是否已有该类型的料盘在使用 + if tray_type_code not in tray_usage: + tray_usage[tray_type_code] = [] + + # 查找是否有未满的料盘 + for tray_info in tray_usage[tray_type_code]: + position, shelf_position, current_slot, max_slots_in_tray = tray_info[:4] + if current_slot < max_slots_in_tray: + # 找到未满的料盘, 分配下一个坑位 + slot_name = _get_slot_name(current_slot, cols, rows) + tray_info[2] += 1 # 更新当前坑位索引 + logger.info(f"使用现有料盘 {position}, 坑位 {slot_name}") + return position, shelf_position, slot_name + + # 没有未满的料盘, 需要分配新位置(按索引遍历) + new_idx = None + for i in range(len(position_list)): + if i not in allocated_position_indices: + new_idx = i + break + + if new_idx is None: + raise ValueError("可用位置已用完, 无法分配新料盘") + + allocated_position_indices.add(new_idx) + new_position = position_list[new_idx] + new_shelf_position = shelf_position_list[new_idx] + + # 创建新料盘记录 + slot_name = _get_slot_name(0, cols, rows) + tray_usage[tray_type_code].append( + [new_position, new_shelf_position, 1, max_slots, new_idx] + ) + logger.info(f"分配新料盘 {new_position}(shelf {new_shelf_position}), 类型 {tray_type_name}") + return new_position, new_shelf_position, slot_name + + def _normalize_consumable_name(raw_name: str) -> str: + """ + 功能: + 将耗材名称归一化, 用于在统一别名表中稳定匹配. + 参数: + raw_name: str, 原始耗材名称. + 返回: + str, 归一化后的耗材名称. + """ + normalized_name = str(raw_name).strip().lower() + normalized_name = normalized_name.replace("μ", "u").replace("µ", "u") + normalized_name = normalized_name.replace(" ", "") + normalized_name = normalized_name.replace("-", "") + normalized_name = normalized_name.replace("_", "") + return normalized_name + + # 5. 解析missing列表并生成上料数据 + # 使用字典来按位置分组物资: {position: {"tray_type": ..., "contents": [...], "shelf_position": ..., "storages": [...]}} + position_groups = {} + + # 分类存储: 先处理固体、液体、耗材,按顺序生成 + solid_items = [] + liquid_items = [] + consumable_items = [] + + # 第一遍: 分类 + for missing_item in missing_list: + # 解析格式: "物质名:数量单位" + if ":" not in missing_item: + logger.warning(f"跳过格式错误的缺失项: {missing_item}") + continue + + substance, amount_str = missing_item.split(":", 1) + substance = substance.strip() + amount_str = amount_str.strip() + + # 检查是否为耗材(以"件"结尾) + is_consumable = amount_str.endswith("件") + + if is_consumable: + consumable_items.append((substance, amount_str)) + else: + # 获取物质信息判断固液 + chem_info = chemical_dict.get(substance) + if not chem_info: + logger.warning(f"在化学品列表中未找到物质: {substance}, 跳过") + continue + + physical_state = chem_info["physical_state"] + if physical_state == "solid": + solid_items.append((substance, amount_str, chem_info)) + elif physical_state == "liquid": + liquid_items.append((substance, amount_str, chem_info)) + else: + logger.warning(f"物质状态未知: {physical_state}, 跳过物质 {substance}") + + # 第二遍: 按顺序处理 - 固体 + for substance, amount_str, chem_info in solid_items: + physical_state = chem_info["physical_state"] + storage_location = chem_info["storage_location"] + + # 解析数量和单位 + amount_value = 0.0 + unit = "" + + # 提取数字和单位 + import re + match = re.match(r"([\d.]+)\s*(\w+)", amount_str) + if match: + amount_value = float(match.group(1)) + unit = match.group(2).lower() + else: + logger.warning(f"无法解析数量: {amount_str}, 跳过") + continue + + # 固体: 使用粉桶托盘 + tray_type_code = int(ResourceCode.POWDER_BUCKET_TRAY_30ML) + tray_type_name = f"30 mL粉桶托盘({tray_type_code})" + + # 单位转换为mg + if unit == "mg": + final_amount = amount_value + elif unit == "g": + final_amount = amount_value * 1000 + else: + logger.warning(f"固体物质单位不支持: {unit}, 跳过") + continue + + # 计算上料量: 最小100mg, 大于100mg按实际需要量的两倍取整 + if final_amount <= 100: + final_amount = 100 + else: + final_amount = final_amount * 2 + # 向上取整到百位 + final_amount = math.ceil(final_amount / 100) * 100 + + final_unit = "mg" + + # 分配坑位 + try: + position, shelf_position, slot_name = _allocate_slot(tray_type_code, tray_type_name) + except ValueError as e: + logger.warning(f"无法分配坑位: {e}, 跳过物资 {substance}") + break + + # 处理final_amount可能是int或float的情况 + if isinstance(final_amount, int): + amount_display = final_amount + elif isinstance(final_amount, float) and final_amount.is_integer(): + amount_display = int(final_amount) + else: + amount_display = final_amount + + # 生成单个坑位的内容: "坑位|物质名|数量单位" + slot_content = f"{slot_name}|{substance}|{amount_display}{final_unit}" + + # 按位置+货架分组(同一 TB 位在不同轮次对应不同货架, 需区分) + tray_key = f"{position}_{shelf_position}" + if tray_key not in position_groups: + position_groups[tray_key] = { + "position": position, + "tray_type": tray_type_name, + "contents": [], + "shelf_position": shelf_position, + "storages": [] + } + + position_groups[tray_key]["contents"].append(slot_content) + position_groups[tray_key]["storages"].append(f"{substance}|{storage_location if storage_location else '未知'}") + logger.info(f"添加固体上料项: {substance} -> {position} {slot_name}, {final_amount}{final_unit}") + + # 第三遍: 处理液体 + for substance, amount_str, chem_info in liquid_items: + physical_state = chem_info["physical_state"] + storage_location = chem_info["storage_location"] + + # 解析数量和单位 + amount_value = 0.0 + unit = "" + + # 提取数字和单位 + import re + match = re.match(r"([\d.]+)\s*(\w+)", amount_str) + if match: + amount_value = float(match.group(1)) + unit = match.group(2).lower() + else: + logger.warning(f"无法解析数量: {amount_str}, 跳过") + continue + + # 液体: 根据量选择试剂瓶托盘 + # 单位转换为mL + if unit == "ml" or unit == "mL": + final_amount = amount_value + elif unit == "μl" or unit == "ul": + final_amount = amount_value / 1000 + elif unit == "l": + final_amount = amount_value * 1000 + else: + logger.warning(f"液体物质单位不支持: {unit}, 跳过") + continue + + # 最少1mL, 向上取整 + final_amount = max(1, math.ceil(final_amount)) + final_unit = "mL" + + # 根据量选择瓶子规格 + if final_amount <= 2: + tray_type_code = int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML) + tray_type_name = f"2 mL试剂瓶托盘({tray_type_code})" + elif final_amount <= 8: + tray_type_code = int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML) + tray_type_name = f"8 mL试剂瓶托盘({tray_type_code})" + elif final_amount <= 40: + tray_type_code = int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML) + tray_type_name = f"40 mL试剂瓶托盘({tray_type_code})" + else: + logger.warning(f"液体量超过40mL, 不支持: {final_amount}mL, 跳过") + continue + + # 分配坑位 + try: + position, shelf_position, slot_name = _allocate_slot(tray_type_code, tray_type_name) + except ValueError as e: + logger.warning(f"无法分配坑位: {e}, 跳过物资 {substance}") + break + + # 处理final_amount可能是int或float的情况 + if isinstance(final_amount, int): + amount_display = final_amount + elif isinstance(final_amount, float) and final_amount.is_integer(): + amount_display = int(final_amount) + else: + amount_display = final_amount + + # 生成单个坑位的内容: "坑位|物质名|数量单位" + slot_content = f"{slot_name}|{substance}|{amount_display}{final_unit}" + + # 按位置+货架分组(同一 TB 位在不同轮次对应不同货架, 需区分) + tray_key = f"{position}_{shelf_position}" + if tray_key not in position_groups: + position_groups[tray_key] = { + "position": position, + "tray_type": tray_type_name, + "contents": [], + "shelf_position": shelf_position, + "storages": [] + } + + position_groups[tray_key]["contents"].append(slot_content) + position_groups[tray_key]["storages"].append(f"{substance}|{storage_location if storage_location else '未知'}") + logger.info(f"添加液体上料项: {substance} -> {position} {slot_name}, {final_amount}{final_unit}") + + # 第四遍: 处理耗材 + unrecognized_consumable_names: List[str] = [] + for substance, amount_str in consumable_items: + # 耗材处理: 解析数量 + import re + match = re.match(r"([\d.]+)\s*件", amount_str) + if not match: + logger.warning(f"无法解析耗材数量: {amount_str}, 跳过") + continue + + consumable_count = int(float(match.group(1))) + + normalized_substance = _normalize_consumable_name(substance) + consumable_code = CONSUMABLE_ALIAS_TO_CODE.get(normalized_substance) + if consumable_code is None: + logger.error("无法识别耗材类型: %s, 请先在constants.py中注册别名", substance) + unrecognized_consumable_names.append(substance) + continue + + tray_type_code = CONSUMABLE_CODE_TO_TRAY_CODE.get(consumable_code) + if tray_type_code is None: + logger.error("耗材缺少托盘映射: %s(code=%s), 请检查constants.py", substance, consumable_code) + unrecognized_consumable_names.append(substance) + continue + + tray_display_name = TRAY_CODE_DISPLAY_NAME.get(tray_type_code, f"未知托盘") + tray_type_name = f"{tray_display_name}({tray_type_code})" + consumable_display_name = CONSUMABLE_CODE_DISPLAY_NAME.get(consumable_code, substance) + + # 获取托盘规格, 计算满盘数量 + spec = tray_spec_map.get(tray_type_code) + if spec is None: + logger.warning(f"未找到托盘规格: {tray_type_code}, 跳过") + continue + + cols, rows = spec + full_tray_capacity = cols * rows + + # REACTION_TUBE_TRAY_2ML 特殊处理: 按需求数量选择12/24/36/48规格 + if tray_type_code == int(ResourceCode.REACTION_TUBE_TRAY_2ML): + tray_capacity = 24 # 一盘24个 + available_specs = [12, 24, 36, 48] + + # 选择最小的满足需求的规格, 超出48则取48 + chosen_spec = available_specs[-1] + for spec_val in available_specs: + if consumable_count <= spec_val: + chosen_spec = spec_val + break + + # 按每盘24个拆分到多个托盘 + remaining = chosen_spec + tray_allocations = [] + while remaining > 0: + tray_amount = min(remaining, tray_capacity) + tray_allocations.append(tray_amount) + remaining -= tray_amount + + # 为每个托盘分配独立位置 + for tray_amount in tray_allocations: + new_idx = None + for i in range(len(position_list)): + if i not in allocated_position_indices: + new_idx = i + break + + if new_idx is None: + logger.warning("可用位置已用完, 无法分配新料盘, 跳过耗材 %s", substance) + break + + allocated_position_indices.add(new_idx) + position = position_list[new_idx] + shelf_position = shelf_position_list[new_idx] + + # 记录到tray_usage, 标记已用坑位数以避免被复用 + if tray_type_code not in tray_usage: + tray_usage[tray_type_code] = [] + tray_usage[tray_type_code].append( + [position, shelf_position, tray_amount, full_tray_capacity, new_idx] + ) + + slot_content = str(tray_amount) + + tray_key = f"{position}_{shelf_position}" + if tray_key not in position_groups: + position_groups[tray_key] = { + "position": position, + "tray_type": tray_type_name, + "contents": [], + "shelf_position": shelf_position, + "storages": [] + } + + position_groups[tray_key]["contents"].append(slot_content) + position_groups[tray_key]["storages"].append(f"{consumable_display_name}|耗材库") + logger.info( + "添加耗材上料项(反应试管): %s -> %s, 选定规格 %s, 本盘数量 %s(需求 %s)", + substance, position, chosen_spec, tray_amount, consumable_count, + ) + + continue + + # 其他耗材: 按满盘分配 + try: + position, shelf_position, slot_name = _allocate_slot(tray_type_code, tray_type_name) + except ValueError as e: + logger.warning(f"无法分配坑位: {e}, 跳过耗材 {substance}") + break + + # 耗材的content格式: 满盘数量 + slot_content = str(full_tray_capacity) + + # 按位置+货架分组(同一 TB 位在不同轮次对应不同货架, 需区分) + tray_key = f"{position}_{shelf_position}" + if tray_key not in position_groups: + position_groups[tray_key] = { + "position": position, + "tray_type": tray_type_name, + "contents": [], + "shelf_position": shelf_position, + "storages": [] + } + + position_groups[tray_key]["contents"].append(slot_content) + position_groups[tray_key]["storages"].append(f"{consumable_display_name}|耗材库") + logger.info( + "添加耗材上料项: %s -> %s, 标准名=%s, 满盘数量 %s(需求 %s)", + substance, + position, + consumable_display_name, + full_tray_capacity, + consumable_count, + ) + + if len(unrecognized_consumable_names) > 0: + unknown_consumables = sorted(set(unrecognized_consumable_names)) + supported_consumables = sorted(set(CONSUMABLE_CODE_DISPLAY_NAME.values())) + raise ValidationError( + "检测到无法识别的耗材类型: " + + ", ".join(unknown_consumables) + + ". 请先在constants.py的CONSUMABLE_ALIAS_TO_CODE中注册后再试. " + + "当前支持的标准耗材: " + + ", ".join(supported_consumables) + ) + + # 6. 合并同一位置的内容并生成最终数据 + # 保持首次分配顺序, 避免第二轮复用的 TB 位被字符串排序插回第一轮. + batch_in_data = [] + for group in position_groups.values(): + # 用分号连接同一料盘的所有坑位 + combined_content = ";".join(group["contents"]) + combined_storage = ";".join(group["storages"]) + batch_in_data.append({ + "position": group["position"], + "tray_type": group["tray_type"], + "content": combined_content, + "shelf_position": group["shelf_position"], + "storage": combined_storage + }) + + # 7. 写入batch_in_tray.xlsx + if not batch_in_data: + logger.warning("没有生成任何上料数据") + return + + batch_in_path = MODULE_ROOT / "sheet" / "batch_in_tray.xlsx" + + # 检查模板是否存在,不存在则生成 + if not batch_in_path.exists(): + logger.info(f"未找到上料模板,正在生成: {batch_in_path}") + self._generate_batch_in_tray_template(batch_in_path) + + # 读取现有模板并选择目标sheet + wb = load_workbook(batch_in_path) + try: + try: + ws, _, _ = self._select_batch_in_sheet(wb) + except ValueError: + if "batch_in_tray" in wb.sheetnames: + ws = wb["batch_in_tray"] + else: + ws = wb.create_sheet("batch_in_tray") + ws.cell(row=1, column=1, value="position") + ws.cell(row=1, column=2, value="tray_type") + ws.cell(row=1, column=3, value="content") + ws.cell(row=1, column=4, value="shelf_position") + ws.cell(row=1, column=5, value="storage") + + # 清除现有数据(保留表头,从第2行开始清除) + max_row = ws.max_row + if max_row > 1: + ws.delete_rows(2, max_row - 1) + + # 写入新数据(从第2行开始) + for idx, item in enumerate(batch_in_data, start=2): + ws.cell(row=idx, column=1, value=item["position"]) + ws.cell(row=idx, column=2, value=item["tray_type"]) + ws.cell(row=idx, column=3, value=item["content"]) + ws.cell(row=idx, column=4, value=item["shelf_position"]) + ws.cell(row=idx, column=5, value=item["storage"]) + + # 保存文件 + safe_workbook_save(wb, batch_in_path) + finally: + wb.close() + + logger.info(f"已生成上料文件: {batch_in_path}, 共{len(batch_in_data)}行, 包含{sum(len(g['contents']) for g in position_groups.values())}个物资") + logger.info(f"请检查文件并根据需要调整") + + # 自动打印试剂名称标签 + try: + self.print_reagent_labels() + except Exception as exc: + logger.warning("自动打印试剂标签失败(不影响上料文件生成): %s", exc) + + # ---------- 4.5 标签打印 ---------- + + def _get_label_printer(self): + """ + 功能: + 延迟创建并返回标签打印服务实例. + 返回: + LabelPrintService 实例. + """ + if not hasattr(self, "_label_printer") or self._label_printer is None: + from ..printer import LabelPrintService + config_path = MODULE_ROOT / "printer" / "25x10x2.yaml" + self._label_printer = LabelPrintService(str(config_path)) + return self._label_printer + + def print_reagent_labels(self) -> None: + """ + 功能: + 从上料文件(batch_in_tray.xlsx)中提取试剂名称, 打印试剂标签. + 只打印试剂(固体/液体), 不打印耗材. + 通过判断content字段是否包含"|"来区分试剂和耗材. + """ + batch_in_path = MODULE_ROOT / "sheet" / "batch_in_tray.xlsx" + if not batch_in_path.exists(): + raise FileNotFoundError(f"未找到上料文件: {batch_in_path}") + + wb = load_workbook(batch_in_path, read_only=True) + try: + # 定位 sheet + ws = None + for name in wb.sheetnames: + if "batch_in_tray" in name.lower(): + ws = wb[name] + break + if ws is None: + ws = wb.active + + # 解析表头, 找到 content 列 + headers = [cell.value for cell in next(ws.iter_rows(min_row=1, max_row=1))] + if "content" not in headers: + raise ValueError("上料文件缺少 content 列") + content_col_idx = headers.index("content") + + # 提取试剂名称 (含"|"的条目为试剂行, 第二个字段为试剂名) + reagent_names = [] + seen = set() + for row in ws.iter_rows(min_row=2): + cell_value = row[content_col_idx].value + if cell_value is None: + continue + content_str = str(cell_value).strip() + # 多个坑位用";"分隔 + for slot_entry in content_str.split(";"): + slot_entry = slot_entry.strip() + if "|" not in slot_entry: + continue # 耗材行, 跳过 + parts = slot_entry.split("|") + if len(parts) >= 2: + substance = parts[1].strip() + if substance and substance not in seen: + reagent_names.append(substance) + seen.add(substance) + finally: + wb.close() + + if not reagent_names: + logger.info("上料文件中没有试剂条目, 无需打印标签") + return + + logger.info("从上料文件中提取到 %d 种试剂: %s", len(reagent_names), ", ".join(reagent_names)) + + # 打印标签 + printer = self._get_label_printer() + try: + printer.connect() + printer.print_batch(reagent_names) + finally: + printer.disconnect() + + logger.info("试剂标签打印完成") + + def print_task_number_labels(self, task_id: int) -> None: + """ + 功能: + 根据实验方案文件打印两组编号标签: + - 反应管标签: R{task_id}-1, R{task_id}-2, ..., R{task_id}-N + - 检测样品标签: S{task_id}-1, S{task_id}-2, ..., S{task_id}-M + N 由实验编号最大值决定, M 由闪滤实验编号决定(空=全部). + 参数: + task_id: int, 任务ID. + """ + # 1. 查找实验方案文件 + plan_path = MODULE_ROOT / "data" / "tasks" / str(task_id) / f"{task_id}_experiment_plan.xlsx" + if not plan_path.exists(): + raise FileNotFoundError(f"未找到实验方案文件: {plan_path}") + + wb = load_workbook(plan_path, read_only=True) + try: + # 定位实验方案 sheet (第一个 sheet, 不用 wb.active 因为活动页可能是其他 sheet) + ws = wb.worksheets[0] + + # 2. 读取实验编号 (列C, 从第2行开始) + experiment_numbers = [] + for row in ws.iter_rows(min_row=2, min_col=3, max_col=3): + val = row[0].value + if val is not None: + try: + # 兼容 int/float/str 混合类型 + num = int(float(str(val))) + experiment_numbers.append(num) + except (ValueError, TypeError): + pass + + if not experiment_numbers: + raise ValueError(f"任务 {task_id} 的实验方案文件中未找到实验编号") + + max_exp_num = max(experiment_numbers) + logger.info("任务 %d: 实验编号 1~%d", task_id, max_exp_num) + + # 3. 读取闪滤实验编号 (行29, 列B) + flash_filter_cell = ws.cell(row=29, column=2).value + if flash_filter_cell is None or str(flash_filter_cell).strip() == "": + # 空值: 所有实验都需要闪滤 -> 样品编号 = 全部实验编号 + sample_numbers = list(range(1, max_exp_num + 1)) + logger.info("闪滤实验编号为空, 全部 %d 个实验需要样品标签", max_exp_num) + else: + # 解析闪滤编号 (支持 "1,3,5" 或 "1-6" 或混合 "1-3,5,7-9") + sample_numbers = self._parse_number_range(str(flash_filter_cell).strip()) + logger.info("闪滤实验编号: %s -> 样品标签 %d 张", flash_filter_cell, len(sample_numbers)) + finally: + wb.close() + + # 4. 生成标签内容 + reaction_labels = [f"R{task_id}-{i}" for i in range(1, max_exp_num + 1)] + sample_labels = [f"S{task_id}-{i}" for i in sample_numbers] + + printer = self._get_label_printer() + try: + printer.connect() + + # 5. 一次确认后连续打印两组标签 + print(f"\n即将打印标签:") + print(f" 反应管: {len(reaction_labels)} 张 ({reaction_labels[0]} ~ {reaction_labels[-1]})") + print(f" 检测样品: {len(sample_labels)} 张 ({sample_labels[0]} ~ {sample_labels[-1]})") + input("请确认打印机就绪, 按回车开始打印...") + + logger.info("正在打印反应管标签: %d 张", len(reaction_labels)) + printer.print_batch(reaction_labels) + logger.info("反应管标签打印完成") + + logger.info("正在打印检测样品标签: %d 张", len(sample_labels)) + printer.print_batch(sample_labels) + logger.info("检测样品标签打印完成") + finally: + printer.disconnect() + + @staticmethod + def _parse_number_range(range_str: str) -> List[int]: + """ + 功能: + 解析数字范围字符串, 支持逗号分隔和连字符范围. + 例: "1,3,5" -> [1,3,5], "1-6" -> [1,2,3,4,5,6], "1-3,5,7-9" -> [1,2,3,5,7,8,9] + 参数: + range_str: str, 数字范围字符串. + 返回: + List[int], 排序后的编号列表. + """ + import re + numbers = set() + # 按逗号或中文逗号分隔 + parts = re.split(r"[,,]", range_str) + for part in parts: + part = part.strip() + if not part: + continue + # 检查是否包含范围连字符 + range_match = re.match(r"(\d+)\s*[-~]\s*(\d+)", part) + if range_match: + start = int(range_match.group(1)) + end = int(range_match.group(2)) + numbers.update(range(start, end + 1)) + else: + try: + numbers.add(int(part)) + except ValueError: + pass + return sorted(numbers) + + # ---------- 5. 执行任务 ---------- + def device_init(self, device_id=None, *, poll_interval_s: float = 1.0, timeout_s: float = 600.0): + return super().device_init(device_id, poll_interval_s=poll_interval_s, timeout_s=timeout_s) + + def start_task(self, task_id: int | None = None, *, check_glovebox_env: bool = True, water_limit_ppm: float = 10.0, oxygen_limit_ppm: float = 10.0): + return super().start_task(task_id, check_glovebox_env=check_glovebox_env, water_limit_ppm=water_limit_ppm, oxygen_limit_ppm=oxygen_limit_ppm) + + def wait_task_with_ops(self, task_id: int | None = None, *, poll_interval_s: float = 2.0) -> int: + return super().wait_task_with_ops(task_id, poll_interval_s=poll_interval_s) + + def export_task_report(self, task_id: int, file_type: str = "excel") -> Path: + return super().export_task_report(task_id, file_type=file_type) + + # ---------- 6. 下料动作 ---------- + def batch_out_task_and_empty_trays(self, task_id: int | None = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + return super().batch_out_task_and_empty_trays(task_id, poll_interval_s=poll_interval_s, ignore_missing=ignore_missing, timeout_s=timeout_s, move_type=move_type) + + def batch_out_task_and_chemical_trays(self, task_id: int | None = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + return super().batch_out_task_and_chemical_trays(task_id, poll_interval_s=poll_interval_s, ignore_missing=ignore_missing, timeout_s=timeout_s, move_type=move_type) + + def batch_out_task_trays(self, task_id: int | None = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + return super().batch_out_task_trays(task_id, poll_interval_s=poll_interval_s, ignore_missing=ignore_missing, timeout_s=timeout_s, move_type=move_type) + + def batch_out_empty_trays(self, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + return super().batch_out_empty_trays(poll_interval_s=poll_interval_s, ignore_missing=ignore_missing, timeout_s=timeout_s, move_type=move_type) + + def batch_out_tray(self, layout_list: list[dict], move_type: str = "main_out", *, task_id: int = None, poll_interval_s: float = 1.0, timeout_s: float = 900.0): + return super().batch_out_tray(layout_list, move_type=move_type, task_id=task_id, poll_interval_s=poll_interval_s, timeout_s=timeout_s) + + def auto_unload_trays_to_agv(self, batch_out_file: Optional[str] = None, *, block: bool = True, auto_run_analysis: bool = True): + return super().auto_unload_trays_to_agv(batch_out_file, block=block, auto_run_analysis=auto_run_analysis) + + # ---------- 7. Unilab 接口(待修改) ---------- + def submit_experiment_task( + self, + chemical_db_path: str, + task_name: str = "Unilab_Auto_Job", + reaction_type: str = "heat", + duration: str = "8", + temperature: str = "40", + stir_speed: str = "500", + target_temp: str = "30", + auto_magnet: bool = True, + fixed_order: bool = False, + internal_std_name: str = "", + stir_time_after_std: str = "", + diluent_name: str = "", + rows: list = None + ) -> JsonDict: + """ + 功能: + 提交 Unilab 流程编排任务, 按行数据动态生成表头, 兼容包含“加磁子”的列. + 参数: + chemical_db_path: str, 化学品库文件路径. + task_name: str, 任务名称. + reaction_type: str, 反应类型. + duration: str, 反应时间, 必须带单位, 如 "8h" 或 "30min". + temperature: str, 反应温度(°C). + stir_speed: str, 搅拌速度(rpm). + target_temp: str, 搅拌后目标温度(°C). + auto_magnet: bool, 是否自动加磁子. + fixed_order: bool, 是否固定加料顺序. + internal_std_name: str, 内标名称. + stir_time_after_std: str, 内标加入后搅拌时间(min). + diluent_name: str, 稀释液名称. + rows: List[List[Any]], 行数据矩阵, 第1列为实验编号, 其余列为试剂或“加磁子”. + 返回: + Dict[str, Any], 提交成功后返回的任务 ID. + """ + c_path = Path(chemical_db_path) + if c_path.exists() is False: + raise FileNotFoundError(f"未找到化学品库文件: {c_path}") + + chem_df = self._read_table_file_with_required_columns( + c_path, + required_columns=["substance"], + ) + 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 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(), + "density (g/mL)": _pick(row, "density (g/ml)", "density(g/ml)", "density_g_ml", "density", default=None), + "physical_form": str(_pick(row, "physical_form", default="") or "").strip().lower(), + "active_content": _pick(row, "active_content", "active_content(mmol/ml or wt%)", "active_content(mol/l or wt%)", default=""), + } + + if auto_magnet is True: + auto_magnet_text = "是" + else: + auto_magnet_text = "否" + + if fixed_order is True: + fixed_order_text = "是" + else: + fixed_order_text = "否" + + params = { + "实验名称": task_name, + "反应器类型": reaction_type, + "反应时间(min/h)": duration, + "反应温度(°C)": temperature, + "转速(rpm)": stir_speed, + "搅拌后目标温度(°C)": target_temp, + "自动加磁子": auto_magnet_text, + "固定加料顺序": fixed_order_text, + "内标种类": internal_std_name, + "加入内标后搅拌时间(min)": stir_time_after_std, + "稀释液种类": diluent_name, + } + + if rows is None: + rows = [] + + default_pair_count = 5 + cleaned_rows: List[List[Any]] = [] + magnet_columns: set[int] = set() + max_col_count = 1 + + for row in rows: + if isinstance(row, (list, tuple)) is False: + logger.warning("行数据格式需要列表或元组, 已跳过一行") + continue + row_values = list(row) + while len(row_values) > 0: + tail_text = "" if row_values[-1] is None else str(row_values[-1]).strip() + if tail_text == "": + row_values.pop() + continue + break + if len(row_values) > max_col_count: + max_col_count = len(row_values) + for col_index, cell in enumerate(row_values): + cell_text = "" if cell is None else str(cell).strip() + if "加磁子" in cell_text: + magnet_columns.add(col_index) + cleaned_rows.append(row_values) + + if len(cleaned_rows) == 0: + header_count = 1 + default_pair_count * 2 + else: + header_count = max_col_count + + headers: List[str] = ["实验编号"] + for col_index in range(1, header_count): + if col_index in magnet_columns: + headers.append("加磁子") + elif col_index % 2 == 1: + headers.append("试剂") + else: + headers.append("试剂量") + + normalized_rows: List[List[Any]] = [] + for row_values in cleaned_rows: + padded_values = row_values + [""] * (header_count - len(row_values)) + normalized_rows.append(padded_values) + + try: + task_payload = self.build_task_payload(params, headers, normalized_rows, chemical_db) + except AttributeError as exc: + raise Exception("无法找到 build_task_payload 方法, 请检查 StationController 定义") from exc + + try: + resp = self._submit_task_payload(task_payload) + except ApiError as exc: + if getattr(exc, "code", None) == 409: + # 自动重命名: 在任务名称后添加当前日期时间(精确到秒) + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + + task_name_val = task_payload.get("task_name") or params.get("实验名称") + new_task_name = f"{task_name_val}_{timestamp}" + + task_payload["task_name"] = new_task_name + logger.info(f"任务名称重复, 自动重命名为: {new_task_name}") + + # 重试提交 + try: + resp = self._submit_task_payload(task_payload) + except ApiError as retry_exc: + logger.error(f"重命名后任务提交仍失败: {retry_exc}") + raise + else: + raise + + task_id = resp.get("task_id") + return task_id + + # ---------- 分析站对接 ---------- + def run_analysis(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + 读取指定合成任务(或最新任务)的 xlsx 配置, 自动生成分析任务 CSV + 并通过 AnalysisStationController 提交至对应仪器(当前已实现 GC_MS). + 参数: + task_id: 合成任务 ID 字符串, 为 None 时自动选取编号最大的最近任务. + 返回: + Dict: 各仪器提交结果, 格式示例: + { + "gc_ms": {"success": bool, "return_info": str}, + "uplc_qtof": {"success": bool, "return_info": str}, + "hplc": {"success": bool, "return_info": str}, + } + """ + # 延迟绝对导入,避免模块级相对导入越界问题 + # 运行目录为 devices/,eit_analysis_station 可直接作为顶层包访问 + from unilabos.devices.eit_analysis_station.controller.analysis_controller import AnalysisStationController + + # 创建分析站控制器实例,使用其默认配置 + analysis_ctrl = AnalysisStationController() + + logger.info("启动分析任务提交流程, task_id=%s", task_id) + results = analysis_ctrl.run_analysis(task_id=task_id) + return results + + def poll_analysis_run( + self, task_id: Optional[str] = None, poll_interval: float = 30.0 + ) -> Dict: + """ + 功能: + 轮询 GC-MS 分析任务运行状态, 完成后自动触发结果处理(积分+定性+报告). + 内部委托给 AnalysisStationController.poll_analysis_run 执行. + 参数: + task_id: 合成任务 ID 字符串, 为 None 时自动选取编号最大的最近任务. + poll_interval: 轮询间隔(秒), 默认 30 秒. + 返回: + Dict: process_gc_ms_results 的返回值, 包含 success/return_info/report_path. + """ + from unilabos.devices.eit_analysis_station.controller.analysis_controller import AnalysisStationController + + analysis_ctrl = AnalysisStationController() + + logger.info("启动 GC-MS 轮询流程, task_id=%s, poll_interval=%.0fs", task_id, poll_interval) + result = analysis_ctrl.poll_analysis_run(task_id=task_id, poll_interval=poll_interval) + return result diff --git a/unilabos/devices/eit_synthesis_station/manager/station_manager_fack.py b/unilabos/devices/eit_synthesis_station/manager/station_manager_fack.py new file mode 100644 index 00000000..74418800 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/manager/station_manager_fack.py @@ -0,0 +1,870 @@ + # -*- coding: utf-8 -*- +import csv +import re +import logging +import shutil +import time +import pandas as pd +import openpyxl +from datetime import datetime +from openpyxl import Workbook,load_workbook +from openpyxl.worksheet.datavalidation import DataValidation +from openpyxl.workbook.defined_name import DefinedName +from openpyxl.styles import Font, Alignment, NamedStyle +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 ( + CONSUMABLE_ALIAS_TO_CODE, + CONSUMABLE_CODE_DISPLAY_NAME, + CONSUMABLE_CODE_TO_TRAY_CODE, + ResourceCode, + TRAY_CODE_DISPLAY_NAME, + TraySpec, +) +from .synchronizer import EITSynthesisWorkstation + +from ..driver.exceptions import ValidationError,ApiError +from ..utils.file_utils import safe_excel_write, safe_workbook_save +from ..chem_tools.chemical_append_utils import ( + build_append_row_data, + build_append_row_data_for_smiles, + build_prepared_chemical_row_data, + build_duplicate_check_specs, + collect_missing_append_headers, + get_excel_write_value, + save_chemicalbook_record, +) + +logger = logging.getLogger("StationManager") + +JsonDict = Dict[str, Any] + +# 模块根目录, 用于构建相对路径 +MODULE_ROOT = Path(__file__).resolve().parent.parent + +class SynthesisStationManager(EITSynthesisWorkstation, SynthesisStationController): + """ + 功能: + 上层面向用户的管理器,继承自 SynthesisStationController。 + 负责处理 CSV/Excel 文件读取、生成模板,将文件内容转换为中间格式(List/Dict), + 然后调用父类方法执行具体的业务逻辑。 + """ + + def __init__( + self, + settings: Optional[Settings] = None, + config: Optional[Dict[str, Any]] = None, + deck: Optional[Any] = None, + **kwargs, + ): + settings = settings or Settings.from_env() + configure_logging(settings.log_level) + SynthesisStationController.__init__(self, settings) + EITSynthesisWorkstation.__init__( + self, + config=config, + deck=deck, + controller=self, + **kwargs, + ) + + def login(self) -> tuple: + """ + 功能: + 登录并缓存 token, 登录成功后根据配置自动启动异常通知监控. + 参数: + 无. + 返回: + Tuple[str, str], (token_type, access_token). + """ + logger.info("虚假执行 login") + time.sleep(5) + return ("Bearer", "fake_token") + + def _read_table_file_with_required_columns( + self, + path: Path, + *, + required_columns: Optional[List[str]] = None, + preferred_sheet_name: Optional[str] = None, + ) -> pd.DataFrame: + """ + 功能: + 读取 CSV/Excel 文件. + 当为 Excel 且存在多工作表时, 根据必需列选择工作表. + 参数: + path: 文件路径. + required_columns: 必需列名列表, None 表示直接读取默认表. + preferred_sheet_name: 优先工作表名. + 返回: + DataFrame, 读取后的表格数据. + """ + logger.info("虚假执行 _read_table_file_with_required_columns, path=%s", path) + time.sleep(5) + return pd.DataFrame() + + # ---------- 1. 化合物库文件处理 ---------- + def export_chemical_list_to_file(self, output_path: str) -> None: + """ + 功能: + 获取所有化学品并导出到 CSV 文件 + 参数: + output_path: 输出路径 + 返回: + None + """ + logger.info("虚假执行 export_chemical_list_to_file, output_path=%s", output_path) + time.sleep(5) + + def sync_chemicals_from_file(self, file_path: str, overwrite: bool = False) -> None: + """ + 功能: + 读取 CSV 文件并通过父类同步化学品到工站 + 参数: + file_path: CSV 文件路径 + overwrite: 是否覆盖更新 + 返回: + None + """ + logger.info("虚假执行 sync_chemicals_from_file, file_path=%s, overwrite=%s", file_path, overwrite) + time.sleep(5) + + 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 + """ + logger.info("虚假执行 check_chemical_library_by_file, file_path=%s", file_path) + time.sleep(5) + return {"errors": [], "warnings": []} + + 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]], 去重后的数据 + """ + logger.info("虚假执行 deduplicate_chemical_library_by_file, file_path=%s, output_path=%s", file_path, output_path) + time.sleep(5) + return [] + + def _beautify_excel_database(self, file_path: Path) -> None: + """ + 功能: + 美化去重后的 Excel: 表头加粗、全居中、列宽自适应、按内容选择中英文字体 + 参数: + file_path: Path, 目标 Excel 路径 + 返回: + None + """ + logger.info("虚假执行 _beautify_excel_database, file_path=%s", file_path) + time.sleep(5) + + def align_chemicals_with_file(self, file_path: str, auto_delete: bool = True) -> None: + """ + 功能: + 读取 Excel/CSV 文件,调用父类对齐逻辑,并将结果(fid)写回文件 + 参数: + file_path: 文件路径 + auto_delete: 是否删除不在文件中的工站化学品 + 返回: + None + """ + logger.info("虚假执行 align_chemicals_with_file, file_path=%s, auto_delete=%s", file_path, auto_delete) + time.sleep(5) + + @staticmethod + def _has_any_append_core_value(row_data: Dict[str, Any]) -> bool: + """ + 功能: + 判断追加行是否至少包含一个可用于识别化合物的核心字段. + 参数: + row_data: Dict[str, Any], 准备写入 Excel 的行数据. + 返回: + bool, True 表示至少包含 CAS, 英文名, 中文名中的一个. + """ + logger.info("虚假执行 _has_any_append_core_value") + time.sleep(5) + return False + + @staticmethod + def _format_append_row_summary(row_data: Dict[str, Any]) -> str: + """ + 功能: + 提取 chemical list 关键字段, 生成统一的日志摘要文本. + 参数: + row_data: Dict[str, Any], 追加到 chemical list 的行数据. + 返回: + str, 包含 substance, physical_state, physical_form 的摘要文本. + """ + logger.info("虚假执行 _format_append_row_summary") + time.sleep(5) + return "虚假摘要" + + def _resolve_append_excel_path(self, excel_path: Optional[str]) -> Path: + """ + 功能: + 解析化学品追加流程使用的目标 Excel 路径. + 参数: + excel_path: Optional[str], 用户指定路径, None 时使用默认库文件. + 返回: + Path, 已解析的 Excel 路径. + 异常: + FileNotFoundError: 目标文件不存在时抛出. + """ + logger.info("虚假执行 _resolve_append_excel_path, excel_path=%s", excel_path) + time.sleep(5) + return MODULE_ROOT / "sheet" / "fake_append.xlsx" + + @staticmethod + def _build_append_header_map(worksheet: Any) -> Dict[str, int]: + """ + 功能: + 从化学品库工作表首行构建表头到列号的映射. + 参数: + worksheet: Any, openpyxl 工作表对象. + 返回: + Dict[str, int], 表头名称到列号的映射. + """ + logger.info("虚假执行 _build_append_header_map") + time.sleep(5) + return {} + + @staticmethod + def _build_append_row_snapshot( + worksheet: Any, + header_map: Dict[str, int], + row_index: int, + ) -> Dict[str, Any]: + """ + 功能: + 从化学品库工作表中提取单行数据快照, 统一补齐常用别名字段. + 参数: + worksheet: Any, openpyxl 工作表对象. + header_map: Dict[str, int], 表头名称到列号映射. + row_index: int, 目标行号. + 返回: + Dict[str, Any], 单行字段字典. + """ + logger.info("虚假执行 _build_append_row_snapshot") + time.sleep(5) + return {} + + def _find_existing_chemical_row( + self, + *, + excel_path: Optional[str] = None, + cas_number: str = "", + substance_english_name: str = "", + substance: str = "", + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 按 CAS, 英文名, 中文名顺序在化学品库中查找已有条目, 优先返回 neat 行. + 参数: + excel_path: Optional[str], 化学品库文件路径. + cas_number: str, 候选 CAS 号. + substance_english_name: str, 候选英文名. + substance: str, 候选中文名或展示名. + 返回: + Optional[Dict[str, Any]], 命中时返回包含 row_data 与 row_index 的结果字典. + """ + logger.info("虚假执行 _find_existing_chemical_row") + time.sleep(5) + return None + + @staticmethod + def _parse_positive_float(value: Any, field_name: str) -> float: + """ + 功能: + 将输入值解析为大于 0 的浮点数. + 参数: + value: Any, 待解析的数值. + field_name: str, 字段中文名, 用于异常提示. + 返回: + float, 解析后的正数. + 异常: + ValidationError: 字段为空, 非数字或不大于 0 时抛出. + """ + logger.info("虚假执行 _parse_positive_float, field_name=%s", field_name) + time.sleep(5) + return 0.0 + + @staticmethod + def _format_preparation_number(value: float) -> str: + """ + 功能: + 将配液或称量结果格式化为紧凑展示文本. + 参数: + value: float, 原始数值. + 返回: + str, 去除多余尾零后的文本. + """ + logger.info("虚假执行 _format_preparation_number") + time.sleep(5) + return "0" + + def _build_solution_recipe( + self, + base_row_data: Dict[str, Any], + concentration_mol_l: float, + target_volume_ml: float, + solvent_name: str, + ) -> Dict[str, Any]: + """ + 功能: + 根据母体化合物信息生成溶液配置结果. + 参数: + base_row_data: Dict[str, Any], 母体化合物行数据. + concentration_mol_l: float, 目标浓度, 单位 mol/L. + target_volume_ml: float, 目标定容体积, 单位 mL. + solvent_name: str, 溶剂名称. + 返回: + Dict[str, Any], 包含质量, 体积与展示文案的结果字典. + 异常: + ValidationError: 缺少分子量时抛出. + """ + logger.info("虚假执行 _build_solution_recipe") + time.sleep(5) + return {} + + def _build_beads_recipe( + self, + base_row_data: Dict[str, Any], + wt_percent: float, + target_active_mmol: float, + ) -> Dict[str, Any]: + """ + 功能: + 根据母体化合物信息生成 beads 称量结果. + 参数: + base_row_data: Dict[str, Any], 母体化合物行数据. + wt_percent: float, 有效成分质量分数. + target_active_mmol: float, 目标活性摩尔数, 单位 mmol. + 返回: + Dict[str, Any], 包含 beads 质量与展示文案的结果字典. + 异常: + ValidationError: 缺少分子量时抛出. + """ + logger.info("虚假执行 _build_beads_recipe") + time.sleep(5) + return {} + + def _resolve_prepared_base_chemical( + self, + identifier: str, + excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 为溶液或 beads 配置流程解析母体化合物, 不存在时自动补录 neat 条目. + 参数: + identifier: str, CAS 或 SMILES. + excel_path: Optional[str], 化学品库文件路径. + 返回: + Optional[Dict[str, Any]], 成功时返回母体行数据与来源信息, 失败时返回 None. + """ + logger.info("虚假执行 _resolve_prepared_base_chemical") + time.sleep(5) + return {} + + @staticmethod + def _find_duplicate_append_row( + worksheet: Any, + header_map: Dict[str, int], + row_data: Dict[str, Any], + ) -> Optional[Tuple[str, str, str, int]]: + """ + 功能: + 按约定优先级在 Excel 中查找重复化合物. + 参数: + worksheet: Any, openpyxl 工作表对象. + header_map: Dict[str, int], 表头名称到列号映射. + row_data: Dict[str, Any], 准备写入的行数据. + 返回: + Optional[Tuple[str, str, str, int]], 命中时返回 + (字段标签, 目标值, 表头名, 行号), 否则返回 None. + """ + logger.info("虚假执行 _find_duplicate_append_row") + time.sleep(5) + return None + + def _append_chemical_row_to_excel( + self, + row_data: Dict[str, Any], + excel_path: Optional[str] = None, + ) -> Optional[int]: + """ + 功能: + 将单条化学品行数据追加到 Excel 末尾, 并执行重复检查. + 参数: + row_data: Dict[str, Any], 准备写入的行数据. + excel_path: Optional[str], 目标 Excel 文件路径. + 返回: + Optional[int], 成功时返回新行行号, 命中重复时返回 None. + """ + logger.info("虚假执行 _append_chemical_row_to_excel") + time.sleep(5) + + def _fetch_chemicalbook_append_artifacts( + self, + resolved_cas: str, + ) -> Tuple[Optional[Dict[str, Any]], str, str]: + """ + 功能: + 根据已解析的 CAS 获取 ChemicalBook 结构化结果及 sidecar 路径. + 参数: + resolved_cas: str, 已解析出的 CAS 号. + 返回: + Tuple[Optional[Dict[str, Any]], str, str], 依次为 + chemicalbook_record, chemicalbook_status, chemicalbook_record_path. + """ + logger.info("虚假执行 _fetch_chemicalbook_append_artifacts") + time.sleep(5) + return {} + + def lookup_and_append_chemical( + self, query: str, excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 在线查询化合物信息并追加到化学品库 Excel 文件末尾. + 查询分为两层: 先用多源核心查询获取可用基础信息, 再用 ChemicalBook + 结构化抓取补充全量数据, 并将原始结果保存为 sidecar JSON. + 参数: + query: str, CAS 号或化合物中英文名称. + excel_path: Optional[str], 目标 Excel 文件路径, 默认为 sheet/chemical_list.xlsx. + 返回: + Optional[Dict[str, Any]], 成功返回稳定结果字典, 包含 row_data, row_index, + chemicalbook_status, chemicalbook_record_path. 查询失败或重复时返回 None. + """ + logger.info("虚假执行 lookup_and_append_chemical") + time.sleep(5) + return {"success": True, "message": "虚假找测并追加完成"} + + def lookup_and_append_chemical_by_smiles( + self, smiles: str, excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 根据单个完整 SMILES 在线查询化合物信息, 并追加到化学品库 Excel 文件末尾. + 查询顺序固定为 PubChem 结构查询, 拿到 CAS 后再补 Common Chemistry + 与 ChemicalBook. + 参数: + smiles: str, 单个完整 SMILES 结构式. + excel_path: Optional[str], 目标 Excel 文件路径, 默认为 sheet/chemical_list.xlsx. + 返回: + Optional[Dict[str, Any]], 成功返回稳定结果字典, 包含 row_data, row_index, + chemicalbook_status, chemicalbook_record_path. 查询失败或重复时返回 None. + """ + logger.info("虚假执行 lookup_and_append_chemical_by_smiles") + time.sleep(5) + return {"success": True, "message": "虚假 SMILES 追加完成"} + + def prepare_solution_or_beads( + self, + identifier: str, + prepared_form: str, + *, + solvent_name: str = "", + active_content: Any, + target_volume_ml: Optional[Any] = None, + target_active_mmol: Optional[Any] = None, + excel_path: Optional[str] = None, + ) -> Optional[Dict[str, Any]]: + """ + 功能: + 根据 CAS 或 SMILES 解析母体化合物, 按指定形态生成溶液或 beads 条目并追加到化学品库. + 参数: + identifier: str, 母体化合物 CAS 或 SMILES. + prepared_form: str, 派生形态, 支持 solution 或 beads. + solvent_name: str, solution 使用的溶剂名称. + active_content: Any, solution 时表示 mol/L, beads 时表示 wt%. + target_volume_ml: Optional[Any], solution 目标定容体积, 单位 mL. + target_active_mmol: Optional[Any], beads 目标活性摩尔数, 单位 mmol. + excel_path: Optional[str], 目标 Excel 文件路径. + 返回: + Optional[Dict[str, Any]], 成功返回母体信息, 派生条目信息与配制结果摘要. + 派生条目重复或母体无法解析时返回 None. + 异常: + ValidationError: 形态或数值参数非法时抛出. + """ + logger.info("虚假执行 prepare_solution_or_beads") + time.sleep(5) + return {"success": True, "message": "虚假配制完成"} + + # ---------- 2. 上料动作 ---------- + + def _read_batch_in_records(self, file_path: str) -> List[Dict[str, str]]: + """ + 功能: + 读取上料表格文件(xlsx/csv), 返回标准化记录列表. + 参数: + file_path: str, 上料文件路径. + 返回: + List[Dict[str, str]], 包含 position, tray_type, content, + shelf_position, storage 字段的记录列表. + 异常: + FileNotFoundError: 文件不存在时自动生成模板并抛出. + """ + logger.info("虚假执行 _read_batch_in_records, file_path=%s", file_path) + time.sleep(5) + return [] + + def batch_in_tray_by_file(self, file_path: str) -> JsonDict: + """ + 功能: + 读取上料表格, 转换为中间格式, 调用父类生成 Payload 并执行上料 + 参数: + file_path: 文件路径 + 返回: + Dict: API 响应 + """ + logger.info("虚假执行 batch_in_tray_by_file, file_path=%s", file_path) + time.sleep(5) + return {"success": True, "message": "虚假批量入盘完成"} + + def batch_in_tray_with_agv_transfer( + self, + file_path: str = None, + *, + block: bool = True, + chamber_capacity: int = 8, + ) -> JsonDict: + """ + 功能: + 根据 batch_in_tray.xlsx 中的信息, 分轮次(每轮最多 chamber_capacity 个托盘) + 执行: 开过渡舱门 -> AGV 转运 -> 机器人上料. + 全部轮次结束后 AGV 前往充电站. + 当总托盘数 <= chamber_capacity 时, 行为与旧版本一致(单轮). + 参数: + file_path: 上料文件路径, 默认为 sheet/batch_in_tray.xlsx + block: 是否阻塞等待 AGV 转运完成 + chamber_capacity: 过渡舱单次最大容纳托盘数, 默认 8 + 返回: + Dict, 包含多轮次的聚合结果: + - success: bool, 是否全部轮次成功 + - total_trays: int, 总托盘数 + - transferred_trays: int, 成功转运的托盘数 + - loaded_trays: int, 成功上料的托盘数 + - rounds: List[Dict], 每轮次详情 + - charging_result: Dict, AGV 充电结果 + - errors: List[str], 所有错误信息 + - message: str, 结果摘要 + """ + logger.info("虚假执行 batch_in_tray_with_agv_transfer, file_path=%s, block=%s, chamber_capacity=%s", file_path, block, chamber_capacity) + time.sleep(5) + return {"success": True, "total_trays": 0, "transferred_trays": 0, "loaded_trays": 0, "rounds": [], "charging_result": None, "errors": [], "message": "虚假 AGV 转运入盘完成"} + + def _validate_agv_transfer_round_records( + self, + records: List[Dict[str, str]], + *, + round_num: int, + ) -> List[str]: + """ + 功能: + 校验单轮 AGV 上料记录是否满足唯一性要求. + 同一轮次内 position 与 shelf_position 都必须唯一, 且不能为空. + 参数: + records: List[Dict[str, str]], 单轮上料记录列表. + round_num: int, 当前轮次编号. + 返回: + List[str], 校验失败时的错误信息列表. + """ + logger.info("虚假执行 _validate_agv_transfer_round_records, round_num=%s", round_num) + time.sleep(5) + return [] + + def _generate_batch_in_tray_template(self, file_path: Path) -> None: + """ + 功能: + 生成批量上料Excel模板, 配置上料点位下拉、托盘类型下拉与内容示例 + 参数: + file_path: Path, 模板输出路径 + 返回: + None + """ + logger.info("虚假执行 _generate_batch_in_tray_template, file_path=%s", file_path) + time.sleep(5) + + def _find_header_in_sheet(self, worksheet: Any, header_keyword: str) -> Tuple[Optional[int], Optional[int]]: + """ + 功能: + 在单个工作表的前 50 行和前 50 列中查找目标表头. + 参数: + worksheet: openpyxl 工作表对象. + header_keyword: 需要匹配的表头关键词, 例如"实验编号". + 返回: + Tuple[Optional[int], Optional[int]], 命中时返回(行号, 列号), 未命中返回(None, None). + """ + logger.info("虚假执行 _find_header_in_sheet, header_keyword=%s", header_keyword) + time.sleep(5) + return (None, None) + + def _select_task_template_sheet( + self, + workbook: Workbook, + header_keyword: str = "实验编号", + ) -> Tuple[Optional[Any], Optional[int], Optional[int]]: + """ + 功能: + 从任务模板工作簿中选择包含目标表头的工作表. + 选择顺序为: "实验方案设定" -> 当前激活工作表 -> 其余工作表. + 参数: + workbook: openpyxl Workbook 对象. + header_keyword: 需要匹配的表头关键词. + 返回: + Tuple[Optional[Any], Optional[int], Optional[int]], 命中时返回(工作表, 行号, 列号), 未命中返回(None, None, None). + """ + logger.info("虚假执行 _select_task_template_sheet, header_keyword=%s", header_keyword) + time.sleep(5) + return (None, None, None) + + # ---------- 3. 任务生成文件处理 ---------- + def create_task_by_file(self, template_path: str, chemical_db_path: str) -> JsonDict: + """ + 功能: + 读取任务模板和化学品库,解析为中间数据,调用父类生成任务 Payload 并提交 + 参数: + template_path: 实验模板路径 + chemical_db_path: 化学品库路径 + 返回: + Dict: 任务创建结果 + """ + logger.info("虚假执行 create_task_by_file, template_path=%s, chemical_db_path=%s", template_path, chemical_db_path) + time.sleep(5) + return "fake_task_id" + + def _generate_reaction_template(self, path: Path) -> None: + """ + 生成与 reeaction_template.xlsx 一致的反应模板 + 结构:左侧为参数配置区,右侧为实验试剂填报区 + """ + logger.info("虚假执行 _generate_reaction_template, path=%s", path) + time.sleep(5) + + # ---------- 4. 物料核算 ---------- + def check_resource_for_task(self, template_path: str, chemical_db_path: str, auto_generate_batch_file: bool = True) -> JsonDict: + """ + 功能: + 读取实验模板与化学品库, 构建任务 Payload, 获取站内资源并比对是否满足实验需求。 + 参数: + template_path: 实验模板文件路径(xlsx/csv)。 + chemical_db_path: 化学品库文件路径(xlsx/csv)。 + auto_generate_batch_file: 是否自动生成上料文件, 默认为 True。 + 返回: + Dict, analyze_resource_readiness 的结果, 包含需求、库存、缺失与冗余信息。 + """ + logger.info("虚假执行 check_resource_for_task, template_path=%s, chemical_db_path=%s, auto_generate_batch_file=%s", template_path, chemical_db_path, auto_generate_batch_file) + time.sleep(5) + return {"success": True, "resource_ready": True, "errors": [], "warnings": [], "message": "虚假资源检查完成"} + + def auto_generate_batch_in_tray_from_resource_check(self, task_id: Optional[int] = None) -> None: + """ + 功能: + 根据资源核查结果自动修改上料文件, 考虑料盘规格, 优先填满一个料盘再使用下一个 + 参数: + task_id: 任务ID, 如果为None则自动搜索data/tasks中id最大且状态为UNSTARTED的任务 + 返回: + None + """ + logger.info("虚假执行 auto_generate_batch_in_tray_from_resource_check, task_id=%s", task_id) + time.sleep(5) + + # ---------- 4.5 标签打印 ---------- + + def _get_label_printer(self): + """ + 功能: + 延迟创建并返回标签打印服务实例. + 返回: + LabelPrintService 实例. + """ + logger.info("虚假执行 _get_label_printer") + time.sleep(5) + return None + + def print_reagent_labels(self) -> None: + """ + 功能: + 从上料文件(batch_in_tray.xlsx)中提取试剂名称, 打印试剂标签. + 只打印试剂(固体/液体), 不打印耗材. + 通过判断content字段是否包含"|"来区分试剂和耗材. + """ + logger.info("虚假执行 print_reagent_labels") + time.sleep(5) + + def print_task_number_labels(self, task_id: int) -> None: + """ + 功能: + 根据实验方案文件打印两组编号标签: + - 反应管标签: R{task_id}-1, R{task_id}-2, ..., R{task_id}-N + - 检测样品标签: S{task_id}-1, S{task_id}-2, ..., S{task_id}-M + N 由实验编号最大值决定, M 由闪滤实验编号决定(空=全部). + 参数: + task_id: int, 任务ID. + """ + logger.info("虚假执行 print_task_number_labels, task_id=%s", task_id) + time.sleep(5) + + @staticmethod + def _parse_number_range(range_str: str) -> List[int]: + """ + 功能: + 解析数字范围字符串, 支持逗号分隔和连字符范围. + 例: "1,3,5" -> [1,3,5], "1-6" -> [1,2,3,4,5,6], "1-3,5,7-9" -> [1,2,3,5,7,8,9] + 参数: + range_str: str, 数字范围字符串. + 返回: + List[int], 排序后的编号列表. + """ + logger.info("虚假执行 _parse_number_range, range_str=%s", range_str) + time.sleep(5) + return [] + + # ---------- 5. 执行任务 ---------- + def device_init(self, device_id=None, *, poll_interval_s: float = 1.0, timeout_s: float = 600.0): + logger.info("虚假执行 device_init, device_id=%s, poll_interval_s=%s, timeout_s=%s", device_id, poll_interval_s, timeout_s) + time.sleep(5) + return {"success": True, "device_id": device_id, "message": "虚假设备初始化完成"} + + def start_task(self, task_id: int | None = None, *, check_glovebox_env: bool = True, water_limit_ppm: float = 10.0, oxygen_limit_ppm: float = 10.0): + logger.info("虚假执行 start_task, task_id=%s, check_glovebox_env=%s, water_limit_ppm=%s, oxygen_limit_ppm=%s", task_id, check_glovebox_env, water_limit_ppm, oxygen_limit_ppm) + time.sleep(5) + return {"success": True, "task_id": task_id, "message": "虚假任务启动完成"} + + def wait_task_with_ops(self, task_id: int | None = None, *, poll_interval_s: float = 2.0) -> int: + logger.info("虚假执行 wait_task_with_ops, task_id=%s, poll_interval_s=%s", task_id, poll_interval_s) + time.sleep(5) + return 0 + + def export_task_report(self, task_id: int, file_type: str = "excel") -> Path: + logger.info("虚假执行 export_task_report, task_id=%s, file_type=%s", task_id, file_type) + time.sleep(5) + return MODULE_ROOT / "data" / "fake_report.xlsx" + + # ---------- 6. 下料动作 ---------- + def batch_out_task_and_empty_trays(self, task_id: int | None = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + logger.info("虚假执行 batch_out_task_and_empty_trays, task_id=%s, poll_interval_s=%s, ignore_missing=%s, timeout_s=%s, move_type=%s", task_id, poll_interval_s, ignore_missing, timeout_s, move_type) + time.sleep(5) + return {"success": True, "message": "虚假任务盘和空盘出盘完成"} + + def batch_out_task_and_chemical_trays(self, task_id: int | None = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + logger.info("虚假执行 batch_out_task_and_chemical_trays, task_id=%s, poll_interval_s=%s, ignore_missing=%s, timeout_s=%s, move_type=%s", task_id, poll_interval_s, ignore_missing, timeout_s, move_type) + time.sleep(5) + return {"success": True, "message": "虚假任务盘和化学品盘出盘完成"} + + def batch_out_task_trays(self, task_id: int | None = None, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + logger.info("虚假执行 batch_out_task_trays, task_id=%s, poll_interval_s=%s, ignore_missing=%s, timeout_s=%s, move_type=%s", task_id, poll_interval_s, ignore_missing, timeout_s, move_type) + time.sleep(5) + return {"success": True, "message": "虚假任务盘出盘完成"} + + def batch_out_empty_trays(self, *, poll_interval_s: float = 1.0, ignore_missing: bool = True, timeout_s: float = 900.0, move_type: str = "main_out"): + logger.info("虚假执行 batch_out_empty_trays, poll_interval_s=%s, ignore_missing=%s, timeout_s=%s, move_type=%s", poll_interval_s, ignore_missing, timeout_s, move_type) + time.sleep(5) + return {"success": True, "message": "虚假空盘出盘完成"} + + def batch_out_tray(self, layout_list: list[dict], move_type: str = "main_out", *, task_id: int = None, poll_interval_s: float = 1.0, timeout_s: float = 900.0): + logger.info("虚假执行 batch_out_tray, layout_list=%s, move_type=%s, task_id=%s, poll_interval_s=%s, timeout_s=%s", layout_list, move_type, task_id, poll_interval_s, timeout_s) + time.sleep(5) + return {"success": True, "message": "虚假按布局出盘完成"} + + def auto_unload_trays_to_agv(self, batch_out_file: Optional[str] = None, *, block: bool = True, auto_run_analysis: bool = True): + logger.info("虚假执行 auto_unload_trays_to_agv, batch_out_file=%s, block=%s, auto_run_analysis=%s", batch_out_file, block, auto_run_analysis) + time.sleep(5) + return {"success": True, "message": "虚假自动卸盘完成"} + + # ---------- 7. Unilab 接口(待修改) ---------- + def submit_experiment_task( + self, + chemical_db_path: str, + task_name: str = "Unilab_Auto_Job", + reaction_type: str = "heat", + duration: str = "8", + temperature: str = "40", + stir_speed: str = "500", + target_temp: str = "30", + auto_magnet: bool = True, + fixed_order: bool = False, + internal_std_name: str = "", + stir_time_after_std: str = "", + diluent_name: str = "", + rows: list = None + ) -> JsonDict: + """ + 功能: + 提交 Unilab 流程编排任务, 按行数据动态生成表头, 兼容包含“加磁子”的列. + 参数: + chemical_db_path: str, 化学品库文件路径. + task_name: str, 任务名称. + reaction_type: str, 反应类型. + duration: str, 反应时间, 必须带单位, 如 "8h" 或 "30min". + temperature: str, 反应温度(°C). + stir_speed: str, 搅拌速度(rpm). + target_temp: str, 搅拌后目标温度(°C). + auto_magnet: bool, 是否自动加磁子. + fixed_order: bool, 是否固定加料顺序. + internal_std_name: str, 内标名称. + stir_time_after_std: str, 内标加入后搅拌时间(min). + diluent_name: str, 稀释液名称. + rows: List[List[Any]], 行数据矩阵, 第1列为实验编号, 其余列为试剂或“加磁子”. + 返回: + Dict[str, Any], 提交成功后返回的任务 ID. + """ + logger.info("虚假执行 submit_experiment_task, chemical_db_path=%s, task_name=%s", chemical_db_path, task_name) + time.sleep(5) + return "fake_task_id" + + # ---------- 分析站对接 ---------- + def run_analysis(self, task_id: Optional[str] = None) -> Dict: + """ + 功能: + 读取指定合成任务(或最新任务)的 xlsx 配置, 自动生成分析任务 CSV + 并通过 AnalysisStationController 提交至对应仪器(当前已实现 GC_MS). + 参数: + task_id: 合成任务 ID 字符串, 为 None 时自动选取编号最大的最近任务. + 返回: + Dict: 各仪器提交结果, 格式示例: + { + "gc_ms": {"success": bool, "return_info": str}, + "uplc_qtof": {"success": bool, "return_info": str}, + "hplc": {"success": bool, "return_info": str}, + } + """ + logger.info("虚假执行 run_analysis, task_id=%s", task_id) + time.sleep(5) + return { + "gc_ms": {"success": True, "return_info": "虚假 GC-MS 完成"}, + "uplc_qtof": {"success": True, "return_info": "虚假 UPLC-QTOF 完成"}, + "hplc": {"success": True, "return_info": "虚假 HPLC 完成"}, + } + + def poll_analysis_run( + self, task_id: Optional[str] = None, poll_interval: float = 30.0 + ) -> Dict: + """ + 功能: + 轮询 GC-MS 分析任务运行状态, 完成后自动触发结果处理(积分+定性+报告). + 内部委托给 AnalysisStationController.poll_analysis_run 执行. + 参数: + task_id: 合成任务 ID 字符串, 为 None 时自动选取编号最大的最近任务. + poll_interval: 轮询间隔(秒), 默认 30 秒. + 返回: + Dict: process_gc_ms_results 的返回值, 包含 success/return_info/report_path. + """ + logger.info("虚假执行 poll_analysis_run, task_id=%s, poll_interval=%s", task_id, poll_interval) + time.sleep(5) + return {"success": True, "return_info": "虚假分析轮询完成", "report_path": ""} diff --git a/unilabos/devices/eit_synthesis_station/manager/synchronizer.py b/unilabos/devices/eit_synthesis_station/manager/synchronizer.py new file mode 100644 index 00000000..c1eba792 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/manager/synchronizer.py @@ -0,0 +1,1538 @@ +""" +EIT 合成工作站物料同步系统 +实现 EIT 工站与 UniLab 前端的实时物料同步与控制钩子 +""" + +import threading +import time +import re +import json +import traceback +import zipfile +from pathlib import Path +from xml.etree import ElementTree as ET +from typing import Dict, Any, List, Optional, Tuple +from unilabos.devices.workstation.workstation_base import WorkstationBase, ResourceSynchronizer +from unilabos.utils.log import logger +from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode +from pylabrobot.resources import Resource, Container, ResourceHolder, Well +from unilabos.resources import resource_tracker +from unilabos.utils import cls_creator +from unilabos.ros.nodes.base_device_node import ROS2DeviceNode +import uuid +from unilabos.devices.eit_synthesis_station.controller.station_controller import SynthesisStationController +from unilabos.devices.eit_synthesis_station.config.setting import Settings, configure_logging +from unilabos.devices.eit_synthesis_station.config.constants import ResourceCode, TRAY_CODE_DISPLAY_NAME, TraySpec +from unilabos.resources.eit_synthesis_station import bottle_carriers, items +from unilabos.resources.eit_synthesis_station.decks import EIT_Synthesis_Station_Deck +from unilabos.resources.warehouse import WareHouse +from unilabos.resources.itemized_carrier import BottleCarrier +from unilabos_msgs.srv import SerialCommand # type: ignore + +def normalize_layout_code(eit_code: Optional[str]) -> Optional[str]: + if not eit_code or "-" not in eit_code: + return eit_code + parts = eit_code.split("-") + if len(parts) < 2: + return eit_code + normalized = [parts[0]] + for part in parts[1:]: + if part.isdigit(): + normalized.append(str(int(part))) + else: + normalized.append(part) + return "-".join(normalized) + +_EIT_TYPE_MAPPINGS = { + "EIT_Synthesis_Station_Deck": EIT_Synthesis_Station_Deck, + "WareHouse": WareHouse, + "BottleCarrier": BottleCarrier, + "ResourceHolder": ResourceHolder, + "Container": Container, + "Well": Well, + "deck": EIT_Synthesis_Station_Deck, + "warehouse": WareHouse, + "bottle_carrier": BottleCarrier, + "resource_holder": ResourceHolder, + "container": Container, + "well": Well, +} + +for key, cls in _EIT_TYPE_MAPPINGS.items(): + if hasattr(cls_creator, "register"): + cls_creator.register(key, cls) + elif hasattr(cls_creator, "CLASS_MAP"): + cls_creator.CLASS_MAP[key] = cls + +try: + from unilabos.resources import resource_tracker as _resource_tracker + if hasattr(_resource_tracker, "ResourceTracker"): + for key, cls in _EIT_TYPE_MAPPINGS.items(): + _resource_tracker.ResourceTracker.CLASS_MAP[key] = cls +except Exception as e: + logger.debug(f"跳过 ResourceTracker 直接注入: {e}") + +class EITSynthesisResourceSynchronizer(ResourceSynchronizer): + """EIT 资源同步器""" + + # 1. 建立 ResourceCode 与载架工厂函数的映射 + CARRIER_FACTORY = { + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_2ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_8ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_40ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML): bottle_carriers.EIT_REAGENT_BOTTLE_TRAY_125ML, + int(ResourceCode.POWDER_BUCKET_TRAY_30ML): bottle_carriers.EIT_POWDER_BUCKET_TRAY_30ML, + int(ResourceCode.TIP_TRAY_1ML): bottle_carriers.EIT_TIP_TRAY_1ML, + int(ResourceCode.TIP_TRAY_5ML): bottle_carriers.EIT_TIP_TRAY_5ML, + int(ResourceCode.TIP_TRAY_50UL): bottle_carriers.EIT_TIP_TRAY_50UL, + int(ResourceCode.REACTION_TUBE_TRAY_2ML): bottle_carriers.EIT_REACTION_TUBE_TRAY_2ML, + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML): bottle_carriers.EIT_TEST_TUBE_MAGNET_TRAY_2ML, + int(ResourceCode.REACTION_SEAL_CAP_TRAY): bottle_carriers.EIT_REACTION_SEAL_CAP_TRAY, + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY): bottle_carriers.EIT_FLASH_FILTER_INNER_BOTTLE_TRAY, + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): bottle_carriers.EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY, + } + + # 2. 建立托盘类型与容器物品的对应关系 + TRAY_TO_ITEM_MAP = { + int(ResourceCode.REAGENT_BOTTLE_TRAY_2ML): items.EIT_REAGENT_BOTTLE_2ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_8ML): items.EIT_REAGENT_BOTTLE_8ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_40ML): items.EIT_REAGENT_BOTTLE_40ML, + int(ResourceCode.REAGENT_BOTTLE_TRAY_125ML): items.EIT_REAGENT_BOTTLE_125ML, + int(ResourceCode.POWDER_BUCKET_TRAY_30ML): items.EIT_POWDER_BUCKET_30ML, + int(ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY): items.EIT_FLASH_FILTER_INNER_BOTTLE, + int(ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY): items.EIT_FLASH_FILTER_OUTER_BOTTLE, + int(ResourceCode.REACTION_SEAL_CAP_TRAY): items.EIT_REACTION_SEAL_CAP, + int(ResourceCode.REACTION_TUBE_TRAY_2ML): items.EIT_REACTION_TUBE_2ML, + int(ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML): items.EIT_TEST_TUBE_MAGNET_2ML, + } + + def __init__( + self, + workstation: 'EITSynthesisWorkstation', + controller: Optional[SynthesisStationController] = None, + ): + super().__init__(workstation) + self.controller: Optional[SynthesisStationController] = controller + self._chemical_name_map: Optional[Dict[str, str]] = None + self.initialize() + + def initialize(self) -> bool: + """初始化 EIT Manager 并登录""" + try: + if self.controller is None: + settings = Settings.from_env() + configure_logging(settings.log_level) + self.controller = SynthesisStationController(settings=settings) + self.controller.login() + return True + except Exception as e: + logger.error(f"EIT Synchronizer 初始化失败: {e}") + return False + + def sync_from_external(self) -> bool: + """[工站 -> 前端] 从 EIT 获取资源信息并更新 UniLab Deck""" + try: + raw_data = self.controller.get_resource_info() + # 注意:raw_data == [] 也要继续往下走,以便把本地残留物料清掉 + if raw_data is None: + return True + if not isinstance(raw_data, list): + logger.warning(f"get_resource_info 返回非 list: {type(raw_data)}") + return False + # raw_data 允许为空列表 + + + hardware_items = {} + if raw_data: + for item in raw_data: + raw_code = item.get("layout_code") + norm_code = normalize_layout_code(raw_code) + if norm_code: + item["layout_code"] = norm_code + hardware_items[norm_code] = item + occupied_codes = set(hardware_items.keys()) + + # 观测工站事实状态,释放已完成的上料去重锁 + if hasattr(self.workstation, "_observe_and_release_batchin_locks"): + self.workstation._observe_and_release_batchin_locks(hardware_items) + + + # 记录发生变化的仓库,用于增量上传 + changed_warehouses = [] + # 获取是否已完成首次全量同步的标识(在 EITSynthesisWorkstation 中定义) + is_first_sync = not getattr(self.workstation, "_first_full_sync_done", False) + + # 2. 安全地获取所有仓库资源 + eit_zones = ["W", "N", "TB", "AS", "FF", "MS", "MSB", "SC", "T", "TS"] + warehouses = [res for res in self.workstation.deck.children if res.name in eit_zones] + + if not warehouses and is_first_sync: + logger.warning("Deck 上未发现 WareHouse 资源,请检查 Deck 是否正确执行了 setup() 初始化") + + # 3. 核心比对逻辑:以 EIT 原始 layout_code 为准定位槽位 + slot_index: Dict[str, ResourceHolder] = {} + for wh in warehouses: + for slot in wh.children: + if not isinstance(slot, ResourceHolder): + continue + slot_name = normalize_layout_code(getattr(slot, "name", None)) + if slot_name: + slot_index[slot_name] = slot + for wh in warehouses: + wh_changed = False + for slot in wh.children: + if not isinstance(slot, ResourceHolder): + continue + slot_name = normalize_layout_code(getattr(slot, "name", None)) + if not slot_name: + continue + eit_code = slot_name if slot_name in hardware_items else None + current_child = slot.children[0] if slot.children else None + + # --- 情况 A:硬件端该位点有物料 (增加或更新) --- + if eit_code: + item = hardware_items[eit_code] + raw_res_type = item.get("resource_type") + if raw_res_type is None: + # tray_item 缺失导致 resource_type 为 None,尝试保留已有物料或用通用 Container 兜底 + tray_display_name = item.get("resource_type_name") or "EIT Tray" + desired_tray_name = f"{tray_display_name}@{eit_code}" + if current_child: + # 已有物料,只更新名称,不破坏已有结构 + if current_child.name != desired_tray_name: + current_child.name = desired_tray_name + else: + # 无已有物料且无法确定类型,用通用 Container 兜底 + from pylabrobot.resources import Container as _Container + fallback = _Container( + name=desired_tray_name, + size_x=127.8, size_y=85.5, size_z=40.0, + ) + fallback.unilabos_uuid = str(uuid.uuid4()) + fallback.description = f"type unknown @ {eit_code}" + slot.assign_child_resource(fallback) + logger.debug(f"layout_code={eit_code} 的 resource_type 为 None,保留已有物料或兜底处理") + continue + try: + res_type = int(raw_res_type) + except (ValueError, TypeError): + logger.warning(f"layout_code={eit_code} 的 resource_type={raw_res_type!r} 无法转为 int,跳过") + continue + details = item.get("substance_details", []) + tray_display_name = item.get("resource_type_name") or "EIT Tray" + desired_tray_name = f"{tray_display_name}@{eit_code}" + + if current_child: + existing_type = getattr(current_child, "eit_resource_type", None) + if existing_type is None: + extra = getattr(current_child, "unilabos_extra", {}) or {} + existing_type = extra.get("eit_resource_type") + if existing_type is None: + model_val = getattr(current_child, "model", None) + if model_val is not None: + try: + existing_type = int(str(model_val).strip()) + except Exception: + existing_type = None + if existing_type == res_type: + if not getattr(current_child, "unilabos_extra", None): + current_child.unilabos_extra = {} + current_child.unilabos_extra["eit_layout_code"] = eit_code + # 事实态覆盖:清理请求态字段,防止前端/逻辑判断被 update_resource_site 污染 + for k in ("update_resource_site", "eit_staging_code", "eit_last_requested_site", "eit_last_batch_in_tray_code"): + current_child.unilabos_extra.pop(k, None) + current_child.unilabos_extra["eit_resource_type"] = res_type + current_child.eit_resource_type = res_type + if current_child.name != desired_tray_name: + current_child.name = desired_tray_name + if details and hasattr(current_child, "sites"): + item_factory = self.TRAY_TO_ITEM_MAP.get(res_type) + for detail in details: + slot_idx = detail.get("slot") + if slot_idx is None or slot_idx >= len(current_child.sites): + continue + well_name = detail.get("well") or f"slot_{slot_idx + 1}" + substance_name = detail.get("substance") or well_name + desired_bottle_name = f"{substance_name}@{well_name}" + site = current_child.get_item(slot_idx) + if isinstance(site, ResourceHolder): + child = site.children[0] if site.children else None + else: + child = site + if child is None and item_factory: + bottle = item_factory(name=desired_bottle_name) + bottle.unilabos_uuid = str(uuid.uuid4()) + bottle.description = substance_name + current_child[slot_idx] = bottle + elif child and getattr(child, "name", None) != desired_bottle_name: + child.name = desired_bottle_name + child.description = substance_name + continue + + # 若类型不匹配或原槽位为空,清理旧物料并重建 + if current_child: + slot.unassign_child_resource(current_child) + + # 根据资源类型调用对应的载架工厂函数 + factory_func = self.CARRIER_FACTORY.get(res_type) + if factory_func: + try: + new_carrier = factory_func(name=desired_tray_name, prefill_items=False) + except TypeError: + new_carrier = factory_func(name=desired_tray_name) + else: + new_carrier = Container(name=desired_tray_name, size_x=127.8, size_y=85.5, size_z=20) + new_carrier.description = item.get("resource_type_name") + + # 注入 UUID 与同步必需的元数据 + new_carrier.unilabos_uuid = str(uuid.uuid4()) + new_carrier.eit_resource_type = res_type + new_carrier.unilabos_extra = { + "eit_layout_code": eit_code, + "eit_resource_type": res_type + } + new_carrier.description = tray_display_name + + # 填充载架内部细节(例如试剂瓶/吸头等子物料) + item_factory = self.TRAY_TO_ITEM_MAP.get(res_type) + if item_factory and hasattr(new_carrier, 'sites'): + for detail in details: + slot_idx = detail.get("slot") + if slot_idx is None or slot_idx >= len(new_carrier.sites): + continue + well_name = detail.get("well") or f"slot_{slot_idx + 1}" + substance_name = detail.get("substance") or well_name + bottle = item_factory(name=f"{substance_name}@{well_name}") + bottle.unilabos_uuid = str(uuid.uuid4()) + bottle.description = substance_name + new_carrier[slot_idx] = bottle + + # 将新创建的物料挂载到虚拟槽位 + slot.assign_child_resource(new_carrier) + wh_changed = True + + # --- 情况 B:硬件端该位点为空,但本地有物料 (检测到物料被移除/减少) --- + elif current_child: + logger.info(f"[同步] 检测到硬件位点为空,同步移除本地物料") + # 执行逻辑移除,清空本地虚拟槽位 + slot.unassign_child_resource(current_child) + wh_changed = True + + # 如果该分区(Warehouse)内有任何变动,将其加入更新队列 + if wh_changed: + changed_warehouses.append(wh) + + # 4. 执行云端推送策略 + if is_first_sync: + # 首次全量同步:必须发送整个 Deck 以初始化完整的仓库和槽位结构 + if self.workstation and hasattr(self.workstation, "_ros_node"): + deck = self.workstation.deck + if hasattr(deck, "_recursive_assign_uuid"): + deck._recursive_assign_uuid(deck) + logger.info("正在上传 EIT Deck 到云端...") + self._update_resource_flattened([deck]) + self.workstation._first_full_sync_done = True + logger.info("EIT 首次全量同步上报完成") + elif changed_warehouses: + # 增量推送:仅发送发生变动的仓库对象,有效解决减少物料同步并减轻前端性能压力 + logger.info(f"检测到 {len(changed_warehouses)} 个分区变动,执行增量更新上报") + self._update_resource_flattened(changed_warehouses) + + return True + except Exception as e: + logger.error(f"同步 EIT 硬件状态异常: {e}") + return False + + def _update_resource_flattened(self, resources: List[Resource]) -> None: + """上传前扁平化资源树,移除仓库/托盘内的站位节点。""" + tree_set = resource_tracker.ResourceTreeSet.from_plr_resources(resources) + tree_dump = tree_set.dump() + flattened_trees: List[List[Dict[str, Any]]] = [] + for tree_nodes in tree_dump: + nodes_by_uuid = {node.get("uuid"): node for node in tree_nodes if node.get("uuid")} + children_map: Dict[Optional[str], List[str]] = {} + for node in tree_nodes: + node_uuid = node.get("uuid") + if not node_uuid: + continue + parent_uuid = node.get("parent_uuid") + children_map.setdefault(parent_uuid, []).append(node_uuid) + + for node_uuid, node in list(nodes_by_uuid.items()): + if node.get("type") != "resource_holder": + continue + parent_uuid = node.get("parent_uuid") + parent = nodes_by_uuid.get(parent_uuid) + if not parent or parent.get("type") not in {"warehouse", "bottle_carrier"}: + continue + parent_uuid = node.get("parent_uuid") + slot_label = node.get("name") or node.get("id") + reparented_children: List[Dict[str, Any]] = [] + for child_uuid in children_map.get(node_uuid, []): + child = nodes_by_uuid.get(child_uuid) + if not child: + continue + child["parent_uuid"] = parent_uuid + child_pose = child.get("pose", {}) + offset_pose = node.get("pose", {}) + for key in ("position", "position3d"): + child_pos = child_pose.get(key) + offset_pos = offset_pose.get(key) + if not isinstance(child_pos, dict) or not isinstance(offset_pos, dict): + continue + child_pos["x"] = float(child_pos.get("x", 0.0)) + float(offset_pos.get("x", 0.0)) + child_pos["y"] = float(child_pos.get("y", 0.0)) + float(offset_pos.get("y", 0.0)) + child_pos["z"] = float(child_pos.get("z", 0.0)) + float(offset_pos.get("z", 0.0)) + children_map.setdefault(parent_uuid, []).append(child_uuid) + reparented_children.append(child) + if parent_uuid in children_map: + children_map[parent_uuid] = [c for c in children_map[parent_uuid] if c != node_uuid] + parent_node = nodes_by_uuid.get(parent_uuid) if parent_uuid else None + if parent_node and slot_label: + child_name = reparented_children[0].get("name") if reparented_children else None + config = parent_node.get("config") + if isinstance(config, dict): + sites = config.get("sites") + if isinstance(sites, list): + label_candidates = [slot_label] + layout_label = None + if slot_label and "-" not in slot_label: + row_letter = slot_label[:1] + col_str = slot_label[1:] + if row_letter.isalpha() and col_str.isdigit(): + try: + row = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(row_letter.upper()) + 1 + col = int(col_str) + except ValueError: + row = None + col = None + if row is not None and col is not None: + parent_name = parent_node.get("name") or parent_node.get("id") + if parent_name: + num_items_y = config.get("num_items_y") + if num_items_y == 1: + layout_label = f"{parent_name}-{col}" + else: + layout_label = f"{parent_name}-{row}-{col}" + if layout_label and layout_label not in label_candidates: + label_candidates.append(layout_label) + for site in sites: + if site.get("label") in label_candidates: + site["occupied_by"] = child_name + break + nodes_by_uuid.pop(node_uuid, None) + flattened_trees.append(list(nodes_by_uuid.values())) + ROS2DeviceNode.run_async_func( + self._update_resource_with_tree_dump, + True, + **{"resources": resources, "resource_tree_dump": flattened_trees}, + ) + + async def _update_resource_with_tree_dump( + self, + resources: List[Resource], + resource_tree_dump: List[List[Dict[str, Any]]], + ) -> None: + """ + 功能: + 使用扁平化后的资源树执行资源更新, 避免基类 update_resource 无法接收额外参数. + 参数: + resources: 本地资源对象列表, 用于根据服务端返回结果回填 uuid. + resource_tree_dump: 已扁平化的资源树序列化结果. + 返回: + None. + """ + ros_node = self.workstation._ros_node + # 与 ROS2DeviceNode.update_resource 保持一致:根节点若缺少 parent_uuid, + # 必须回挂到当前设备 uuid,避免 host 侧以 mount_uuid=None 上传到云端。 + for tree_nodes in resource_tree_dump: + for node in tree_nodes: + if not node.get("parent_uuid"): + node["parent_uuid"] = ros_node.uuid + + request = SerialCommand.Request() + # 直接上报扁平化结果, 保持前端所需的资源树结构. + request.command = json.dumps({"data": {"data": resource_tree_dump}, "action": "update"}) + response = await ros_node._resource_clients["c2s_update_resource_tree"].call_async(request) # type: ignore + try: + uuid_maps = json.loads(response.response) + ros_node.resource_tracker.loop_update_uuid(resources, uuid_maps) + except Exception as exc: + ros_node.lab_logger().error(f"更新资源 uuid 失败: {exc}") + ros_node.lab_logger().error(traceback.format_exc()) + ros_node.lab_logger().trace(f"资源更新结果: {response}") + + def sync_to_external(self, resource: Resource) -> bool: + """[虚拟 -> 硬件] 组装 BatchInTray payload,补齐化学品与单位后同步。""" + def _load_parameters(*sources: Any) -> Dict[str, Any]: + for src in sources: + if not isinstance(src, dict): + continue + for key in ("Parameters", "parameters", "params"): + if key not in src: + continue + val = src.get(key) + if isinstance(val, dict): + return val + if isinstance(val, str): + text = val.strip() + if text in ("", "{}", "null", "None"): + continue + try: + parsed = json.loads(text) + except Exception: + continue + if isinstance(parsed, dict): + return parsed + return {} + + def _get_liquids_from_source(source: Any) -> Optional[List[Any]]: + if source is None: + return None + if isinstance(source, dict): + liquids = source.get("liquids") or source.get("pending_liquids") + if isinstance(liquids, list): + return liquids + return None + liquids = getattr(source, "liquids", None) or getattr(source, "pending_liquids", None) + if isinstance(liquids, list): + return liquids + return None + + def _resolve_slot_label_from_name(name: Optional[str]) -> Optional[str]: + if not name: + return None + name = str(name).strip() + if "@" in name: + left, right = [part.strip() for part in name.split("@", 1)] + if re.match(r"^[A-Za-z]+\d+$", right): + return right + if re.match(r"^[A-Za-z]+\d+$", left): + return left + if re.match(r"^[A-Za-z]+\d+$", name): + return name + return None + + def _extract_substance( + bottle: Resource, + slot_label: Optional[str], + extra_sources: Optional[List[Dict[str, Any]]] = None, + ) -> Optional[str]: + extra = getattr(bottle, "unilabos_extra", {}) or {} + state = getattr(bottle, "_unilabos_state", {}) or {} + data = getattr(bottle, "data", {}) or {} + if not isinstance(extra, dict): + extra = {} + if not isinstance(state, dict): + state = {} + if not isinstance(data, dict): + data = {} + sources: List[Dict[str, Any]] = [extra, state, data] + if extra_sources: + sources.extend([src for src in extra_sources if isinstance(src, dict)]) + params = _load_parameters(*sources) + sources = [extra, params, state, data] + (extra_sources or []) + for source in sources: + for key in ("substance", "material_name", "chemical_name", "material", "name"): + value = source.get(key) + if value: + return str(value).strip() + liquid_sources: List[Any] = [data, state, params] + (extra_sources or []) + tracker = getattr(bottle, "tracker", None) + if tracker is not None: + liquid_sources.append(tracker) + for source in liquid_sources: + liquids = _get_liquids_from_source(source) + if not isinstance(liquids, list) or not liquids: + continue + first = liquids[0] + if isinstance(first, (list, tuple)) and first: + name = str(first[0]).strip() + if name: + return name + if isinstance(first, dict): + for key in ("substance", "name", "material", "chemical_name"): + value = first.get(key) + if value: + return str(value).strip() + for key in ("substance", "material_name", "chemical_name", "name"): + value = extra.get(key) + if value: + return str(value).strip() + desc = str(getattr(bottle, "description", "") or "").strip() + if desc: + return desc + name = str(getattr(bottle, "name", "") or "").strip() + if "@" in name: + name = name.split("@", 1)[0].strip() + if slot_label and name == slot_label: + return None + if re.match(r"^[A-Z]+\d+$", name): + return None + return name or None + + def _extract_amount( + bottle: Resource, + amount_kind: str, + extra_sources: Optional[List[Dict[str, Any]]] = None, + ) -> Tuple[Optional[float], Optional[float]]: + def _parse_amount(text: Any) -> Tuple[Optional[float], Optional[str]]: + if text is None: + return None, None + if isinstance(text, (int, float)): + return float(text), None + match = re.search(r"([0-9]+(?:\.[0-9]+)?)\s*([a-zA-Zμµ]+)?", str(text)) + if not match: + return None, None + value = float(match.group(1)) + unit = match.group(2) or "" + unit = unit.replace("µ", "μ").strip() + return value, unit + + def _normalize_amount(value: float, unit: Optional[str], amount_kind: str) -> float: + unit = (unit or "").lower() + if amount_kind == "volume": + if unit in ("l", "liter"): + return value * 1000.0 + if unit in ("μl", "ul"): + return value / 1000.0 + return value + if amount_kind == "weight": + if unit in ("kg", "kilogram"): + return value * 1_000_000.0 + if unit in ("g", "gram"): + return value * 1000.0 + return value + return value + + extra = getattr(bottle, "unilabos_extra", {}) or {} + state = getattr(bottle, "_unilabos_state", {}) or {} + data = getattr(bottle, "data", {}) or {} + if not isinstance(extra, dict): + extra = {} + if not isinstance(state, dict): + state = {} + if not isinstance(data, dict): + data = {} + sources: List[Dict[str, Any]] = [extra, state, data] + if extra_sources: + sources.extend([src for src in extra_sources if isinstance(src, dict)]) + params = _load_parameters(*sources) + sources = [extra, params, state, data] + (extra_sources or []) + for source in sources: + for key in ("initial_volume", "initial_weight", "volume", "weight"): + if key in source: + try: + val = float(source[key]) + except Exception: + val = None + if val is None: + continue + if "volume" in key: + return val, None + return None, val + for source in (params, extra, state, data, *(extra_sources or [])): + raw = source.get("value") or source.get("amount") or source.get("quantity") + unit = source.get("unit") + if raw is None: + continue + if unit: + raw = f"{raw}{unit}" + value, unit = _parse_amount(raw) + if value is None: + continue + value = _normalize_amount(value, unit, amount_kind) + if amount_kind == "volume": + return value, None + if amount_kind == "weight": + return None, value + liquid_sources: List[Any] = [data, state] + (extra_sources or []) + tracker = getattr(bottle, "tracker", None) + if tracker is not None: + liquid_sources.append(tracker) + for source in liquid_sources: + liquids = _get_liquids_from_source(source) + if not isinstance(liquids, list) or not liquids: + continue + first = liquids[0] + if isinstance(first, (list, tuple)) and len(first) > 1: + value, unit = _parse_amount(first[1]) + elif isinstance(first, dict): + raw = first.get("value") or first.get("amount") or first.get("volume") or first.get("weight") + value, unit = _parse_amount(raw) + else: + value, unit = (None, None) + if value is None: + continue + value = _normalize_amount(value, unit, amount_kind) + if amount_kind == "volume": + return value, None + if amount_kind == "weight": + return None, value + for key in ("value", "amount", "quantity"): + raw = extra.get(key) + if raw is None: + raw = state.get(key) + if raw is None: + raw = data.get(key) + if raw is None: + continue + value, unit = _parse_amount(raw) + if value is None: + continue + value = _normalize_amount(value, unit, amount_kind) + if amount_kind == "volume": + return value, None + if amount_kind == "weight": + return None, value + return None, None + + carrier = resource + if getattr(resource, "category", None) != "bottle_carrier": + if resource.parent and getattr(resource.parent, "category", None) == "bottle_carrier": + carrier = resource.parent + elif resource.parent and resource.parent.parent and getattr(resource.parent.parent, "category", None) == "bottle_carrier": + carrier = resource.parent.parent + + extra = getattr(carrier, "unilabos_extra", {}) or {} + eit_code = extra.get("eit_staging_code") + if not eit_code and self.workstation.is_in_staging(carrier): + eit_code = self.workstation.get_slot_layout_code(carrier) + if eit_code: + if not hasattr(carrier, "unilabos_extra"): + carrier.unilabos_extra = {} + carrier.unilabos_extra["eit_staging_code"] = eit_code + if not eit_code: + logger.warning(f"[同步→硬件] 非入口位点或缺少入口坐标,跳过: {carrier.name}") + return False + + tray_code = None + model_val = getattr(carrier, "model", None) + if model_val is not None: + try: + tray_code = int(str(model_val).strip()) + except Exception: + tray_code = None + if tray_code is None: + extra_code = extra.get("eit_resource_type") + if extra_code is not None: + try: + tray_code = int(str(extra_code).strip()) + except Exception: + tray_code = None + if tray_code is None: + desc = str(getattr(carrier, "description", "") or "").strip() + name = str(getattr(carrier, "name", "") or "").strip() + for key, label in TRAY_CODE_DISPLAY_NAME.items(): + if desc == label or name == label: + tray_code = int(key) + break + if tray_code is None: + logger.warning(f"[同步→硬件] 无法解析托盘类型,跳过: {carrier.name}") + return False + + tray_spec = None + try: + tray_spec = getattr(TraySpec, ResourceCode(tray_code).name, None) + except Exception: + tray_spec = None + + 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"), + } + + resource_list: List[Dict[str, Any]] = [{ + "layout_code": f"{eit_code}:-1", + "resource_type": str(tray_code), + }] + + media_code, with_cap, amount_kind, default_unit = tray_to_media.get( + tray_code, + (str(tray_code), False, "volume", "mL"), + ) + holder_by_slot: Dict[str, Any] = {} + for child in getattr(carrier, "children", []): + if isinstance(child, ResourceHolder): + holder_by_slot[getattr(child, "name", "")] = child + + name_map = self._chemical_name_map + chem_cache: Dict[str, Tuple[Optional[int], Optional[str]]] = {} + + def _resolve_chemical(sub_name: str) -> Tuple[Optional[int], Optional[str]]: + """对齐化学品库,优先完全匹配与中文名。""" + raw_name = str(sub_name or "").strip() + if raw_name == "": + return None, None + cached = chem_cache.get(raw_name) + if cached is not None: + return cached + nonlocal name_map + if name_map is None: + name_map = {} + sheet_path = Path(__file__).resolve().parent.parent / "sheet" / "chemical_list.xlsx" + if not sheet_path.exists(): + logger.warning(f"[同步→硬件] 未找到化学品映射表: {sheet_path}") + else: + try: + with zipfile.ZipFile(sheet_path) as zf: + shared = zf.read("xl/sharedStrings.xml") if "xl/sharedStrings.xml" in zf.namelist() else None + sheet = zf.read("xl/worksheets/sheet1.xml") + except Exception as exc: + logger.warning(f"[同步→硬件] 读取化学品映射表失败: {exc}") + else: + ns = {"a": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"} + if shared: + try: + shared_root = ET.fromstring(shared) + strings = [ + (si.find(".//a:t", ns).text if si.find(".//a:t", ns) is not None else "") + for si in shared_root.findall("a:si", ns) + ] + except Exception: + strings = [] + else: + strings = [] + sheet_root = ET.fromstring(sheet) + sheet_data = sheet_root.find(".//a:sheetData", ns) + if sheet_data is not None: + header: Dict[int, str] = {} + english_idx = None + substance_idx = None + for row_idx, row in enumerate(sheet_data.findall("a:row", ns), start=1): + row_values: Dict[int, str] = {} + for cell in row.findall("a:c", ns): + ref = cell.get("r") or "" + if ref == "": + continue + letters = "".join(ch for ch in ref if ch.isalpha()).upper() + col_idx = 0 + for ch in letters: + col_idx = col_idx * 26 + (ord(ch) - ord("A") + 1) + col_idx -= 1 + cell_type = cell.get("t") + if cell_type == "inlineStr": + inline = cell.find("a:is/a:t", ns) + value = inline.text if inline is not None else None + else: + value_node = cell.find("a:v", ns) + if value_node is None: + value = None + else: + text = value_node.text or "" + if cell_type == "s": + try: + value = strings[int(text)] + except Exception: + value = None + else: + value = text + if value is None: + continue + row_values[col_idx] = str(value).strip() + + if not row_values: + continue + + if row_idx == 1: + header = {idx: val for idx, val in row_values.items() if val != ""} + for idx, name in header.items(): + if name == "substance_english_name": + english_idx = idx + elif name == "substance": + substance_idx = idx + if english_idx is None or substance_idx is None: + logger.warning("[同步→硬件] 化学品映射表缺少 substance_english_name 或 substance 列") + break + continue + + if english_idx is None or substance_idx is None: + break + english = row_values.get(english_idx, "").strip() + substance = row_values.get(substance_idx, "").strip() + if english and substance: + name_map.setdefault(english, substance) + name_map.setdefault(english.lower(), substance) + self._chemical_name_map = name_map + name = raw_name + mapped = name_map.get(name) or name_map.get(name.lower()) + if mapped: + name = mapped + try: + resp = self.controller.get_chemical_list(query_key=name, limit=10) + except Exception as exc: + logger.warning(f"[同步→硬件] 化学品查询失败: {name}, err={exc}") + chem_cache[raw_name] = (None, name) + return chem_cache[raw_name] + candidates = resp.get("chemical_list") or [] + exact = None + for item in candidates: + item_name = str(item.get("name") or "").strip() + if item_name == name: + exact = item + break + chosen = exact + if chosen is None: + for item in candidates: + item_name = str(item.get("name") or "").strip() + if item_name != "" and re.search(r"[\u4e00-\u9fff]", item_name): + chosen = item + break + if chosen is None and candidates: + chosen = candidates[0] + if chosen is None: + logger.warning(f"[同步→硬件] 未找到化学品: {name}") + chem_cache[raw_name] = (None, name) + return chem_cache[raw_name] + fid = chosen.get("fid") or chosen.get("chemical_id") + chosen_name = str(chosen.get("name") or name).strip() or name + chem_cache[raw_name] = (fid, chosen_name) + return chem_cache[raw_name] + + bottle_debug: List[Dict[str, Any]] = [] + seen_slots: set = set() + bottle_items: List[Tuple[Optional[str], Optional[Resource]]] = [] + if hasattr(carrier, "sites"): + for idx in range(len(carrier.sites)): + site = carrier.get_item(idx) + slot_label = None + bottle = None + if isinstance(site, ResourceHolder): + slot_label = getattr(site, "name", None) + bottle = site.children[0] if site.children else site + else: + bottle = site + slot_label = _resolve_slot_label_from_name(getattr(bottle, "name", None)) + bottle_items.append((slot_label, bottle)) + for child in getattr(carrier, "children", []): + if isinstance(child, ResourceHolder): + slot_label = getattr(child, "name", None) + bottle = child.children[0] if child.children else child + bottle_items.append((slot_label, bottle)) + else: + slot_label = _resolve_slot_label_from_name(getattr(child, "name", None)) + bottle_items.append((slot_label, child)) + + for slot_label, bottle in bottle_items: + if not bottle: + continue + bottle_extra = getattr(bottle, "unilabos_extra", {}) or {} + if slot_label and "@" in slot_label: + slot_label = slot_label.split("@", 1)[0].strip() + if not slot_label: + slot_label = bottle_extra.get("well") or _resolve_slot_label_from_name(getattr(bottle, "name", None)) or "" + slot_label = slot_label or "" + slot_idx = None + if tray_spec and slot_label and len(slot_label) > 1 and slot_label[0].isalpha(): + try: + col_count, row_count = tray_spec + col_index = int(slot_label[1:]) + row_index = ord(slot_label[0].upper()) - ord("A") + if 1 <= col_index <= col_count and 0 <= row_index < row_count: + slot_idx = row_index * col_count + (col_index - 1) + except Exception: + slot_idx = None + if slot_idx is None: + slot_raw = bottle_extra.get("slot") or getattr(bottle, "slot", None) + if slot_raw is not None: + try: + slot_idx = int(str(slot_raw).strip()) + except Exception: + slot_idx = None + if slot_idx is None: + continue + if slot_idx in seen_slots: + continue + seen_slots.add(slot_idx) + + holder = holder_by_slot.get(slot_label) + holder_extra = getattr(holder, "unilabos_extra", {}) or {} if holder else {} + holder_state = getattr(holder, "_unilabos_state", {}) or {} if holder else {} + holder_data = getattr(holder, "data", {}) or {} if holder else {} + bottle_state: Dict[str, Any] = {} + for attr in ("serialize_state", "serialize_all_state"): + func = getattr(bottle, attr, None) + if not callable(func): + continue + try: + state = func() + except Exception: + continue + if not isinstance(state, dict): + continue + if attr == "serialize_all_state": + name = getattr(bottle, "name", None) + if isinstance(name, str) and isinstance(state.get(name), dict): + bottle_state = state[name] + break + bottle_state = state + break + + bottle_debug.append({ + "type": type(bottle).__name__, + "name": getattr(bottle, "name", None) if hasattr(bottle, "name") else str(bottle), + "slot_label": slot_label, + "slot_idx": slot_idx, + "unilabos_extra": bottle_extra if isinstance(bottle_extra, dict) else {}, + "data": getattr(bottle, "data", None) if hasattr(bottle, "data") else None, + "_unilabos_state": getattr(bottle, "_unilabos_state", None) if hasattr(bottle, "_unilabos_state") else None, + "serialized_state": bottle_state, + "tracker_liquids": getattr(getattr(bottle, "tracker", None), "liquids", None), + "holder_unilabos_extra": holder_extra, + "holder_data": holder_data, + "holder_unilabos_state": holder_state, + }) + + extra_sources = [holder_extra, holder_state, holder_data, bottle_state] + substance = _extract_substance(bottle, slot_label, extra_sources) + init_vol, init_wt = _extract_amount(bottle, amount_kind, extra_sources) + if not substance and init_vol is None and init_wt is None: + continue + + bottle_type = None + bottle_model = getattr(bottle, "model", None) + if bottle_model is not None: + try: + bottle_type = int(str(bottle_model).strip()) + except Exception: + bottle_type = None + resource_type = str(bottle_type) if bottle_type is not None else media_code + resolved_substance = substance + chemical_id = None + if substance: + liquids_present = False + for source in (bottle_state, getattr(bottle, "data", None), getattr(bottle, "_unilabos_state", None)): + liquids = _get_liquids_from_source(source) + if liquids: + liquids_present = True + break + if not liquids_present: + tracker = getattr(bottle, "tracker", None) + liquids_present = bool(_get_liquids_from_source(tracker)) + if liquids_present: + chemical_id, resolved_substance = _resolve_chemical(substance) + entry: Dict[str, Any] = { + "layout_code": f"{eit_code}:{slot_idx}", + "resource_type": resource_type, + "with_cap": with_cap, + } + if resolved_substance: + entry["substance"] = resolved_substance + if chemical_id is not None: + entry["chemical_id"] = chemical_id + if amount_kind == "weight": + if init_wt is not None: + entry["initial_weight"] = init_wt + entry["unit"] = default_unit + elif init_vol is not None: + entry["initial_volume"] = init_vol + entry["unit"] = "mL" + else: + if init_vol is not None: + entry["initial_volume"] = init_vol + entry["unit"] = default_unit + elif init_wt is not None: + entry["initial_weight"] = init_wt + entry["unit"] = "mg" + resource_list.append(entry) + + logger.info(f"同步至硬件: {carrier.name} -> {eit_code}") + if bottle_debug: + logger.info(f"[同步→硬件] Bottle raw data: {bottle_debug}") + else: + logger.info( + "[同步→硬件] Bottle raw data: [] (children=%s, sites=%s)", + [(getattr(c, "name", None), getattr(c, "category", None), type(c).__name__) for c in getattr(carrier, "children", [])], + len(getattr(carrier, "sites", []) or []), + ) + resource_req_list = [{ + "remark": "", + "resource_list": resource_list, + }] + logger.info(f"[同步→硬件] BatchInTray payload: {resource_req_list}") + # tray_code 已在上方解析并校验(非 None), 直接用于异常时释放去重锁 + + def _run_batch_in_tray(): + try: + resp = self.controller.batch_in_tray(resource_req_list) + if resp is not None and hasattr(carrier, "unilabos_extra"): + carrier.unilabos_extra.pop("eit_staging_code", None) + # 不在这里“释放锁”,锁由 sync_from_external 观测到离开入口后自动释放 + return + except Exception as e: + logger.error(f"[同步→硬件] BatchInTray 后台执行失败: {e}") + # 后台执行失败:释放去重锁允许重试 + try: + if tray_code is not None and hasattr(self.workstation, "_release_batchin_lock"): + self.workstation._release_batchin_lock(tray_code, eit_code) + except Exception as e2: + logger.warning(f"[去重锁] 后台失败释放锁异常: {e2}") + + threading.Thread(target=_run_batch_in_tray, daemon=True).start() + # 这里返回 True 表示“已派发/已受理”,避免阻塞 ROS 回调线程 + return True + + def handle_external_change(self, change_info: Dict[str, Any]) -> bool: + """ + [Physical -> Virtual] 处理外部硬件触发的变更(如手动搬运托盘)。 + 参考 Bioyond 模式:记录日志并触发强制同步。 + """ + logger.info(f"处理 EIT 外部变更通知: {change_info}") + # 触发全量状态更新以确保前端一致性 + return self.sync_from_external() + +class EITSynthesisWorkstation(WorkstationBase): + """EIT 工作站核心类:集成资源树钩子与状态监控""" + + def __init__( + self, + config: Optional[Dict] = None, + deck: Optional[Any] = None, + controller: Optional[SynthesisStationController] = None, + **kwargs): + super().__init__(deck=deck, **kwargs) + self.name = getattr(self, "device_id", "eit_station") + self.unilabos_uuid = getattr(self, "uuid", None) + self.config = config or {} + if controller is None and isinstance(self, SynthesisStationController): + controller = self + self.resource_synchronizer = EITSynthesisResourceSynchronizer(self, controller=controller) + self.controller = self.resource_synchronizer.controller + # ========= 上料请求去重/锁 ========= + # key: "|" -> {"ts": float, "tray_code": int, "staging_code": str} + self._batchin_lock = threading.Lock() + self._batchin_locks: Dict[str, Dict[str, Any]] = {} + # 默认锁 TTL:建议 5 分钟(覆盖一次完整上料耗时+网络抖动) + self._batchin_lock_ttl_s = int(self.config.get("batchin_lock_ttl_s", 300)) + # ========= 回滚上报节流 ========= + self._rollback_throttle_lock = threading.Lock() + # key: warehouse_name -> last_report_monotonic_ts + self._rollback_last_report_ts = {} + # 建议 0.2~0.5s,默认 0.3s + self._rollback_throttle_window_s = float(self.config.get("rollback_throttle_window_s", 0.3)) + + def post_init(self, ros_node: ROS2WorkstationNode): + """初始化后上传 Deck 资源树""" + self._ros_node = ros_node + # 首次同步工站状态 + self.resource_synchronizer.sync_from_external() + self._ros_node.create_timer(30.0, self.resource_synchronizer.sync_from_external) + logger.info(f"EIT 工作站 {ros_node.device_id} 定时同步任务已通过 ROS Timer 启动") + + # 自动启动异常通知邮件监控 (通过 Uni-Lab-OS 系统启动时触发) + try: + if self.controller._settings.notification.enabled: + self.controller.start_notification_monitor() + logger.info("异常通知邮件监控已启动") + except Exception as e: + logger.warning(f"异常通知监控启动失败, 不影响主流程: {e}") + + @property + def station_status(self) -> Dict[str, Any]: + """[状态上报] 对接底层控制器获取工站环境数据""" + try: + env = self.controller.get_glovebox_env() + state = self.controller.station_state() + return { + "connected": True, + "station_state": state, + "o2_ppm": env.get("oxygen_content"), + "h2o_ppm": env.get("water_content"), + "pressure_pa": env.get("box_pressure") + } + except Exception: + return {"connected": False} + + # ================= 资源树操作钩子 ================= + + def is_staging_code(self, code: Optional[str]) -> bool: + """判断一个 layout_code 是否属于过渡区入口位点。""" + if not code: + return False + code = normalize_layout_code(code) or "" + # TB 仓库:全部视为入口(兼容 TB 或 TB-...) + if code == "TB" or code.startswith("TB-"): + return True + # W 仓库:W-1-1 ~ W-1-8 + return bool(re.match(r"^W-1-[1-8]$", code)) + + def _derive_site_code_from_parent(self, res: Resource) -> str: + """从资源的 parent 链推导 slot 的 layout_code(尽量兼容 parent=WareHouse 的情况)。""" + # 情况1:parent 是 slot(ResourceHolder) —— slot.name 就是布局码 + parent = getattr(res, "parent", None) + if parent is None: + return "" + slot_name = normalize_layout_code(getattr(parent, "name", "") or "") + if slot_name: + return slot_name + # 情况2:parent 不是 slot(可能直接是 WareHouse),尝试 resolve + try: + return self._resolve_eit_code_by_slot(parent) or "" + except Exception: + return "" + + def get_slot_layout_code(self, res: Resource) -> str: + """ + 获取“前端请求挂载位置”的 layout_code。 + 优先使用 update_resource_site,其次从 parent 推导。 + 注意:这里不读取 eit_staging_code(staging_code 仅用于命令,不用于位置判断)。 + """ + extra = getattr(res, "unilabos_extra", {}) or {} + req = extra.get("update_resource_site") + req = normalize_layout_code(req) if req else "" + if req: + return req + + return self._derive_site_code_from_parent(res) + + def is_in_staging(self, res: Resource) -> bool: + """ + 判断资源“请求挂载位置”是否位于过渡区入口。 + 仅使用 update_resource_site / parent 推导的位置,不读取 eit_staging_code。 + """ + code = self.get_slot_layout_code(res) + return self.is_staging_code(code) + + def _get_eit_layout_code(self, res: Resource) -> str: + """ + 获取“当前物理位置”(由 sync_from_external 回写)。 + 注意:这里 只 读取 eit_layout_code,不读取 update_resource_site。 + """ + extra = getattr(res, "unilabos_extra", {}) or {} + eit_code = extra.get("eit_layout_code") + eit_code = normalize_layout_code(eit_code) if eit_code else "" + return eit_code or "" + + def _rollback_ui_virtual_tray(self, tray: Resource) -> None: + """ + 前端 add / update 产生的“请求态托盘”不应长期存在于资源树中。 + 这里将该托盘从本地 deck 结构中移除,并立刻上报受影响的仓库,以纠正云端/前端显示。 + """ + try: + # 1) 找到托盘所在仓库(用于增量上报纠偏) + wh = None + p = getattr(tray, "parent", None) + while p is not None: + if getattr(p, "category", None) == "warehouse": + wh = p + break + p = getattr(p, "parent", None) + + # 2) 从父节点移除 tray + parent = getattr(tray, "parent", None) + if parent is not None: + # parent 可能是 ResourceHolder / Container + if hasattr(parent, "unassign_child_resource"): + # ResourceHolder + parent.unassign_child_resource(tray) + else: + # 兜底:直接从 children 列表剔除 + if hasattr(parent, "children") and tray in parent.children: + parent.children.remove(tray) + # 断开引用 + tray.parent = None + + # 3) 立即上报纠偏:让云端/前端回到“事实态”(由 sync_from_external 驱动) + if wh is not None: + wh_name = str(getattr(wh, "name", "") or "warehouse") + now = time.monotonic() + win = getattr(self, "_rollback_throttle_window_s", 0.3) + + with self._rollback_throttle_lock: + last = float(self._rollback_last_report_ts.get(wh_name, 0.0) or 0.0) + if now - last < win: + # 命中节流:跳过本次上报 + logger.debug(f"回滚纠偏上报节流命中: wh={wh_name}, dt={now-last:.3f}s") + return + self._rollback_last_report_ts[wh_name] = now + + self.resource_synchronizer._update_resource_flattened([wh]) + + else: + # 找不到仓库时,保守上报整个 deck(该分支不做节流,发生频率应很低) + self.resource_synchronizer._update_resource_flattened([self.deck]) + + except Exception as e: + logger.warning(f"回滚前端虚拟托盘失败: {e}") + + def resource_tree_add(self, resources: List[Resource]): + """ + 处理前端物料添加请求 + - 仅当“进入过渡区入口位点”时触发上料(batch_in_tray) + - 不将该物料作为库存实体留在资源树里(立刻回滚并纠偏上报) + """ + for res in resources: + # 统一提升到托盘根对象 + tray = res + if getattr(res, "category", None) != "bottle_carrier": + if res.parent and getattr(res.parent, "category", None) == "bottle_carrier": + tray = res.parent + elif res.parent and res.parent.parent and getattr(res.parent.parent, "category", None) == "bottle_carrier": + tray = res.parent.parent + + if getattr(tray, "category", None) != "bottle_carrier": + continue + + if not hasattr(tray, "unilabos_extra") or tray.unilabos_extra is None: + tray.unilabos_extra = {} + extra = tray.unilabos_extra + + new_site = self.get_slot_layout_code(tray) # update_resource_site 优先 + old_site = normalize_layout_code(extra.get("eit_last_requested_site")) if extra.get("eit_last_requested_site") else "" + + old_in = self.is_staging_code(old_site) + new_in = self.is_staging_code(new_site) + + # 仅“非入口 -> 入口”触发上料(过渡区) + if (not old_in) and new_in and new_site: + extra["eit_staging_code"] = new_site + logger.info(f"[EIT] add 进入入口位点,上料触发: {new_site}") + ok, reason = self._acquire_batchin_lock(tray, new_site) + if not ok: + logger.info(f"[去重锁] add 上料触发被拦截: {tray.name} -> {new_site}, {reason}") + else: + logger.info(f"[去重锁] add 上料触发通过: {tray.name} -> {new_site}, {reason}") + success = self.resource_synchronizer.sync_to_external(tray) + if success is False: + # 若同步到硬件立刻失败,释放锁以允许重试 + tray_code = self._resolve_tray_code(tray) + if tray_code is not None: + self._release_batchin_lock(tray_code, new_site) + + else: + # 非过渡区 add:不触发硬件,仅视为一次“请求”,马上回滚 + logger.info(f"非过渡区 add 仅记录请求,不入库: {tray.name} -> {new_site}") + + # 记录本次请求位置(用于 update 判定) + extra["eit_last_requested_site"] = new_site or old_site or "" + + # 关键:立即回滚 UI 虚拟托盘,避免前端长期残留 + self._rollback_ui_virtual_tray(tray) + + def resource_tree_remove(self, resources: List[Resource]): + """ + 删除触发下料:只对托盘根对象执行 batch_out_tray。 + 子瓶/item 删除不触发硬件下料。 + """ + trays: List[Resource] = [] + seen = set() + + for res in resources: + tray = res + if getattr(res, "category", None) != "bottle_carrier": + if res.parent and getattr(res.parent, "category", None) == "bottle_carrier": + tray = res.parent + elif res.parent and res.parent.parent and getattr(res.parent.parent, "category", None) == "bottle_carrier": + tray = res.parent.parent + + if getattr(tray, "category", None) != "bottle_carrier": + continue + + tray_uuid = getattr(tray, "unilabos_uuid", None) or id(tray) + if tray_uuid in seen: + continue + seen.add(tray_uuid) + trays.append(tray) + + layout_list = [] + for tray in trays: + eit_code = self._get_eit_layout_code(tray) # 只读 eit_layout_code + if eit_code: + logger.info(f"[EIT] 真正触发硬件下料动作: {eit_code}") + layout_list.append({"layout_code": eit_code}) + + if not layout_list: + return + + def _run_batch_out(): + try: + self.controller.batch_out_tray(layout_list) + except Exception as e: + logger.error(f"[EIT] batch_out_tray 异步执行失败: {e}") + + threading.Thread(target=_run_batch_out, daemon=True).start() + + def resource_tree_transfer(self, old_parent: Optional[Resource], resource: Resource, new_parent: Resource): + """处理资源在设备间迁移时的同步 + + 当资源从一个设备迁移到 Workstation 时,只创建物料(不入库) + 入库操作由后续的 resource_tree_add 完成 + + Args: + old_parent: 资源的原父节点(可能为 None) + resource: 要迁移的资源 + new_parent: 资源的新父节点 + """ + logger.info(f"[Transfer] 资源 {resource.name} 移入工站") + self.resource_tree_add([resource]) + + def resource_tree_update(self, resources: List[Resource]): + """ + 处理资源更新:除“进入过渡区入口”外,任何拖拽移动都不触发硬件动作。 + """ + for res in resources: + # 统一提升到托盘根对象 + tray = res + if getattr(res, "category", None) != "bottle_carrier": + if res.parent and getattr(res.parent, "category", None) == "bottle_carrier": + tray = res.parent + elif res.parent and res.parent.parent and getattr(res.parent.parent, "category", None) == "bottle_carrier": + tray = res.parent.parent + + if getattr(tray, "category", None) != "bottle_carrier": + continue + + if not hasattr(tray, "unilabos_extra") or tray.unilabos_extra is None: + tray.unilabos_extra = {} + extra = tray.unilabos_extra + + new_site = self.get_slot_layout_code(tray) # update_resource_site or parent-derived + old_site = normalize_layout_code(extra.get("eit_last_requested_site")) if extra.get("eit_last_requested_site") else "" + + old_in = self.is_staging_code(old_site) + new_in = self.is_staging_code(new_site) + + # 仅“非入口 -> 入口”触发上料 + if (not old_in) and new_in and new_site: + extra["eit_staging_code"] = new_site + extra["eit_last_batch_in_tray_code"] = new_site + + ok, reason = self._acquire_batchin_lock(tray, new_site) + if not ok: + logger.info(f"[去重锁] update 上料触发被拦截: {tray.name} -> {new_site}, {reason}") + else: + logger.info(f"[去重锁] update 上料触发通过: {tray.name} -> {new_site}, {reason}") + success = self.resource_synchronizer.sync_to_external(tray) + if success is False: + tray_code = self._resolve_tray_code(tray) + if tray_code is not None: + self._release_batchin_lock(tray_code, new_site) + + + # 无论是否入口,上报“本次请求位置” + extra["eit_last_requested_site"] = new_site or old_site or "" + + self._rollback_ui_virtual_tray(tray) + + # ================= 上料请求去重/锁 ================= + + def _resolve_tray_code(self, carrier: Resource) -> Optional[int]: + """ + 尽量用与 sync_to_external 相同的策略解析 tray_code(EIT 的 resource_type)。 + tray_code 将用于生成去重锁 key。 + """ + extra = getattr(carrier, "unilabos_extra", {}) or {} + if not isinstance(extra, dict): + extra = {} + + # 1) model 优先(通常就是 tray_code) + model_val = getattr(carrier, "model", None) + if model_val is not None: + try: + return int(str(model_val).strip()) + except Exception: + pass + + # 2) extra 中常见字段 + for k in ("eit_resource_type", "resource_type", "tray_code"): + if extra.get(k) is not None: + try: + return int(str(extra.get(k)).strip()) + except Exception: + pass + + # 3) 用 name/description 与 TRAY_CODE_DISPLAY_NAME 反查 + desc = str(getattr(carrier, "description", "") or "").strip() + name = str(getattr(carrier, "name", "") or "").strip() + # name 可能是 "xxx@TB-2-1" 这种,去掉后缀 + if "@" in name: + name = name.split("@", 1)[0].strip() + + for key, label in TRAY_CODE_DISPLAY_NAME.items(): + if desc == label or name == label: + try: + return int(key) + except Exception: + continue + + return None + + def _batchin_lock_key(self, tray_code: int, staging_code: str) -> str: + return f"{int(tray_code)}|{normalize_layout_code(staging_code) or staging_code}" + + def _acquire_batchin_lock(self, tray: Resource, staging_code: str) -> Tuple[bool, str]: + """ + 获取上料去重锁: + - 同 tray_code + staging_code 在 TTL 内只允许触发一次 + 返回 (ok, reason) + """ + staging_code = normalize_layout_code(staging_code) or staging_code + tray_code = self._resolve_tray_code(tray) + + if tray_code is None: + # tray_code 解析不到时,不做硬拦截(避免误伤),但会在日志提示 + return True, "tray_code_unresolved_skip_lock" + + key = self._batchin_lock_key(tray_code, staging_code) + now = time.monotonic() + ttl = getattr(self, "_batchin_lock_ttl_s", 300) + + with self._batchin_lock: + # 清理过期锁 + expired = [] + for k, meta in self._batchin_locks.items(): + ts = float(meta.get("ts", 0.0) or 0.0) + if now - ts >= ttl: + expired.append(k) + for k in expired: + self._batchin_locks.pop(k, None) + + # 检查并获取 + if key in self._batchin_locks: + meta = self._batchin_locks[key] + age = now - float(meta.get("ts", now)) + return False, f"dedup_locked(age={age:.1f}s,key={key})" + + self._batchin_locks[key] = {"ts": now, "tray_code": int(tray_code), "staging_code": staging_code} + return True, f"dedup_lock_acquired(key={key})" + + def _release_batchin_lock(self, tray_code: int, staging_code: Optional[str] = None) -> None: + """释放锁:可按 (tray_code + staging_code) 精确释放,也可按 tray_code 批量释放。""" + with self._batchin_lock: + if staging_code: + key = self._batchin_lock_key(int(tray_code), staging_code) + self._batchin_locks.pop(key, None) + return + + # 未指定 staging_code:释放该 tray_code 的所有锁 + remove_keys = [k for k, v in self._batchin_locks.items() if int(v.get("tray_code", -1)) == int(tray_code)] + for k in remove_keys: + self._batchin_locks.pop(k, None) + + def _observe_and_release_batchin_locks(self, hardware_items: Dict[str, Dict[str, Any]]) -> None: + """ + 从工站事实状态中释放锁: + - 当观察到某 tray_code 出现在“非入口位点”时,认为该上料流程已完成(至少已离开入口),释放该 tray_code 的锁 + """ + try: + for code, item in (hardware_items or {}).items(): + # code 是 layout_code(已 normalize) + if self.is_staging_code(code): + continue + + rt = item.get("resource_type") + if rt is None: + continue + try: + tray_code = int(rt) + except Exception: + continue + + # 观测到 tray_code 已在非入口位点 -> 释放该 tray_code 所有锁 + self._release_batchin_lock(tray_code) + + except Exception as e: + logger.warning(f"[去重锁] 观测释放锁失败: {e}") diff --git a/unilabos/devices/eit_synthesis_station/manager/task_ai_manager.py b/unilabos/devices/eit_synthesis_station/manager/task_ai_manager.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_synthesis_station/notification/__init__.py b/unilabos/devices/eit_synthesis_station/notification/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/eit_synthesis_station/notification/email_channel.py b/unilabos/devices/eit_synthesis_station/notification/email_channel.py new file mode 100644 index 00000000..8cd479e1 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/notification/email_channel.py @@ -0,0 +1,110 @@ +import logging +import smtplib +import ssl +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from typing import List + +from .notification_settings import NotificationSettings + + +class EmailChannel: + """ + 功能: + 邮件通知渠道, 基于 SMTP 协议发送 HTML 格式邮件. + 兼容 QQ邮箱(SSL 465)、163邮箱(SSL 465)、Gmail(SSL 465)、Outlook(STARTTLS 587). + 参数: + settings: NotificationSettings, 包含 SMTP 连接与认证配置. + """ + + def __init__(self, settings: NotificationSettings): + self._settings = settings + self._logger = logging.getLogger(self.__class__.__name__) + + def is_available(self) -> bool: + """ + 功能: + 检查 SMTP 参数是否完整配置, 缺少关键字段则不可用. + 参数: + 无. + 返回: + bool, True 表示可发送邮件. + """ + s = self._settings + if not s.smtp_host: + return False + if not s.smtp_user: + return False + if not s.smtp_password: + return False + if not s.email_from: + return False + if not s.get_recipients(): + return False + return True + + def send(self, subject: str, html_body: str) -> bool: + """ + 功能: + 发送 HTML 格式邮件到所有配置的收件人. + smtp_ssl=True 时使用 SMTP_SSL 直连 (QQ/163/Gmail 端口465); + smtp_ssl=False 时使用 SMTP + STARTTLS (Outlook 端口587). + 参数: + subject: str, 邮件主题. + html_body: str, HTML 格式的邮件正文. + 返回: + bool, True 表示发送成功, False 表示发送失败(已记录日志). + """ + if not self.is_available(): + self._logger.warning("邮件渠道未正确配置, 跳过发送") + return False + + s = self._settings + recipients = s.get_recipients() + + # 构建 MIME 邮件 + msg = MIMEMultipart("alternative") + msg["Subject"] = subject + msg["From"] = s.email_from + msg["To"] = ", ".join(recipients) + msg.attach(MIMEText(html_body, "html", "utf-8")) + + try: + if s.smtp_ssl: + # SSL 直连 (QQ邮箱/163邮箱/Gmail, 端口465) + context = ssl.create_default_context() + with smtplib.SMTP_SSL(s.smtp_host, s.smtp_port, context=context, timeout=30) as server: + server.login(s.smtp_user, s.smtp_password) + server.sendmail(s.email_from, recipients, msg.as_string()) + else: + # STARTTLS (Outlook, 端口587) + with smtplib.SMTP(s.smtp_host, s.smtp_port, timeout=30) as server: + server.ehlo() + context = ssl.create_default_context() + server.starttls(context=context) + server.ehlo() + server.login(s.smtp_user, s.smtp_password) + server.sendmail(s.email_from, recipients, msg.as_string()) + + self._logger.info( + "邮件发送成功, 收件人=%s, 主题=%s", + recipients, subject, + ) + return True + + except smtplib.SMTPAuthenticationError as e: + self._logger.error( + "SMTP 认证失败, 请检查用户名和密码/授权码, host=%s, user=%s, err=%s", + s.smtp_host, s.smtp_user, e, + ) + return False + except smtplib.SMTPException as e: + self._logger.error("SMTP 发送异常, err=%s", e) + return False + except OSError as e: + # 网络连接错误 + self._logger.error( + "邮件服务器连接失败, host=%s, port=%s, err=%s", + s.smtp_host, s.smtp_port, e, + ) + return False diff --git a/unilabos/devices/eit_synthesis_station/notification/formatter.py b/unilabos/devices/eit_synthesis_station/notification/formatter.py new file mode 100644 index 00000000..cafe44b7 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/notification/formatter.py @@ -0,0 +1,143 @@ +import datetime +from typing import Any, Dict, List, Tuple + +# Notice API type 字段到中文名称的映射 +_NOTICE_TYPE_NAME = { + 0: "通知", + 1: "故障", + 2: "告警", +} + +# Notice API status 字段到中文名称的映射 +_NOTICE_STATUS_NAME = { + 1: "异常", + 2: "修复中", + 3: "已恢复", +} + + +class NoticeFormatter: + """ + 功能: + 将 Notice API 返回的原始通知数据格式化为邮件内容 (主题 + HTML 正文). + """ + + @staticmethod + def format_email(notices: List[Dict[str, Any]]) -> Tuple[str, str]: + """ + 功能: + 将通知列表格式化为邮件主题和 HTML 正文. + 正文包含一个详细信息表格, 列出每条通知的时间、类型、错误码、影响范围与状态. + 参数: + notices: List[Dict], Notice API 返回的通知列表, 每项包含 + id, type, error_code, created_at, abnormal_impact_range, status, task_id, data. + 返回: + Tuple[str, str], (邮件主题, HTML 正文). + """ + count = len(notices) + + # 统计故障和告警数量, 用于主题 + fault_count = sum(1 for n in notices if n.get("type") == 1) + alarm_count = sum(1 for n in notices if n.get("type") == 2) + type_parts = [] + if fault_count > 0: + type_parts.append(f"{fault_count} 条故障") + if alarm_count > 0: + type_parts.append(f"{alarm_count} 条告警") + # 兜底: 如果都不是故障/告警 + if not type_parts: + type_parts.append(f"{count} 条通知") + + subject = f"[EIT合成工站] 检测到 {'、'.join(type_parts)}" + + # 构建 HTML 表格 + rows_html = "" + for notice in notices: + time_str = NoticeFormatter._format_timestamp(notice.get("created_at")) + type_name = _NOTICE_TYPE_NAME.get(notice.get("type"), str(notice.get("type", ""))) + error_code = str(notice.get("error_code", "")) + impact_range = str(notice.get("abnormal_impact_range", "")) + status_name = _NOTICE_STATUS_NAME.get(notice.get("status"), str(notice.get("status", ""))) + task_id = str(notice.get("task_id", "")) + notice_id = str(notice.get("id", "")) + + rows_html += f""" + + {notice_id} + {time_str} + + + {type_name} + + + {error_code} + {impact_range} + {status_name} + {task_id} + """ + + html_body = f""" + + +

{subject}

+

以下是最新检测到的异常通知详情:

+ + + + + + + + + + + + + + {rows_html} + +
ID时间类型错误码影响范围状态任务ID
+

+ 此邮件由 EIT 合成工站异常通知系统自动发送, 请勿直接回复. +

+ + + """ + + return subject, html_body + + @staticmethod + def _format_timestamp(ts: Any) -> str: + """ + 功能: + 将 Unix 时间戳转换为可读的日期时间字符串. + 参数: + ts: Any, Unix 时间戳(秒), 可以是 int/float/str. + 返回: + str, 格式为 "YYYY-MM-DD HH:MM:SS", 解析失败返回原始值. + """ + if ts is None: + return "" + try: + ts_float = float(ts) + dt = datetime.datetime.fromtimestamp(ts_float) + return dt.strftime("%Y-%m-%d %H:%M:%S") + except (ValueError, TypeError, OSError): + return str(ts) + + @staticmethod + def _type_color(notice_type: Any) -> str: + """ + 功能: + 根据通知类型返回对应的 HTML 颜色值, 用于邮件中突出显示. + 参数: + notice_type: int, 通知类型 (0=通知, 1=故障, 2=告警). + 返回: + str, CSS 颜色值. + """ + colors = { + 0: "#1976d2", # 蓝色 - 通知 + 1: "#d32f2f", # 红色 - 故障 + 2: "#f57c00", # 橙色 - 告警 + } + return colors.get(notice_type, "#333") diff --git a/unilabos/devices/eit_synthesis_station/notification/monitor.py b/unilabos/devices/eit_synthesis_station/notification/monitor.py new file mode 100644 index 00000000..10c93045 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/notification/monitor.py @@ -0,0 +1,364 @@ +import json +import logging +import threading +import time +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Optional, Set + +from .email_channel import EmailChannel +from .formatter import NoticeFormatter +from .notification_settings import NotificationSettings + +# 快照存储目录 +_SNAPSHOTS_DIR = Path(__file__).resolve().parent.parent / "data" / "snapshots" + + +class NotificationMonitor: + """ + 功能: + 后台守护线程, 周期性轮询 Notice API, 检测新的故障/告警通知并发送邮件. + 通过 threading.Event 控制优雅退出, 支持去重与速率限制. + 已通知的 notice_id 持久化到磁盘, 避免重启后重复发送; + 每次检测到的故障/告警快照保存到 data/snapshots/fault_notices.json. + 参数: + controller: SynthesisStationController 实例, 用于调用 notice() 方法. + settings: NotificationSettings, 通知配置. + """ + + # 持久化文件名 + _NOTIFIED_IDS_FILE = "notified_ids.json" + _FAULT_NOTICES_FILE = "fault_notices.json" + + def __init__(self, controller: Any, settings: NotificationSettings): + self._controller = controller + self._settings = settings + self._logger = logging.getLogger(self.__class__.__name__) + + # 邮件渠道 + self._email_channel = EmailChannel(settings) + + # 线程控制 + self._thread: Optional[threading.Thread] = None + self._stop_event = threading.Event() + + # 确保快照目录存在 + _SNAPSHOTS_DIR.mkdir(parents=True, exist_ok=True) + + # 去重: 从磁盘加载已通知的 notice_id 集合, 避免重复发送 + self._notified_ids: Set[int] = self._load_notified_ids() + + # 速率限制: 记录每次发送通知的时间戳 + self._send_timestamps: List[float] = [] + + # 统计信息 + self._total_processed: int = 0 + self._last_poll_time: Optional[float] = None + + def start(self) -> None: + """ + 功能: + 启动后台轮询守护线程. 如果已在运行则跳过. + 参数: + 无. + 返回: + 无. + """ + if self._thread is not None and self._thread.is_alive(): + self._logger.warning("通知监控已在运行, 跳过重复启动") + return + + if not self._email_channel.is_available(): + self._logger.warning("邮件渠道未配置完整, 通知监控启动但无法发送邮件") + + self._stop_event.clear() + self._thread = threading.Thread( + target=self._poll_loop, + name="NotificationMonitor", + daemon=True, # 跟随主进程退出 + ) + self._thread.start() + self._logger.info( + "异常通知监控已启动, 轮询间隔=%.1fs, 监控类型=%s", + self._settings.poll_interval_s, + self._settings.notice_types, + ) + + def stop(self) -> None: + """ + 功能: + 通知守护线程优雅退出, 最多等待 5 秒. + 参数: + 无. + 返回: + 无. + """ + if self._thread is None or not self._thread.is_alive(): + self._logger.info("通知监控未在运行") + return + + self._stop_event.set() + self._thread.join(timeout=5.0) + if self._thread.is_alive(): + self._logger.warning("通知监控线程未在超时内退出") + else: + self._logger.info("异常通知监控已停止") + self._thread = None + + @property + def is_running(self) -> bool: + """ + 功能: + 返回监控线程是否正在运行. + 参数: + 无. + 返回: + bool. + """ + return self._thread is not None and self._thread.is_alive() + + @property + def status_info(self) -> Dict[str, Any]: + """ + 功能: + 返回监控器的当前状态信息, 用于 CLI 状态查询. + 参数: + 无. + 返回: + Dict, 包含 running, total_processed, last_poll_time, processed_ids_count. + """ + return { + "running": self.is_running, + "total_processed": self._total_processed, + "last_poll_time": self._last_poll_time, + "notified_ids_count": len(self._notified_ids), + "email_available": self._email_channel.is_available(), + } + + def _poll_loop(self) -> None: + """ + 功能: + 主轮询循环: 周期性获取通知 -> 保存快照 -> 过滤新通知 -> 发送邮件. + 使用 _stop_event.wait() 替代 time.sleep(), 确保可快速响应停止信号. + 参数: + 无. + 返回: + 无. + """ + self._logger.info("通知监控轮询循环已启动") + + while not self._stop_event.is_set(): + try: + self._last_poll_time = time.time() + + # 获取通知 + notices = self._fetch_notices() + if notices: + # 保存故障快照到磁盘 + self._save_fault_snapshot(notices) + + # 过滤出未通知过的新通知 + new_notices = self._filter_new(notices) + if new_notices: + self._send_email(new_notices) + + except Exception as e: + # 捕获所有异常, 确保循环不中断 + self._logger.error("通知监控轮询异常, 将在下一周期重试: %s", e) + + # 等待下一轮, 可被 stop() 信号快速唤醒 + self._stop_event.wait(timeout=self._settings.poll_interval_s) + + self._logger.info("通知监控轮询循环已退出") + + def _fetch_notices(self) -> List[Dict[str, Any]]: + """ + 功能: + 调用 controller.notice() 获取指定类型的通知列表. + 复用 controller 层的 _call_with_relogin 机制处理 401. + 参数: + 无. + 返回: + List[Dict], 通知列表. 获取失败返回空列表. + """ + try: + resp = self._controller.notice(types=self._settings.notice_types) + notice_list = resp.get("list", []) + if not isinstance(notice_list, list): + return [] + return notice_list + except Exception as e: + self._logger.warning("获取通知列表失败: %s", e) + return [] + + def _filter_new(self, notices: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + """ + 功能: + 过滤出未通知过的新通知: + - 跳过已持久化记录的 id (已发送过邮件的不再重复发送) + - 跳过状态为 FIXED(3) 的通知 + 参数: + notices: List[Dict], 从 Notice API 获取的原始通知列表. + 返回: + List[Dict], 需要发送通知的新条目. + """ + new_notices = [] + + for notice in notices: + notice_id = notice.get("id") + if notice_id is None: + continue + + # 跳过已恢复的通知 + status = notice.get("status") + if status == 3: + continue + + # 跳过已通知过的 notice_id (持久化去重, 不再基于冷却时间) + if notice_id in self._notified_ids: + continue + + new_notices.append(notice) + + return new_notices + + def _send_email(self, notices: List[Dict[str, Any]]) -> None: + """ + 功能: + 格式化通知内容并通过邮件渠道发送. + 发送成功后将 notice_id 持久化到磁盘, 确保不再重复发送. + 参数: + notices: List[Dict], 需要发送的通知列表. + 返回: + 无. + """ + # 检查速率限制 + if not self._check_rate_limit(): + self._logger.warning( + "已达到每小时最大通知数(%d), 暂停发送", + self._settings.max_notifications_per_hour, + ) + return + + # 格式化邮件 + subject, html_body = NoticeFormatter.format_email(notices) + + # 发送 + success = self._email_channel.send(subject, html_body) + + now = time.time() + if success: + # 将已通知的 id 加入集合并持久化到磁盘 + for notice in notices: + notice_id = notice.get("id") + if notice_id is not None: + self._notified_ids.add(notice_id) + self._save_notified_ids() + self._total_processed += len(notices) + self._send_timestamps.append(now) + self._logger.info("已发送 %d 条异常通知邮件", len(notices)) + else: + self._logger.error("异常通知邮件发送失败, 将在下一轮重试") + + def _check_rate_limit(self) -> bool: + """ + 功能: + 检查是否超过每小时最大通知数. + 参数: + 无. + 返回: + bool, True 表示未超限可继续发送, False 表示已超限. + """ + now = time.time() + one_hour_ago = now - 3600.0 + + # 清理一小时前的记录 + self._send_timestamps = [ts for ts in self._send_timestamps if ts > one_hour_ago] + + return len(self._send_timestamps) < self._settings.max_notifications_per_hour + + def _load_notified_ids(self) -> Set[int]: + """ + 功能: + 从磁盘加载已通知的 notice_id 集合. + 文件不存在或解析失败时返回空集合. + 参数: + 无. + 返回: + Set[int], 已通知的 notice_id 集合. + """ + file_path = _SNAPSHOTS_DIR / self._NOTIFIED_IDS_FILE + if not file_path.exists(): + return set() + try: + with open(file_path, "r", encoding="utf-8") as f: + data = json.load(f) + ids = set(int(i) for i in data.get("notified_ids", [])) + self._logger.info("从磁盘加载了 %d 条已通知记录", len(ids)) + return ids + except Exception as e: + self._logger.warning("加载已通知记录失败, 将重新开始: %s", e) + return set() + + def _save_notified_ids(self) -> None: + """ + 功能: + 将已通知的 notice_id 集合持久化到磁盘, 确保重启后不重复发送. + 参数: + 无. + 返回: + 无. + """ + file_path = _SNAPSHOTS_DIR / self._NOTIFIED_IDS_FILE + try: + data = { + "updated_at": datetime.now().isoformat(), + "notified_ids": sorted(self._notified_ids), + } + with open(file_path, "w", encoding="utf-8") as f: + json.dump(data, f, ensure_ascii=False, indent=2) + except Exception as e: + self._logger.warning("持久化已通知记录失败: %s", e) + + def _save_fault_snapshot(self, notices: List[Dict[str, Any]]) -> None: + """ + 功能: + 将本轮获取到的故障/告警通知保存到 data/snapshots/fault_notices.json. + 采用追加合并模式: 按 notice_id 去重, 保留最新状态. + 参数: + notices: List[Dict], 从 Notice API 获取的通知列表. + 返回: + 无. + """ + file_path = _SNAPSHOTS_DIR / self._FAULT_NOTICES_FILE + try: + # 读取已有快照 + existing: Dict[str, Any] = {} + if file_path.exists(): + with open(file_path, "r", encoding="utf-8") as f: + existing = json.load(f) + + # 按 notice_id 索引已有记录 + records: Dict[int, Any] = {} + for item in existing.get("notices", []): + nid = item.get("id") + if nid is not None: + records[nid] = item + + # 合并本轮通知, 覆盖同 id 的旧记录 + for notice in notices: + nid = notice.get("id") + if nid is not None: + records[nid] = notice + + # 按 id 排序后写回 + snapshot = { + "updated_at": datetime.now().isoformat(), + "count": len(records), + "notices": sorted(records.values(), key=lambda x: x.get("id", 0)), + } + with open(file_path, "w", encoding="utf-8") as f: + json.dump(snapshot, f, ensure_ascii=False, indent=2) + + except Exception as e: + self._logger.warning("保存故障快照失败: %s", e) diff --git a/unilabos/devices/eit_synthesis_station/notification/notification_settings.py b/unilabos/devices/eit_synthesis_station/notification/notification_settings.py new file mode 100644 index 00000000..744f74f8 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/notification/notification_settings.py @@ -0,0 +1,146 @@ +import logging +import os +from dataclasses import dataclass, field +from typing import List + + +@dataclass +class NotificationSettings: + """ + 功能: + 异常通知子系统配置, 控制轮询行为、防刷策略及邮件 SMTP 参数. + 采用与 Settings 相同的 dataclass + from_env() 模式. + 参数: + enabled: 总开关, 默认关闭. + poll_interval_s: 轮询 Notice API 的间隔(秒). + notice_types: 监控的通知类型列表, 1=FAULT, 2=ALARM. + cooldown_s: 同一 notice id 的通知冷却时间(秒), 避免重复发送. + max_notifications_per_hour: 每小时最大通知数, 超限后暂停发送. + smtp_host: SMTP 服务器地址, 例如 "smtp.qq.com". + smtp_port: SMTP 端口, SSL 通常为 465, STARTTLS 通常为 587. + smtp_ssl: 是否使用 SSL 直连(True) 或 STARTTLS(False). + smtp_user: SMTP 认证用户名. + smtp_password: SMTP 认证密码/授权码 (QQ邮箱填授权码, 非QQ密码). + email_from: 发件人邮箱地址. + email_to: 收件人邮箱地址, 多个用逗号分隔. + 返回: + NotificationSettings. + """ + + # --- 总开关与轮询 --- + enabled: bool = True + poll_interval_s: float = 10.0 + notice_types: List[int] = field(default_factory=lambda: [1, 2]) + + # --- 防刷 --- + cooldown_s: float = 300.0 + max_notifications_per_hour: int = 30 + + # --- 邮件 (SMTP) --- + smtp_host: str = "smtp.qq.com" + smtp_port: int = 465 + smtp_ssl: bool = True + smtp_user: str = "718118082@qq.com" + smtp_password: str = "ppwcaplfqghfbcdh" + email_from: str = "718118082@qq.com" + email_to: str = "718118082@qq.com" + + @staticmethod + def from_env() -> "NotificationSettings": + """ + 功能: + 从环境变量读取通知配置, 前缀为 SYN_STATION_NOTIFY_. + 参数: + 无. + 返回: + NotificationSettings. + 环境变量: + SYN_STATION_NOTIFY_ENABLED, SYN_STATION_NOTIFY_POLL_INTERVAL_S, + SYN_STATION_NOTIFY_NOTICE_TYPES, SYN_STATION_NOTIFY_COOLDOWN_S, + SYN_STATION_NOTIFY_MAX_NOTIFICATIONS_PER_HOUR, + SYN_STATION_NOTIFY_SMTP_HOST, SYN_STATION_NOTIFY_SMTP_PORT, + SYN_STATION_NOTIFY_SMTP_SSL, SYN_STATION_NOTIFY_SMTP_USER, + SYN_STATION_NOTIFY_SMTP_PASSWORD, SYN_STATION_NOTIFY_EMAIL_FROM, + SYN_STATION_NOTIFY_EMAIL_TO. + """ + defaults = NotificationSettings() + + # --- 总开关与轮询 --- + enabled_str = os.getenv("SYN_STATION_NOTIFY_ENABLED", str(defaults.enabled)) + enabled = enabled_str.strip().lower() in ("1", "true", "yes", "y", "on") + + poll_str = os.getenv("SYN_STATION_NOTIFY_POLL_INTERVAL_S", str(defaults.poll_interval_s)) + try: + poll_interval_s = float(poll_str) + except ValueError: + poll_interval_s = defaults.poll_interval_s + + # notice_types: 逗号分隔的整数, 例如 "1,2" + types_str = os.getenv("SYN_STATION_NOTIFY_NOTICE_TYPES", "1,2") + notice_types = [] + for t in types_str.split(","): + t = t.strip() + if t.isdigit(): + notice_types.append(int(t)) + if not notice_types: + notice_types = list(defaults.notice_types) + + # --- 防刷 --- + cooldown_str = os.getenv("SYN_STATION_NOTIFY_COOLDOWN_S", str(defaults.cooldown_s)) + try: + cooldown_s = float(cooldown_str) + except ValueError: + cooldown_s = defaults.cooldown_s + + max_per_hour_str = os.getenv( + "SYN_STATION_NOTIFY_MAX_NOTIFICATIONS_PER_HOUR", + str(defaults.max_notifications_per_hour), + ) + try: + max_notifications_per_hour = int(max_per_hour_str) + except ValueError: + max_notifications_per_hour = defaults.max_notifications_per_hour + + # --- 邮件 (SMTP) --- + smtp_host = os.getenv("SYN_STATION_NOTIFY_SMTP_HOST", defaults.smtp_host) + smtp_port_str = os.getenv("SYN_STATION_NOTIFY_SMTP_PORT", str(defaults.smtp_port)) + try: + smtp_port = int(smtp_port_str) + except ValueError: + smtp_port = defaults.smtp_port + + smtp_ssl_str = os.getenv("SYN_STATION_NOTIFY_SMTP_SSL", str(defaults.smtp_ssl)) + smtp_ssl = smtp_ssl_str.strip().lower() in ("1", "true", "yes", "y", "on") + + smtp_user = os.getenv("SYN_STATION_NOTIFY_SMTP_USER", defaults.smtp_user) + smtp_password = os.getenv("SYN_STATION_NOTIFY_SMTP_PASSWORD", defaults.smtp_password) + email_from = os.getenv("SYN_STATION_NOTIFY_EMAIL_FROM", defaults.email_from) + email_to = os.getenv("SYN_STATION_NOTIFY_EMAIL_TO", defaults.email_to) + + return NotificationSettings( + enabled=enabled, + poll_interval_s=poll_interval_s, + notice_types=notice_types, + cooldown_s=cooldown_s, + max_notifications_per_hour=max_notifications_per_hour, + smtp_host=smtp_host, + smtp_port=smtp_port, + smtp_ssl=smtp_ssl, + smtp_user=smtp_user, + smtp_password=smtp_password, + email_from=email_from, + email_to=email_to, + ) + + def get_recipients(self) -> List[str]: + """ + 功能: + 解析 email_to 字段, 返回收件人地址列表. + 参数: + 无. + 返回: + List[str], 非空收件人邮箱列表. + """ + if not self.email_to: + return [] + return [addr.strip() for addr in self.email_to.split(",") if addr.strip()] diff --git a/unilabos/devices/eit_synthesis_station/printer/25x10x2.yaml b/unilabos/devices/eit_synthesis_station/printer/25x10x2.yaml new file mode 100644 index 00000000..c5cc5a9a --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/25x10x2.yaml @@ -0,0 +1,28 @@ +printer: + port: "Gprinter GP-1134T" # Windows打印机名称(佳博等非TSC打印机不能用"USB", 需填写系统中的打印机名称) + ppi: 300 # 打印机分辨率(DPI): 常见值 203 或 300 + +# 纸张参数 +paper: + width: 56 # 纸张总宽度(mm) + height: 10 # 单个标签高度(mm) + unit: mm + columns: 2 # 列数 + column_gap: 3 # 列间距(mm) + margin: 1.3 # 左右边间距(mm) + gap: 2 # 连续纸设为0, 有间隙标签纸填实际间距(mm) + gap_offset: 0 # 间隙偏移(mm) + direction: 1 # 打印方向: 0或1 + +# 字体参数 (Windows字体, 支持中文) +font: + name: Arial # 字体名称(ASCII, 如: Arial, SimHei, SimSun) + size: 60 # 字号上限(dot). 实际字号 = min(此值, 标签高度上限). 仅文字溢出时缩小 + bold: 0 # 粗体: 0=否, 1=是 + underline: 0 # 下划线: 0=否, 1=是 + rotation: 0 # 旋转角度: 0/90/180/270 + +# 文字打印位置微调 (全局偏移, 单位mm, 正值=向右/向下) +position: + x: 1.5 + y: 1 diff --git a/unilabos/devices/eit_synthesis_station/printer/__init__.py b/unilabos/devices/eit_synthesis_station/printer/__init__.py new file mode 100644 index 00000000..7907ca3d --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from .label_print_service import LabelPrintService + +__all__ = ["LabelPrintService"] diff --git a/unilabos/devices/eit_synthesis_station/printer/label_print_service.py b/unilabos/devices/eit_synthesis_station/printer/label_print_service.py new file mode 100644 index 00000000..f2f09627 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/label_print_service.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +""" +功能: + TSC标签打印服务类. 封装print_text.py的核心功能为可复用接口. + 支持中文, 自动缩放字号, 自动居中对齐, 兼容单列和多列标签纸. + +用法: + from eit_synthesis_station.printer import LabelPrintService + + svc = LabelPrintService("path/to/25x10x2.yaml") + svc.connect() + svc.print_batch(["试剂A", "试剂B", "试剂C"]) + svc.disconnect() +""" + +import logging +import os +from typing import List, Optional + +from .print_text import ( + load_config, + load_dll, + check_printer_ready, + execute_print_job, +) + +logger = logging.getLogger(__name__) + +# 默认配置文件: 与本文件同目录下的 25x10x2.yaml +_DEFAULT_CONFIG = os.path.join(os.path.dirname(os.path.abspath(__file__)), "25x10x2.yaml") +# DLL 默认路径 +_DEFAULT_DLL = os.path.join(os.path.dirname(os.path.abspath(__file__)), "libs", "TSCLIB.dll") + + +class LabelPrintService: + """ + 功能: + TSC标签打印服务. 基于print_text.py的windowsfontUnicode方案, + 支持中文, 自动缩放字号, 自动居中对齐. + 兼容单列和多列标签纸, 按列数自动分组打印. + 参数: + config_path: str, 标签规格YAML配置文件路径. 默认使用 25x10x2.yaml. + """ + + def __init__(self, config_path: Optional[str] = None): + if config_path is None: + config_path = _DEFAULT_CONFIG + self._config_path = config_path + self._config = load_config(config_path) + self._dll_path = _DEFAULT_DLL + self._lib = None + self._connected = False + + # ──────────────────── 连接管理 ──────────────────── + + def connect(self) -> bool: + """ + 功能: + 加载DLL并预检打印机可用性. + 预检结束后立即关闭端口, 避免长期占用同一个打印会话. + 返回: + bool, 连接成功返回True. + """ + if self._connected: + return True + try: + if self._lib is None: + self._lib = load_dll(self._dll_path) + check_printer_ready(self._lib, self._config) + self._connected = True + logger.debug("标签打印服务已就绪") + return True + except Exception as exc: + logger.error("标签打印服务连接失败: %s", exc) + self._lib = None + self._connected = False + return False + + def disconnect(self) -> None: + """ + 功能: + 释放打印服务资源. + """ + self._lib = None + self._connected = False + logger.debug("标签打印服务已断开") + + @property + def connected(self) -> bool: + """返回当前连接状态.""" + return self._connected + + @property + def columns(self) -> int: + """返回配置中的标签列数.""" + return self._config.get("paper", {}).get("columns", 1) + + # ──────────────────── 打印方法 ──────────────────── + + def print_label(self, texts: List[str], copies: int = 1) -> bool: + """ + 功能: + 打印一张标签纸(一行), 每列分别渲染对应文字. + 自动计算字号和居中位置. + 参数: + texts: List[str], 每列的文字内容. 长度应 <= 列数, 不足的列留空. + copies: int, 打印份数, 默认1. + 返回: + bool, 打印指令发送成功返回True. + """ + if not self._connected: + if not self.connect(): + return False + try: + for _ in range(copies): + execute_print_job(self._lib, self._config, texts) + return True + except Exception as exc: + logger.error("标签打印失败: %s", exc) + return False + + def print_batch(self, text_list: List[str], copies_each: int = 1) -> bool: + """ + 功能: + 批量打印多张标签. 将text_list按列数自动分组, + 每组占一行标签纸, 最后不足一组的也打印. + 参数: + text_list: List[str], 所有要打印的文字内容(扁平列表). + copies_each: int, 每张标签的打印份数. + 返回: + bool, 全部打印成功返回True. + """ + if not text_list: + logger.warning("没有需要打印的内容") + return True + + if not self._connected: + if not self.connect(): + return False + + cols = self.columns + total = len(text_list) + success = True + + try: + # 按列数分组 + for batch_start in range(0, total, cols): + batch = text_list[batch_start:batch_start + cols] + if not self.print_label(batch, copies=copies_each): + success = False + break + + if success: + logger.debug("批量打印完成: 共 %d 张标签", total) + except Exception as exc: + logger.error("批量打印失败: %s", exc) + success = False + + return success diff --git a/unilabos/devices/eit_synthesis_station/printer/libs/.gitignore b/unilabos/devices/eit_synthesis_station/printer/libs/.gitignore new file mode 100644 index 00000000..3abaa09f --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/libs/.gitignore @@ -0,0 +1,2 @@ +# 厂商打印驱动二进制,不参与版本库;请从供应商获取后放置于此目录 +*.dll diff --git a/unilabos/devices/eit_synthesis_station/printer/print_text.py b/unilabos/devices/eit_synthesis_station/printer/print_text.py new file mode 100644 index 00000000..29445209 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/print_text.py @@ -0,0 +1,598 @@ +""" +功能: + TSPL兼容标签打印机交互式打印脚本. + 通过Windows打印机名称连接打印机, 用户输入文字后直接打印. + 支持TSC, 佳博(Gainscha)等TSPL兼容打印机. + 纸张/字体/位置参数保存在config.yaml中. + +依赖: + pyyaml (pip install pyyaml) + +用法: + python print_text.py +""" + +import ctypes +import logging +import os +import subprocess +import sys + +import yaml + +# ──────────────────────────── 日志配置 ──────────────────────────── +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + datefmt="%H:%M:%S", +) +logger = logging.getLogger(__name__) + +# ──────────────────────────── 常量 ──────────────────────────────── +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +CONFIG_PATH = os.path.join(SCRIPT_DIR, "25x10x2.yaml") +DLL_PATH = os.path.join(SCRIPT_DIR, "libs", "TSCLIB.dll") + +# 默认配置, 首次运行时写入config.yaml +DEFAULT_CONFIG = { + "printer": { + "port": "Gprinter GP-1134T", # Windows打印机名称 + "ppi": 300, + }, + "paper": { + "width": 56, + "height": 10, + "unit": "mm", + "columns": 2, + "column_gap": 3, + "margin": 1.8, + "gap": 0, + "gap_offset": 0, + "direction": 1, + }, + "font": { + "name": "微软雅黑", + "size": 60, # 字号上限(dot), 仅溢出时缩小 + "bold": 0, + "underline": 0, + "rotation": 0, + }, + "position": { + "x": 10, + "y": 30, + }, +} + + +def load_config(path): + """ + 功能: + 读取YAML配置文件, 若不存在则创建默认配置. + + 参数: + path: str, 配置文件路径 + + 返回: + dict, 配置字典 + """ + if not os.path.exists(path): + logger.info("配置文件不存在, 正在生成默认配置: %s", path) + save_config(path, DEFAULT_CONFIG) + return DEFAULT_CONFIG.copy() + + with open(path, "r", encoding="utf-8") as f: + config = yaml.safe_load(f) + + logger.debug("已加载配置: %s", path) + return config + + +def save_config(path, config): + """ + 功能: + 将配置字典写入YAML文件. + + 参数: + path: str, 配置文件路径 + config: dict, 配置字典 + """ + with open(path, "w", encoding="utf-8") as f: + yaml.dump(config, f, default_flow_style=False, allow_unicode=True, sort_keys=False) + + +def load_dll(dll_path): + """ + 功能: + 加载TSCLIB.dll动态链接库. + + 参数: + dll_path: str, DLL文件路径 + + 返回: + ctypes.WinDLL 实例 + """ + if not os.path.exists(dll_path): + logger.error("找不到DLL文件: %s", dll_path) + sys.exit(1) + + lib = ctypes.WinDLL(dll_path) + logger.debug("已加载DLL: %s", dll_path) + + # 声明常用函数的参数类型, 确保ctypes正确传递参数 + wstr = ctypes.c_wchar_p + lib.openportW.argtypes = [wstr] + lib.openportW.restype = ctypes.c_int # 0=失败, 非0=成功 + lib.closeport.argtypes = [] + lib.sendcommandW.argtypes = [wstr] + lib.printlabelW.argtypes = [wstr, wstr] + # windowsfontUnicode(x, y, fontheight, rotation, fontstyle, fontunderline, szFaceName, content) + # 前6个int, 第7个char*(字体名), 第8个void*(UTF-16LE字节缓冲区, 不能用c_char_p会截断\x00) + lib.windowsfontUnicode.argtypes = [ + ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, + ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p, + ] + lib.windowsfontUnicode.restype = ctypes.c_int + return lib + + +def list_windows_printers(): + """ + 功能: + 列出系统中已安装的Windows打印机名称, 帮助用户查找正确的打印机端口名称. + + 返回: + list[str], 打印机名称列表. 获取失败时返回空列表. + """ + try: + result = subprocess.run( + ["powershell", "-Command", + "Get-Printer | Select-Object -ExpandProperty Name"], + capture_output=True, text=True, timeout=5, + ) + if result.returncode == 0: + return [line.strip() for line in result.stdout.strip().split("\n") if line.strip()] + except Exception as e: + logger.warning("无法枚举系统打印机: %s", e) + return [] + + +def calc_label_width(paper): + """ + 功能: + 根据纸张总宽度/列数/边距/列间距计算单个标签的宽度. + + 参数: + paper: dict, 纸张配置 + + 返回: + float, 单个标签宽度(与paper.unit同单位) + """ + total_width = paper["width"] + columns = paper.get("columns", 1) + margin = paper.get("margin", 0) + column_gap = paper.get("column_gap", 0) + + # 标签宽度 = (总宽 - 左右边距×2 - 列间距×(列数-1)) / 列数 + label_width = (total_width - margin * 2 - column_gap * (columns - 1)) / columns + return round(label_width, 2) + + +def calc_dots_per_mm(config): + """ + 功能: + 根据配置中的PPI计算每毫米点数. + + 参数: + config: dict, 配置字典 + + 返回: + float, 每毫米的点数(dots per mm) + """ + ppi = config["printer"].get("ppi", 203) + # 1英寸 = 25.4mm + return ppi / 25.4 + + +def calc_auto_layout(paper, font_cfg, text, dots_per_mm): + """ + 功能: + 根据标签尺寸和YAML字号上限计算布局. + 字号由标签物理尺寸决定, 不随文字长度逐字变化. + 仅当文字溢出一行时才尝试换行, 换行仍溢出才缩小字号. + + 参数: + paper: dict, 纸张配置 + font_cfg: dict, 字体配置 (font_cfg["size"]为字号上限, 单位dot) + text: str, 要打印的文字 + dots_per_mm: float, 每毫米点数(由PPI计算得到) + + 返回: + list[tuple(x, y, font_height, line_text)], 每行布局信息. + x/y 是相对于单个标签左上角的偏移(dot). + """ + label_w_mm = calc_label_width(paper) + label_h_mm = paper["height"] + label_w = label_w_mm * dots_per_mm + label_h = label_h_mm * dots_per_mm + + # 80%可用区域 + usable_w = label_w * 0.8 + usable_h = label_h * 0.8 + + # YAML中的size字段作为字号上限(dot) + max_font = font_cfg.get("size", 60) + MIN_FONT = 8 + + font_height = None + final_lines = None + + # 依次尝试1~3行, 优先保持max_font不缩小 + for num_lines in range(1, 4): + # 该行数下的高度上限: 可用高度/行数 * 0.9 (留行间距) + h_cap = int(usable_h / num_lines * 0.9) + fh = min(max_font, h_cap) + + if fh < MIN_FONT: + continue + + # 拆分文字 + lines = [text] if num_lines == 1 else _split_text(text, num_lines) + + # 检查所有行在fh字号下是否都不超宽 + all_fit = True + for line in lines: + wf = _calc_width_factor(line) + if fh * wf > usable_w: + all_fit = False + break + + if all_fit: + font_height = fh + final_lines = lines + break + else: + # 3行仍溢出, 在3行基础上缩小字号直到适配 + final_lines = _split_text(text, 3) + h_cap = int(usable_h / 3 * 0.9) + font_height = min(max_font, h_cap) + for line in final_lines: + wf = _calc_width_factor(line) + if wf > 0: + font_height = min(font_height, int(usable_w / wf)) + if font_height < MIN_FONT: + font_height = MIN_FONT + + # 计算每行居中坐标 + total_text_h = font_height * len(final_lines) + max(0, len(final_lines) - 1) * int(font_height * 0.15) + y_start = int((label_h - total_text_h) / 2) + if y_start < 0: + y_start = 0 + + result = [] + for i, line in enumerate(final_lines): + wf = _calc_width_factor(line) + text_w = font_height * wf + x = int((label_w - text_w) / 2) + if x < 0: + x = 0 + y = y_start + int(i * font_height * 1.15) + result.append((x, y, font_height, line)) + + logger.debug("自动布局: 单标签%.1fx%.1fmm, %d行, 字号%d (上限%d)", + label_w_mm, label_h_mm, len(final_lines), font_height, max_font) + return result + + +def _calc_width_factor(text): + """ + 功能: + 计算文字的宽度系数. CJK字符宽度约等于字高, ASCII约为字高的0.55倍. + 参数: + text: str, 文字内容. + 返回: + float, 宽度系数 (乘以字号即为文字像素宽度). + """ + cjk_count = 0 + ascii_count = 0 + for ch in text: + if ord(ch) > 0x7F: + cjk_count += 1 + else: + ascii_count += 1 + factor = cjk_count * 1.0 + ascii_count * 0.55 + return factor if factor > 0 else 1 + + +def _split_text(text, max_lines): + """ + 功能: + 将文字按自然断点拆分为指定行数, 尽量在空格/括号/逗号处断开. + 参数: + text: str, 原始文字. + max_lines: int, 目标行数. + 返回: + list[str], 拆分后的文字行列表. + """ + if max_lines <= 1 or len(text) <= 1: + return [text] + + # 按宽度系数找分割点, 使每段宽度尽量均匀 + total_wf = _calc_width_factor(text) + target_wf = total_wf / max_lines + + lines = [] + remaining = text + for line_idx in range(max_lines - 1): + best_pos = -1 + accum_wf = 0 + # 遍历字符累加宽度, 在超过目标宽度附近找最佳断点 + for i, ch in enumerate(remaining): + accum_wf += 1.0 if ord(ch) > 0x7F else 0.55 + # 在目标宽度的 70%~130% 范围内寻找自然断点 + if accum_wf >= target_wf * 0.7: + if ch in " ,,;;()()/\\-": + best_pos = i + 1 # 断点后面 + elif i + 1 < len(remaining) and remaining[i + 1] in " ,,;;()()/\\-": + best_pos = i + 1 + if accum_wf >= target_wf * 1.3: + break + + # 没找到自然断点, 按宽度均分强制切 + if best_pos <= 0: + accum_wf = 0 + for i, ch in enumerate(remaining): + accum_wf += 1.0 if ord(ch) > 0x7F else 0.55 + if accum_wf >= target_wf: + best_pos = i + 1 + break + if best_pos <= 0: + best_pos = len(remaining) // 2 + + lines.append(remaining[:best_pos].strip()) + remaining = remaining[best_pos:].strip() + + if remaining: + lines.append(remaining) + + return [l for l in lines if l] + + +def init_printer(lib, config): + """ + 功能: + 打开打印机端口并发送纸张初始化命令(SIZE/GAP/DIRECTION). + 自动根据列数和边距计算单个标签尺寸. + + 参数: + lib: ctypes.WinDLL, TSC库实例 + config: dict, 配置字典 + """ + port = config["printer"]["port"] + paper = config["paper"] + unit = paper["unit"] + + # 连接打印机并检查返回值 + ret = lib.openportW(port) + if ret == 0: + available = list_windows_printers() + hint = "" + if available: + hint = f" 系统中可用的打印机: {', '.join(available)}" + raise RuntimeError( + f"无法连接打印机, 端口/名称: '{port}'.{hint}" + " 请检查配置中的打印机名称是否与系统中的名称一致" + ) + logger.debug("已连接打印机, 端口: %s", port) + + # SIZE使用纸张总宽度, 打印机传感器自动识别多列布局 + size_cmd = f"SIZE {paper['width']} {unit}, {paper['height']} {unit}" + lib.sendcommandW(size_cmd) + logger.debug("纸张尺寸: %s", size_cmd) + + # 设置上下间隙 + gap_cmd = f"GAP {paper['gap']} {unit}, {paper['gap_offset']} {unit}" + lib.sendcommandW(gap_cmd) + + # 设置打印方向 + lib.sendcommandW(f"DIRECTION {paper['direction']}") + + +def print_text(lib, config, texts): + """ + 功能: + 将文字列表发送到打印机并打印一张标签(支持多列, 每列不同内容). + 自动根据纸张尺寸和文字长度计算每列的字号及居中位置. + + 参数: + lib: ctypes.WinDLL, TSC库实例 + config: dict, 配置字典 + texts: list[str], 每列要打印的文字内容, 长度应等于列数 + """ + font = config["font"] + paper = config["paper"] + columns = paper.get("columns", 1) + margin = paper.get("margin", 0) + column_gap = paper.get("column_gap", 0) + + # 从配置计算DPI转换系数 + dots_per_mm = calc_dots_per_mm(config) + + # 计算单个标签宽度(mm) + label_width_mm = calc_label_width(paper) + + # 读取位置偏移量 (用于物理打印机对齐微调, 单位mm, 转换为dot) + pos_cfg = config.get("position", {}) + offset_x = int(float(pos_cfg.get("x", 0)) * dots_per_mm) + offset_y = int(float(pos_cfg.get("y", 0)) * dots_per_mm) + + # 清除打印缓冲区 + lib.sendcommandW("CLS") + + font_name = font.get("name", "Arial").encode("ascii") + + # 为每一列绘制对应文字 + for col in range(columns): + text = texts[col] if col < len(texts) else "" + if text == "": + continue + + # 每列独立计算字号和居中坐标, 支持自动换行 + layout_lines = calc_auto_layout(paper, font, text, dots_per_mm) + + # 列起始x偏移(mm) = 左边距 + 列序号 * (标签宽度 + 列间距) + col_origin_mm = margin + col * (label_width_mm + column_gap) + col_origin_dot = int(col_origin_mm * dots_per_mm) + + for center_x, center_y, font_height, line_text in layout_lines: + # 绝对坐标 = 列起始偏移 + 标签内居中偏移 + 配置微调偏移 + abs_x = col_origin_dot + center_x + offset_x + abs_y = center_y + offset_y + + # UTF-16LE编码后追加终止符, 用create_string_buffer避免c_char_p截断\x00 + raw = line_text.encode("utf-16-le") + b"\x00\x00" + content_buf = ctypes.create_string_buffer(raw) + + lib.windowsfontUnicode( + abs_x, abs_y, font_height, + int(font.get("rotation", 0)), + int(font.get("bold", 0)), + int(font.get("underline", 0)), + font_name, + ctypes.cast(content_buf, ctypes.c_void_p), + ) + logger.debug("列%d: 文字'%s' (%d行), 列起始%.1fmm(%ddot)", + col + 1, text, len(layout_lines), col_origin_mm, col_origin_dot) + + # 所有列绘制完毕后统一打印 + lib.printlabelW("1", "1") + logger.debug("已发送打印(%d列): %s", columns, " | ".join(texts)) + + +def check_printer_ready(lib, config): + """ + 功能: + 预检打印机连接. 启动时短暂打开并关闭一次打印端口, 用于确认配置可用, + 同时避免长时间占用同一个打印会话导致作业延迟提交. + + 参数: + lib: ctypes.WinDLL, TSC库实例. + config: dict, 配置字典. + """ + session_open = False + try: + init_printer(lib, config) + session_open = True + finally: + if session_open: + close_printer(lib) + + +def execute_print_job(lib, config, texts): + """ + 功能: + 执行一次完整打印作业. 每次打印都重新打开和关闭端口, 确保Windows打印队列 + 在单次作业结束后立即提交, 不会等到脚本退出时才真正出纸. + + 参数: + lib: ctypes.WinDLL, TSC库实例. + config: dict, 配置字典. + texts: list[str], 每列要打印的文字内容. + """ + session_open = False + try: + init_printer(lib, config) + session_open = True + print_text(lib, config, texts) + finally: + if session_open: + close_printer(lib) + + +def close_printer(lib): + """ + 功能: + 关闭打印机端口连接. + + 参数: + lib: ctypes.WinDLL, TSC库实例 + """ + lib.closeport() + logger.debug("打印机连接已关闭") + + +def main(): + """ + 功能: + 交互式打印主循环. + 加载配置和DLL, 连接打印机, 循环等待用户输入文字并打印. + """ + logger.info("=== TSC交互式打印脚本启动 ===") + + # 加载配置和DLL + config = load_config(CONFIG_PATH) + lib = load_dll(DLL_PATH) + + # 启动时预检一次, 但不长期占用端口. + try: + check_printer_ready(lib, config) + except Exception as e: + logger.error("打印机初始化失败: %s", e) + sys.exit(1) + + columns = config["paper"].get("columns", 1) + + logger.info("打印机就绪, 等待输入...") + print("-" * 40) + if columns > 1: + print(f"当前配置: {columns}列标签, 每次需依次输入{columns}列内容") + print("输入要打印的文字后按回车即可打印") + print("输入 quit 退出程序") + print("-" * 40) + + try: + while True: + texts = [] + quit_flag = False + + # 收集每列的输入内容 + for col in range(columns): + try: + if columns > 1: + prompt = f"\n请输入第{col + 1}列内容: " + else: + prompt = "\n请输入打印内容: " + text = input(prompt).strip() + except EOFError: + quit_flag = True + break + + if text.lower() == "quit": + quit_flag = True + break + + texts.append(text) + + if quit_flag: + break + + # 检查是否所有列都为空 + if all(t == "" for t in texts): + print("输入为空, 请重新输入") + continue + + try: + execute_print_job(lib, config, texts) + print(f"已打印: {' | '.join(texts)}") + except Exception as e: + logger.error("打印失败: %s", e) + print(f"打印出错: {e}, 请检查打印机连接") + + except KeyboardInterrupt: + print("\n检测到中断信号") + + finally: + logger.info("=== 脚本已退出 ===") + + +if __name__ == "__main__": + main() diff --git a/unilabos/devices/eit_synthesis_station/printer/test_print_session.py b/unilabos/devices/eit_synthesis_station/printer/test_print_session.py new file mode 100644 index 00000000..9aae07e6 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/test_print_session.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +""" +功能: + 验证TSC标签打印作业的会话生命周期. + 确保单次作业结束后立即关闭端口, 不会等到脚本退出时才提交打印任务. +""" + +import unittest +from unittest.mock import MagicMock, call, patch + +from eit_synthesis_station.printer.label_print_service import LabelPrintService +from eit_synthesis_station.printer.print_text import execute_print_job + + +SAMPLE_CONFIG = { + "printer": { + "port": "Test Printer", + "ppi": 300, + }, + "paper": { + "width": 56, + "height": 10, + "unit": "mm", + "columns": 2, + "column_gap": 3, + "margin": 1.8, + "gap": 0, + "gap_offset": 0, + "direction": 1, + }, + "font": { + "name": "Arial", + "size": 60, + "bold": 0, + "underline": 0, + "rotation": 0, + }, + "position": { + "x": 10, + "y": 30, + }, +} + + +class FakeTscLib: + """ + 功能: + 用于测试的TSC动态库桩对象. + 记录端口开关和打印调用次数, 避免依赖真实硬件. + """ + + def __init__(self): + self.openportW = MagicMock(return_value=1) + self.sendcommandW = MagicMock() + self.windowsfontUnicode = MagicMock(return_value=1) + self.printlabelW = MagicMock() + self.closeport = MagicMock() + + +class TestPrintJobSession(unittest.TestCase): + """ + 功能: + 验证底层打印作业会在单次调用内完成提交. + """ + + def test_execute_print_job_closes_port_after_print(self): + """ + 功能: + 验证单次打印完成后会立即关闭端口. + """ + lib = FakeTscLib() + + execute_print_job(lib, SAMPLE_CONFIG, ["123", "456"]) + + self.assertEqual(lib.openportW.call_count, 1) + self.assertEqual(lib.printlabelW.call_count, 1) + self.assertEqual(lib.closeport.call_count, 1) + + @patch("eit_synthesis_station.printer.print_text.print_text", side_effect=RuntimeError("boom")) + def test_execute_print_job_closes_port_when_print_failed(self, mock_print_text): + """ + 功能: + 验证打印异常时仍会关闭端口, 避免下次作业继续挂起. + """ + lib = FakeTscLib() + + with self.assertRaises(RuntimeError): + execute_print_job(lib, SAMPLE_CONFIG, ["123", "456"]) + + mock_print_text.assert_called_once_with(lib, SAMPLE_CONFIG, ["123", "456"]) + self.assertEqual(lib.openportW.call_count, 1) + self.assertEqual(lib.closeport.call_count, 1) + + +class TestLabelPrintService(unittest.TestCase): + """ + 功能: + 验证服务层按单次作业提交打印任务. + """ + + @patch("eit_synthesis_station.printer.label_print_service.load_config", return_value=SAMPLE_CONFIG) + @patch("eit_synthesis_station.printer.label_print_service.load_dll") + @patch("eit_synthesis_station.printer.label_print_service.check_printer_ready") + @patch("eit_synthesis_station.printer.label_print_service.execute_print_job") + def test_print_label_uses_independent_job_per_copy( + self, + mock_execute_print_job, + mock_check_printer_ready, + mock_load_dll, + mock_load_config, + ): + """ + 功能: + 验证服务层每份标签都会单独提交一次打印作业. + """ + fake_lib = object() + mock_load_dll.return_value = fake_lib + service = LabelPrintService("dummy.yaml") + + result = service.print_label(["123", "456"], copies=2) + + self.assertTrue(result) + mock_load_config.assert_called_once_with("dummy.yaml") + mock_check_printer_ready.assert_called_once_with(fake_lib, SAMPLE_CONFIG) + self.assertEqual(mock_execute_print_job.call_count, 2) + mock_execute_print_job.assert_has_calls( + [ + call(fake_lib, SAMPLE_CONFIG, ["123", "456"]), + call(fake_lib, SAMPLE_CONFIG, ["123", "456"]), + ] + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/unilabos/devices/eit_synthesis_station/printer/test_raw_tspl.py b/unilabos/devices/eit_synthesis_station/printer/test_raw_tspl.py new file mode 100644 index 00000000..c58a69c0 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/printer/test_raw_tspl.py @@ -0,0 +1,133 @@ +""" +功能: + 诊断脚本: 绕过TSCLIB.dll, 通过Windows打印API直接发送TSPL原始命令到打印机. + 用于验证佳博1134T是否支持TSPL指令集. + +用法: + python test_raw_tspl.py +""" + +import ctypes +import ctypes.wintypes as wt + +# ──────────────── Windows Spooler API 声明 ──────────────── +winspool = ctypes.WinDLL("winspool.drv") + +# OpenPrinterW +winspool.OpenPrinterW.argtypes = [ + ctypes.c_wchar_p, # pPrinterName + ctypes.POINTER(wt.HANDLE), # phPrinter + ctypes.c_void_p, # pDefault (NULL) +] +winspool.OpenPrinterW.restype = wt.BOOL + + +class DOC_INFO_1W(ctypes.Structure): + _fields_ = [ + ("pDocName", ctypes.c_wchar_p), + ("pOutputFile", ctypes.c_wchar_p), + ("pDatatype", ctypes.c_wchar_p), + ] + + +# StartDocPrinterW +winspool.StartDocPrinterW.argtypes = [wt.HANDLE, wt.DWORD, ctypes.POINTER(DOC_INFO_1W)] +winspool.StartDocPrinterW.restype = wt.DWORD + +# StartPagePrinter +winspool.StartPagePrinter.argtypes = [wt.HANDLE] +winspool.StartPagePrinter.restype = wt.BOOL + +# WritePrinter +winspool.WritePrinter.argtypes = [wt.HANDLE, ctypes.c_void_p, wt.DWORD, ctypes.POINTER(wt.DWORD)] +winspool.WritePrinter.restype = wt.BOOL + +# EndPagePrinter +winspool.EndPagePrinter.argtypes = [wt.HANDLE] +winspool.EndPagePrinter.restype = wt.BOOL + +# EndDocPrinter +winspool.EndDocPrinter.argtypes = [wt.HANDLE] +winspool.EndDocPrinter.restype = wt.BOOL + +# ClosePrinter +winspool.ClosePrinter.argtypes = [wt.HANDLE] +winspool.ClosePrinter.restype = wt.BOOL + + +def send_raw_tspl(printer_name, tspl_commands): + """ + 功能: + 通过Windows Spooler API以RAW模式向打印机发送TSPL原始命令. + + 参数: + printer_name: str, Windows中的打印机名称 + tspl_commands: str, TSPL指令(多行, 用\\n分隔) + + 返回: + bool, 发送成功返回True + """ + h_printer = wt.HANDLE() + + # 打开打印机 + if not winspool.OpenPrinterW(printer_name, ctypes.byref(h_printer), None): + print(f"[错误] 无法打开打印机: '{printer_name}'") + return False + print(f"[成功] 已打开打印机: '{printer_name}'") + + # 设置RAW数据模式 + doc_info = DOC_INFO_1W() + doc_info.pDocName = "TSPL Test" + doc_info.pOutputFile = None + doc_info.pDatatype = "RAW" + + job_id = winspool.StartDocPrinterW(h_printer, 1, ctypes.byref(doc_info)) + if job_id == 0: + print("[错误] StartDocPrinter失败") + winspool.ClosePrinter(h_printer) + return False + print(f"[成功] 创建打印任务, JobID={job_id}") + + winspool.StartPagePrinter(h_printer) + + # 发送TSPL命令(以字节形式) + data = tspl_commands.encode("ascii", errors="replace") + bytes_written = wt.DWORD(0) + ok = winspool.WritePrinter( + h_printer, + ctypes.c_char_p(data), + len(data), + ctypes.byref(bytes_written), + ) + print(f"[信息] WritePrinter: ok={ok}, 写入字节={bytes_written.value}/{len(data)}") + + winspool.EndPagePrinter(h_printer) + winspool.EndDocPrinter(h_printer) + winspool.ClosePrinter(h_printer) + + if ok and bytes_written.value == len(data): + print("[成功] TSPL命令已发送到打印机") + return True + else: + print("[错误] 写入数据不完整") + return False + + +if __name__ == "__main__": + PRINTER_NAME = "Gprinter GP-1134T" + + # 简单的TSPL测试命令: 设置纸张 -> 清除缓冲 -> 打印文字 -> 出纸 + tspl_test = ( + "SIZE 56 mm, 10 mm\n" + "GAP 2 mm, 0 mm\n" + "DIRECTION 1\n" + "CLS\n" + 'TEXT 10,10,"3",0,1,1,"TSPL TEST"\n' + "PRINT 1,1\n" + ) + + print(f"目标打印机: {PRINTER_NAME}") + print(f"发送TSPL命令:\n{tspl_test}") + print("-" * 40) + + send_raw_tspl(PRINTER_NAME, tspl_test) diff --git a/unilabos/devices/eit_synthesis_station/requirements.txt b/unilabos/devices/eit_synthesis_station/requirements.txt new file mode 100644 index 00000000..49228b75 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/requirements.txt @@ -0,0 +1,5 @@ +requests>=2.31.0 +pandas>=2.0.0 +openpyxl>=3.1.0 +beautifulsoup4>=4.12.0 +playwright>=1.42.0 diff --git a/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template.xlsx b/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template.xlsx new file mode 100644 index 00000000..1c5822e6 Binary files /dev/null and b/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template.xlsx differ diff --git a/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template_2.xlsx b/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template_2.xlsx new file mode 100644 index 00000000..53433f01 Binary files /dev/null and b/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template_2.xlsx differ diff --git "a/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template\342\200\224\342\200\2244.xlsx" "b/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template\342\200\224\342\200\2244.xlsx" new file mode 100644 index 00000000..1fea90eb Binary files /dev/null and "b/unilabos/devices/eit_synthesis_station/sheet/backup/reaction_template\342\200\224\342\200\2244.xlsx" differ diff --git a/unilabos/devices/eit_synthesis_station/sheet/backup/task_reaction.xlsx b/unilabos/devices/eit_synthesis_station/sheet/backup/task_reaction.xlsx new file mode 100644 index 00000000..e81cd8b5 Binary files /dev/null and b/unilabos/devices/eit_synthesis_station/sheet/backup/task_reaction.xlsx differ diff --git a/unilabos/devices/eit_synthesis_station/sheet/batch_in_tray.xlsx b/unilabos/devices/eit_synthesis_station/sheet/batch_in_tray.xlsx new file mode 100644 index 00000000..515eaa4e Binary files /dev/null and b/unilabos/devices/eit_synthesis_station/sheet/batch_in_tray.xlsx differ diff --git a/unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx b/unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx new file mode 100644 index 00000000..1a8c79f8 Binary files /dev/null and b/unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx differ diff --git a/unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx b/unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx new file mode 100644 index 00000000..cea5f54b Binary files /dev/null and b/unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx differ diff --git a/unilabos/devices/eit_synthesis_station/test.json b/unilabos/devices/eit_synthesis_station/test.json new file mode 100644 index 00000000..1c000b0c --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/test.json @@ -0,0 +1,102 @@ +{ + "nodes": [ + { + "id": "eit_synthesis_station", + "name": "eit_synthesis_station", + "children": null, + "parent": null, + "type": "device", + "class": "eit_synthesis_station", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "deck": { + "data": { + "_resource_child_name": "EIT_Synthesis_Station_Deck", + "_resource_type": "unilabos.resources.eit_synthesis_station.decks:EIT_Synthesis_Station_Deck" + } + }, + "protocol_type": [] + } + }, + { + "id": "eit_agv", + "name": "eit_agv", + "children": null, + "parent": null, + "type": "device", + "class": "eit_agv", + "position": { + "x": 0, + "y": 0, + "z": 0 + }, + "config": { + "deck": { + "data": { + "_resource_child_name": "EIT_AGV_Deck", + "_resource_type": "unilabos.resources.eit_agv.decks:EIT_AGV_Deck" + } + }, + "protocol_type": [], + "arm_ip": "", + "arm_port": 0, + "arm_timeout": 180000 + } + }, + { + "id": "EIT_Synthesis_Station_Deck", + "name": "EIT_Synthesis_Station_Deck", + "sample_id": null, + "children": [], + "parent": "eit_synthesis_station", + "type": "deck", + "class": "", + "position": { + "x": 2000, + "y": 2000, + "z": 0 + }, + "config": { + "type": "EIT_Synthesis_Station_Deck", + "setup": true, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + } + }, + "data": {} + }, + { + "id": "EIT_AGV_Deck", + "name": "EIT_AGV_Deck", + "sample_id": null, + "children": [], + "parent": "eit_agv", + "type": "deck", + "class": "", + "position": { + "x": 200, + "y": -600, + "z": 0 + }, + "config": { + "type": "EIT_AGV_Deck", + "setup": true, + "rotation": { + "x": 0, + "y": 0, + "z": 0, + "type": "Rotation" + } + }, + "data": {} + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/eit_synthesis_station/test_prepared_chemical_workflow.py b/unilabos/devices/eit_synthesis_station/test_prepared_chemical_workflow.py new file mode 100644 index 00000000..fb836cd4 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/test_prepared_chemical_workflow.py @@ -0,0 +1,528 @@ +# -*- coding: utf-8 -*- +""" +功能: +验证溶液与 beads 派生条目流程的关键回归场景. +参数: +无. +返回: +无. +""" + +import importlib +import io +import logging +import shutil +import sys +import types +import unittest +import uuid +from contextlib import redirect_stdout +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import patch + +import openpyxl + +from eit_synthesis_station.chem_tools.chemical_append_utils import ( + build_duplicate_check_specs, + build_prepared_chemical_row_data, +) +from eit_synthesis_station.controller.station_controller import SynthesisStationController +from eit_synthesis_station.manager.station_manager import SynthesisStationManager + + +CHEMICAL_HEADERS = [ + "cas_number", + "chemical_id", + "substance_english_name", + "substance", + "other_name", + "brand", + "package_size", + "storage_location", + "molecular_weight", + "density (g/mL)", + "physical_state", + "physical_form", + "active_content(mol/L or wt%)", +] + + +class PreparedChemicalWorkflowTestCase(unittest.TestCase): + """ + 功能: + 覆盖派生条目 builder, manager, CLI 与校验逻辑. + 参数: + 无. + 返回: + 无. + """ + + @staticmethod + def _get_workspace_temp_root() -> Path: + """ + 功能: + 返回位于工作区内的临时目录根路径. + 参数: + 无. + 返回: + Path, 可写入的临时目录根路径. + """ + temp_root = Path(__file__).resolve().parent / "_tmp" + temp_root.mkdir(parents=True, exist_ok=True) + return temp_root + + def _make_workspace_case_dir(self) -> Path: + """ + 功能: + 在工作区内创建唯一测试目录. + 参数: + 无. + 返回: + Path, 新建的测试目录路径. + """ + case_dir = self._get_workspace_temp_root() / f"case_{uuid.uuid4().hex}" + case_dir.mkdir(parents=True, exist_ok=False) + return case_dir + + @staticmethod + def _create_manager() -> SynthesisStationManager: + """ + 功能: + 创建不执行完整初始化的管理器实例, 仅用于单元测试. + 参数: + 无. + 返回: + SynthesisStationManager, 可直接调用目标方法的实例. + """ + manager = SynthesisStationManager.__new__(SynthesisStationManager) + manager._logger = logging.getLogger("TestPreparedChemicalWorkflow.Manager") + return manager + + @staticmethod + def _create_chemical_workbook(file_path: Path, rows: list[list[object]]) -> None: + """ + 功能: + 创建最小可用的 chemical_list.xlsx 测试文件. + 参数: + file_path: Path, 目标文件路径. + rows: list[list[object]], 数据行列表. + 返回: + 无. + """ + workbook = openpyxl.Workbook() + worksheet = workbook.active + worksheet.append(CHEMICAL_HEADERS) + for row in rows: + worksheet.append(row) + workbook.save(file_path) + workbook.close() + + @staticmethod + def _load_main_with_stubs(): + """ + 功能: + 以最小依赖加载 main 模块, 避免真实设备依赖影响测试. + 参数: + 无. + 返回: + module, 导入后的 eit_synthesis_station.main 模块. + """ + fake_station_manager = types.ModuleType("eit_synthesis_station.manager.station_manager") + fake_station_manager.SynthesisStationManager = object + + fake_setting = types.ModuleType("eit_synthesis_station.config.setting") + fake_setting.Settings = object + + fake_constants = types.ModuleType("eit_synthesis_station.config.constants") + fake_constants.TaskStatus = SimpleNamespace( + UNSTARTED=0, + RUNNING=1, + COMPLETED=2, + PAUSED=3, + FAILED=4, + STOPPED=5, + PAUSING=6, + STOPPING=7, + WAITING=8, + HOLDING=9, + ) + fake_constants.StationState = SimpleNamespace( + IDLE=0, + RUNNING=1, + PAUSED=2, + PAUSING=3, + STOPPING=4, + HOLDING=5, + ) + + with patch.dict( + sys.modules, + { + "eit_synthesis_station.manager.station_manager": fake_station_manager, + "eit_synthesis_station.config.setting": fake_setting, + "eit_synthesis_station.config.constants": fake_constants, + }, + ): + sys.modules.pop("eit_synthesis_station.main", None) + return importlib.import_module("eit_synthesis_station.main") + + def test_build_prepared_chemical_row_data_for_solution_keeps_parent_metadata(self) -> None: + """ + 功能: + 校验 solution 派生行会继承母体核心字段并生成规范名称. + 参数: + 无. + 返回: + 无. + """ + row_data = build_prepared_chemical_row_data( + base_row_data={ + "cas_number": "64-17-5", + "substance_english_name": "ethanol", + "substance": "乙醇", + "substance_chinese_name": "乙醇", + "molecular_weight": 46.07, + "density (g/mL)": 0.7893, + }, + prepared_form="solution", + active_content=0.4, + solvent_name="CH2Cl2", + ) + + self.assertEqual(row_data["cas_number"], "64-17-5") + self.assertEqual(row_data["substance"], "乙醇 (溶液, 0.4 M in CH2Cl2)") + self.assertEqual(row_data["physical_form"], "solution") + self.assertEqual(row_data["density (g/mL)"], 0.7893) + + def test_build_prepared_chemical_row_data_for_beads_clears_density(self) -> None: + """ + 功能: + 校验 beads 派生行会清空密度并生成 wt% 名称. + 参数: + 无. + 返回: + 无. + """ + row_data = build_prepared_chemical_row_data( + base_row_data={ + "cas_number": "7681-65-4", + "substance_english_name": "Cuprous iodide", + "substance": "碘化亚铜", + "substance_chinese_name": "碘化亚铜", + "molecular_weight": 190.45, + "density (g/mL)": 5.62, + }, + prepared_form="beads", + active_content=1.49, + ) + + self.assertEqual(row_data["substance"], "碘化亚铜 (beads, 1.49%)") + self.assertEqual(row_data["physical_form"], "beads") + self.assertEqual(row_data["density (g/mL)"], "") + + def test_build_duplicate_check_specs_for_prepared_row_only_checks_display_name(self) -> None: + """ + 功能: + 校验派生条目仅按展示名查重, 不再按 CAS 与英文名拦截. + 参数: + 无. + 返回: + 无. + """ + duplicate_specs = build_duplicate_check_specs( + { + "cas_number": "64-17-5", + "substance_english_name": "ethanol", + "substance": "乙醇 (溶液, 1 M in MeCN)", + "physical_form": "solution", + } + ) + self.assertEqual( + duplicate_specs, + [ + (("substance", "substance_chinese_name"), "乙醇 (溶液, 1 M in MeCN)", "中文名"), + ], + ) + + def test_prepare_solution_or_beads_reuses_existing_neat_parent(self) -> None: + """ + 功能: + 验证母体 neat 已存在时, 可直接追加同 CAS 的 solution 条目. + 参数: + 无. + 返回: + 无. + """ + case_dir = self._make_workspace_case_dir() + try: + workbook_path = case_dir / "chemical_list.xlsx" + self._create_chemical_workbook( + workbook_path, + [ + [ + "64-17-5", + "", + "ethanol", + "乙醇", + "", + "", + "", + "", + 46.07, + 0.7893, + "liquid", + "neat", + "", + ], + ], + ) + manager = self._create_manager() + + result = manager.prepare_solution_or_beads( + "64-17-5", + "solution", + solvent_name="CH2Cl2", + active_content=0.4, + target_volume_ml=10, + excel_path=str(workbook_path), + ) + + self.assertIsNotNone(result) + self.assertEqual(result["base_created"], False) + self.assertEqual(result["derived_row_index"], 3) + self.assertAlmostEqual(result["recipe"]["solute_mass_g"], 0.18428, places=8) + finally: + shutil.rmtree(case_dir, ignore_errors=True) + + def test_prepare_solution_or_beads_appends_missing_parent_for_cas(self) -> None: + """ + 功能: + 验证 CAS 路径在母体缺失时, 会先补母体 neat 再追加 beads 条目. + 参数: + 无. + 返回: + 无. + """ + case_dir = self._make_workspace_case_dir() + try: + workbook_path = case_dir / "chemical_list.xlsx" + self._create_chemical_workbook(workbook_path, []) + manager = self._create_manager() + + def _fake_lookup_and_append_chemical(query, excel_path=None): + row_data = { + "cas_number": "7681-65-4", + "chemical_id": "", + "substance_english_name": "Cuprous iodide", + "substance": "碘化亚铜", + "substance_chinese_name": "碘化亚铜", + "other_name": "", + "brand": "", + "package_size": "", + "storage_location": "", + "molecular_weight": 190.45, + "density (g/mL)": "", + "physical_state": "solid", + "physical_form": "neat", + "active_content(mol/L or wt%)": "", + } + row_index = manager._append_chemical_row_to_excel(row_data=row_data, excel_path=excel_path) + return { + "row_data": row_data, + "row_index": row_index, + "chemicalbook_status": "", + "chemicalbook_record_path": "", + } + + manager.lookup_and_append_chemical = _fake_lookup_and_append_chemical + + result = manager.prepare_solution_or_beads( + "7681-65-4", + "beads", + active_content=1.49, + target_active_mmol=0.5, + excel_path=str(workbook_path), + ) + + self.assertIsNotNone(result) + self.assertEqual(result["base_created"], True) + self.assertEqual(result["base_row_index"], 2) + self.assertEqual(result["derived_row_index"], 3) + finally: + shutil.rmtree(case_dir, ignore_errors=True) + + def test_prepare_solution_or_beads_appends_missing_parent_for_smiles(self) -> None: + """ + 功能: + 验证 SMILES 路径在母体缺失时, 会先补母体 neat 再追加 solution 条目. + 参数: + 无. + 返回: + 无. + """ + case_dir = self._make_workspace_case_dir() + try: + workbook_path = case_dir / "chemical_list.xlsx" + self._create_chemical_workbook(workbook_path, []) + manager = self._create_manager() + + def _fake_lookup_and_append_by_smiles(smiles, excel_path=None): + row_data = { + "cas_number": "64-17-5", + "chemical_id": "", + "substance_english_name": "ethanol", + "substance": "乙醇", + "substance_chinese_name": "乙醇", + "other_name": "", + "brand": "", + "package_size": "", + "storage_location": "", + "molecular_weight": 46.07, + "density (g/mL)": 0.7893, + "physical_state": "liquid", + "physical_form": "neat", + "active_content(mol/L or wt%)": "", + } + row_index = manager._append_chemical_row_to_excel(row_data=row_data, excel_path=excel_path) + return { + "row_data": row_data, + "row_index": row_index, + "chemicalbook_status": "", + "chemicalbook_record_path": "", + } + + manager.lookup_and_append_chemical_by_smiles = _fake_lookup_and_append_by_smiles + + with patch( + "eit_synthesis_station.chem_tools.chemical_lookup.lookup_chemical_by_smiles", + return_value=SimpleNamespace( + cas_number="64-17-5", + substance_english_name="ethanol", + substance="乙醇", + ), + ): + result = manager.prepare_solution_or_beads( + "CCO", + "solution", + solvent_name="MeCN", + active_content=1.0, + target_volume_ml=5, + excel_path=str(workbook_path), + ) + + self.assertIsNotNone(result) + self.assertEqual(result["base_created"], True) + self.assertEqual(result["base_row_index"], 2) + self.assertEqual(result["derived_row_index"], 3) + finally: + shutil.rmtree(case_dir, ignore_errors=True) + + def test_check_chemical_library_data_allows_prepared_rows_share_english_name(self) -> None: + """ + 功能: + 验证 solution 与 beads 可与 neat 共享英文名而不触发重复错误. + 参数: + 无. + 返回: + 无. + """ + controller = SynthesisStationController.__new__(SynthesisStationController) + controller._logger = logging.getLogger("TestPreparedChemicalWorkflow.Controller") + + headers = [ + "cas_number", + "chemical_id", + "substance_english_name", + "substance", + "molecular_weight", + "density (g/mL)", + "physical_state", + "physical_form", + "active_content(mol/L or wt%)", + ] + rows = [ + { + "cas_number": "64-17-5", + "chemical_id": "", + "substance_english_name": "ethanol", + "substance": "乙醇", + "molecular_weight": 46.07, + "density (g/mL)": 0.7893, + "physical_state": "liquid", + "physical_form": "neat", + "active_content(mol/L or wt%)": "", + }, + { + "cas_number": "64-17-5", + "chemical_id": "", + "substance_english_name": "ethanol", + "substance": "乙醇 (溶液, 1 M in MeCN)", + "molecular_weight": 46.07, + "density (g/mL)": 0.7893, + "physical_state": "liquid", + "physical_form": "solution", + "active_content(mol/L or wt%)": 1, + }, + { + "cas_number": "64-17-5", + "chemical_id": "", + "substance_english_name": "ethanol", + "substance": "乙醇 (beads, 1.5%)", + "molecular_weight": 46.07, + "density (g/mL)": "", + "physical_state": "solid", + "physical_form": "beads", + "active_content(mol/L or wt%)": 1.5, + }, + ] + + result = controller.check_chemical_library_data(rows, headers) + + self.assertEqual(result["errors"], []) + self.assertEqual(result["warnings"], []) + + def test_menu_chemical_library_prints_prepared_summary(self) -> None: + """ + 功能: + 验证新增派生条目菜单会输出母体来源, 派生行号与配制结果摘要. + 参数: + 无. + 返回: + 无. + """ + main = self._load_main_with_stubs() + manager = SimpleNamespace(prepare_solution_or_beads=object()) + result = { + "base_row_data": { + "base_substance": "乙醇", + }, + "base_row_index": 2105, + "base_created": False, + "derived_row_data": { + "substance": "乙醇 (溶液, 0.4 M in MeCN)", + }, + "derived_row_index": 2106, + "recipe": { + "instruction_text": "称取/加入溶质 0.18428 g, 用 MeCN 溶解后定容至 10 mL", + "solute_volume_ml": 0.233469, + }, + } + input_values = iter(["7", "64-17-5", "solution", "MeCN", "0.4", "10", "0"]) + + with patch.object(main, "_print_menu"): + with patch.object(main, "_pause"): + with patch.object(main, "_safe_run", return_value=result): + with patch("builtins.input", side_effect=lambda _prompt="": next(input_values)): + captured_stdout = io.StringIO() + with redirect_stdout(captured_stdout): + main._menu_chemical_library(manager) + + output_text = captured_stdout.getvalue() + self.assertIn("母体条目: 名称=乙醇, 来源=复用现有母体", output_text) + self.assertIn("已成功添加派生条目: 名称=乙醇 (溶液, 0.4 M in MeCN), 行号=2106", output_text) + self.assertIn("配制结果: 称取/加入溶质 0.18428 g, 用 MeCN 溶解后定容至 10 mL", output_text) + + +if __name__ == "__main__": + unittest.main() diff --git a/unilabos/devices/eit_synthesis_station/utils/__init__.py b/unilabos/devices/eit_synthesis_station/utils/__init__.py new file mode 100644 index 00000000..40a96afc --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/utils/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/unilabos/devices/eit_synthesis_station/utils/file_utils.py b/unilabos/devices/eit_synthesis_station/utils/file_utils.py new file mode 100644 index 00000000..33f21aff --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/utils/file_utils.py @@ -0,0 +1,514 @@ +# -*- coding: utf-8 -*- +""" +功能: + 提供 Excel 文件安全写入与保存工具. + 当目标文件被 Excel 占用时, 优先通过 COM 精准定位目标工作簿, + 先保存再关闭目标工作簿, 然后重试写入. + 本模块不会关闭其他工作簿, 也不会终止 Excel 进程. +参数: + 无. +返回: + 无. +""" + +import ctypes +import logging +import os +import time +from pathlib import Path +from typing import Any, Iterator, List, Optional, Tuple, Union + +import psutil + +logger = logging.getLogger("FileUtils") + +_EXCEL_PROC_NAMES = {"excel.exe", "microsoft excel"} +_SCAN_TIMEOUT_SECONDS = 10.0 +_RELEASE_WAIT_SECONDS = 0.5 + + +def _normalize_windows_path(path: Union[str, Path]) -> str: + """ + 功能: + 将路径转换为适合 Windows 比较的规范字符串. + 参数: + path: str | Path, 待规范化的路径. + 返回: + str, 统一大小写与分隔符后的绝对路径字符串. + """ + if path is None: + return "" + + path_str = str(path).strip() + if path_str == "": + return "" + + return os.path.normcase(os.path.normpath(str(Path(path_str).resolve()))) + + +def _load_com_modules() -> Tuple[Any, Any]: + """ + 功能: + 延迟加载 pywin32 相关模块, 避免无 COM 环境下导入失败. + 参数: + 无. + 返回: + Tuple[Any, Any], (pythoncom, win32com.client). + """ + import pythoncom + import win32com.client + + return pythoncom, win32com.client + + +def _iter_excel_workbooks_from_rot() -> Iterator[Any]: + """ + 功能: + 从 Running Object Table 中枚举已注册的 Excel 工作簿对象. + 参数: + 无. + 返回: + Iterator[Any], 逐个返回可访问 FullName 的 Excel 工作簿对象. + """ + pythoncom, win32_client = _load_com_modules() + bind_context = pythoncom.CreateBindCtx(0) + running_table = pythoncom.GetRunningObjectTable() + enum_moniker = running_table.EnumRunning() + + while True: + monikers = enum_moniker.Next(1) + if not monikers: + break + + moniker = monikers[0] + try: + display_name = str(moniker.GetDisplayName(bind_context, None)).lower() + except Exception: + display_name = "" + + # 先用显示名过滤, 避免访问无关 COM 对象. + if display_name and ".xls" not in display_name and "excel" not in display_name: + continue + + try: + workbook = win32_client.Dispatch(running_table.GetObject(moniker)) + full_name = getattr(workbook, "FullName", "") + except Exception: + continue + + if isinstance(full_name, str) and full_name: + yield workbook + + +def _iter_excel_workbooks_from_active_instance() -> Iterator[Any]: + """ + 功能: + 兼容只暴露活动实例的场景, 枚举当前活动 Excel 实例中的工作簿. + 参数: + 无. + 返回: + Iterator[Any], 当前活动实例中的工作簿对象序列. + """ + _, win32_client = _load_com_modules() + excel = win32_client.GetActiveObject("Excel.Application") + + for workbook in excel.Workbooks: + yield workbook + + +class _GUID(ctypes.Structure): + """COM GUID 结构体, 用于 ctypes 调用 AccessibleObjectFromWindow.""" + + _fields_ = [ + ("Data1", ctypes.c_ulong), + ("Data2", ctypes.c_ushort), + ("Data3", ctypes.c_ushort), + ("Data4", ctypes.c_ubyte * 8), + ] + + +# IDispatch GUID: {00020400-0000-0000-C000-000000000046} +_IID_IDISPATCH = _GUID( + 0x00020400, + 0x0000, + 0x0000, + (ctypes.c_ubyte * 8)(0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46), +) + + +def _iter_excel_workbooks_from_all_instances() -> Iterator[Any]: + """ + 功能: + 通过窗口句柄枚举所有 Excel 实例中的工作簿. + 使用 EnumWindows + AccessibleObjectFromWindow 技术, + 能覆盖多实例场景, 不依赖 ROT 或 GetActiveObject. + 参数: + 无. + 返回: + Iterator[Any], 所有 Excel 实例中的工作簿对象序列. + """ + import win32gui # noqa: 延迟导入, 避免非 Windows 环境报错 + + pythoncom, win32_client = _load_com_modules() + + excel_hwnds: list = [] + + # 收集所有 XLMAIN 窗口句柄. + def _collect_excel_windows(hwnd, _): + try: + if win32gui.GetClassName(hwnd) == "XLMAIN": + excel_hwnds.append(hwnd) + except Exception: + pass + + win32gui.EnumWindows(_collect_excel_windows, None) + + seen_app_hwnds: set = set() + + for main_hwnd in excel_hwnds: + # 查找 XLMAIN -> XLDESK -> EXCEL7 子窗口. + try: + desk_hwnd = win32gui.FindWindowEx(main_hwnd, 0, "XLDESK", None) + if desk_hwnd == 0: + continue + excel7_hwnd = win32gui.FindWindowEx(desk_hwnd, 0, "EXCEL7", None) + if excel7_hwnd == 0: + continue + except Exception: + continue + + # 通过 AccessibleObjectFromWindow 获取 COM 对象. + try: + ptr = ctypes.c_void_p() + hr = ctypes.windll.oleacc.AccessibleObjectFromWindow( + excel7_hwnd, + ctypes.c_long(-16), # OBJID_NATIVEOM = 0xFFFFFFF0 + ctypes.byref(_IID_IDISPATCH), + ctypes.byref(ptr), + ) + if hr != 0 or not ptr.value: + continue + + # 将原始 IDispatch 指针包装为 pywin32 COM 对象. + window_obj = win32_client.Dispatch( + pythoncom.ObjectFromAddress(ptr.value) + ) + excel_app = window_obj.Application + except Exception: + continue + + # 用 Application.Hwnd 去重, 避免同一实例的多个窗口重复枚举. + try: + app_hwnd = excel_app.Hwnd + except Exception: + app_hwnd = main_hwnd + + if app_hwnd in seen_app_hwnds: + continue + seen_app_hwnds.add(app_hwnd) + + try: + for workbook in excel_app.Workbooks: + yield workbook + except Exception: + continue + + +def _close_target_workbook_from_iterable(workbooks: Iterator[Any], target_path: str) -> bool: + """ + 功能: + 在工作簿序列中查找目标文件, 命中后先保存再关闭. + 参数: + workbooks : Iterator[Any], Excel 工作簿对象序列. + target_path: str, 规范化后的目标绝对路径. + 返回: + bool, True 表示已保存并关闭目标工作簿, False 表示未命中. + """ + seen_paths = set() + + for workbook in workbooks: + try: + workbook_path = _normalize_windows_path(getattr(workbook, "FullName", "")) + except Exception: + continue + + if workbook_path in seen_paths: + continue + seen_paths.add(workbook_path) + + logger.debug("COM 枚举到工作簿: %s | 目标: %s", workbook_path, target_path) + + if workbook_path != target_path: + continue + + # 先保存 Excel 侧改动, 再关闭目标工作簿. + workbook.Save() + workbook.Close(SaveChanges=False) + return True + + return False + + +def _cleanup_lock_file(lock_file: Path) -> None: + """ + 功能: + 清理 Excel 残留的锁文件. + 参数: + lock_file: Path, 锁文件路径. + 返回: + 无. + """ + if not lock_file.exists(): + return + + try: + lock_file.unlink() + logger.info("已删除残留 Excel 锁文件: %s", lock_file.name) + except OSError as exc: + try: + os.chmod(lock_file, 0o666) # 先放宽权限, 再重试删除残留锁文件. + lock_file.unlink() + logger.info("已删除残留 Excel 锁文件: %s", lock_file.name) + except OSError as retry_exc: + logger.warning("删除 Excel 锁文件失败 | 文件: %s | 错误: %s", lock_file.name, retry_exc) + + +def _try_close_excel_workbook(path: Path) -> bool: + """ + 功能: + 通过 Windows COM 精准定位目标 Excel 工作簿. + 命中后先保存, 再关闭目标工作簿, 不影响其他已打开文件. + 参数: + path: Path, 目标工作簿路径. + 返回: + bool, True 表示已通过 COM 保存并关闭目标工作簿, False 表示未成功关闭. + """ + path = Path(path).resolve() + target_path = _normalize_windows_path(path) + pythoncom_module: Optional[Any] = None + com_initialized = False + + try: + pythoncom_module, _ = _load_com_modules() + pythoncom_module.CoInitialize() + com_initialized = True + + if _close_target_workbook_from_iterable(_iter_excel_workbooks_from_rot(), target_path): + logger.info("已通过 COM 保存并关闭 Excel 工作簿: %s", path.name) + return True + + if _close_target_workbook_from_iterable(_iter_excel_workbooks_from_active_instance(), target_path): + logger.info("已通过活动 Excel 实例保存并关闭工作簿: %s", path.name) + return True + + if _close_target_workbook_from_iterable(_iter_excel_workbooks_from_all_instances(), target_path): + logger.info("已通过窗口句柄枚举保存并关闭工作簿: %s", path.name) + return True + + logger.warning("未在运行中的 Excel 中找到目标工作簿, 无法自动关闭 | 文件: %s", path.name) + return False + except ImportError as exc: + logger.warning("缺少 pywin32, 无法通过 COM 自动关闭 Excel 工作簿 | 文件: %s | 错误: %s", path.name, exc) + return False + except Exception as exc: + logger.warning("通过 COM 保存并关闭 Excel 工作簿失败 | 文件: %s | 错误: %s", path.name, exc) + return False + finally: + if com_initialized and pythoncom_module is not None: + try: + pythoncom_module.CoUninitialize() + except Exception: + logger.debug("COM 反初始化失败, 已忽略 | 文件: %s", path.name) + + +def _iter_excel_processes() -> Iterator[psutil.Process]: + """ + 功能: + 迭代当前系统中的 Excel 进程. + 参数: + 无. + 返回: + Iterator[psutil.Process], Excel 进程对象序列. + """ + for proc in psutil.process_iter(["pid", "name"]): + try: + proc_name = (proc.info.get("name") or "").lower() + if proc_name in _EXCEL_PROC_NAMES: + yield proc + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + + +def _find_excel_lock_holders(path: Path, lock_file: Path) -> List[int]: + """ + 功能: + 扫描哪些 Excel 进程仍持有目标文件或其锁文件句柄. + 参数: + path : Path, 目标文件路径. + lock_file: Path, Excel 锁文件路径. + 返回: + List[int], 持有目标句柄的 Excel 进程 PID 列表. + """ + holder_pids: List[int] = [] + scan_start = time.monotonic() + + for proc in _iter_excel_processes(): + if time.monotonic() - scan_start > _SCAN_TIMEOUT_SECONDS: + logger.warning("Excel 文件句柄扫描超时, 已停止继续扫描") + break + + try: + open_files = proc.open_files() or [] + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + + held_paths = set() + for open_file in open_files: + try: + held_paths.add(Path(open_file.path).resolve()) + except OSError: + continue + + if path in held_paths or lock_file in held_paths: + holder_pids.append(proc.info["pid"]) + + return holder_pids + + +def release_file_lock(path: Union[str, Path]) -> bool: + """ + 功能: + 尝试以非破坏方式释放 Excel 对目标文件的占用. + 优先通过 COM 对目标工作簿执行保存并关闭. + 若目标文件已无 Excel 占用, 则仅清理残留锁文件. + 参数: + path: str | Path, 目标 Excel 文件路径. + 返回: + bool, True 表示已释放占用或清理残留锁文件, False 表示目标文件仍被 Excel 占用. + """ + path = Path(path).resolve() + lock_file = path.parent / f"~${path.name}" + + if _try_close_excel_workbook(path): + time.sleep(_RELEASE_WAIT_SECONDS) # 等待 Excel 释放文件句柄. + _cleanup_lock_file(lock_file) + return True + + holder_pids = _find_excel_lock_holders(path, lock_file) + if holder_pids: + logger.warning( + "Excel 仍占用目标文件, 未执行任何强制关闭动作 | 文件: %s | PID=%s", + path.name, + holder_pids, + ) + return False + + if lock_file.exists(): + logger.info("未检测到 Excel 占用, 锁文件视为残留文件并清理 | 文件: %s", lock_file.name) + _cleanup_lock_file(lock_file) + return True + + return False + + +def _build_permission_error_message(action_text: str, path: Path, retries: int) -> str: + """ + 功能: + 生成统一的 Excel 写权限失败提示. + 参数: + action_text: str, 当前动作描述, 如写入或保存. + path : Path, 目标文件路径. + retries : int, 已重试次数. + 返回: + str, 中文错误提示. + """ + return ( + f"{action_text} Excel 文件失败, 已重试 {retries} 次仍无法获得写权限: {path}\n" + "已尝试通过 COM 保存并关闭目标工作簿.\n" + "已取消强制关闭 Excel 进程.\n" + "如果当前环境未安装 pywin32, 系统将无法自动关闭 Excel 工作簿.\n" + "请先在 Excel 中保存并关闭目标文件后重试." + ) + + +def safe_excel_write(df, path: Union[str, Path], retries: int = 3, delay: float = 2.0, **kwargs) -> None: + """ + 功能: + 将 DataFrame 安全写入 Excel 文件. + 遇到 PermissionError 时, 尝试先保存并关闭目标工作簿, 再重试写入. + 参数: + df : Any, 具有 to_excel 方法的对象. + path : str | Path, 目标 Excel 文件路径. + retries : int, 最大重试次数. + delay : float, 每次重试前等待秒数. + **kwargs: 透传给 to_excel 的额外参数. + 返回: + 无. + """ + path = Path(path) + + for attempt in range(1, retries + 1): + try: + df.to_excel(path, **kwargs) + if attempt > 1: + logger.info("Excel 写入成功 | 文件: %s | 第 %d 次尝试", path.name, attempt) + return + except PermissionError as exc: + logger.warning( + "写入 Excel 权限不足, 准备尝试保存并关闭目标工作簿 | 文件: %s | 第 %d/%d 次 | 错误: %s", + path.name, + attempt, + retries, + exc, + ) + + released = release_file_lock(path) + if released: + logger.info("目标工作簿已释放, 等待文件句柄回收 | 文件: %s", path.name) + else: + logger.warning("未能自动释放目标工作簿, 将继续按重试策略处理 | 文件: %s", path.name) + + time.sleep(delay) + if attempt == retries: + raise PermissionError(_build_permission_error_message("写入", path, retries)) from exc + + +def safe_workbook_save(wb, path: Union[str, Path], retries: int = 3, delay: float = 2.0) -> None: + """ + 功能: + 将 openpyxl Workbook 安全保存到文件. + 遇到 PermissionError 时, 尝试先保存并关闭目标工作簿, 再重试保存. + 参数: + wb : Any, 具有 save 方法的 Workbook 对象. + path : str | Path, 目标文件路径. + retries : int, 最大重试次数. + delay : float, 每次重试前等待秒数. + 返回: + 无. + """ + path = Path(path) + + for attempt in range(1, retries + 1): + try: + wb.save(path) + if attempt > 1: + logger.info("Excel 保存成功 | 文件: %s | 第 %d 次尝试", path.name, attempt) + return + except PermissionError as exc: + logger.warning( + "保存 Excel 权限不足, 准备尝试保存并关闭目标工作簿 | 文件: %s | 第 %d/%d 次 | 错误: %s", + path.name, + attempt, + retries, + exc, + ) + + released = release_file_lock(path) + if released: + logger.info("目标工作簿已释放, 等待文件句柄回收 | 文件: %s", path.name) + else: + logger.warning("未能自动释放目标工作簿, 将继续按重试策略处理 | 文件: %s", path.name) + + time.sleep(delay) + if attempt == retries: + raise PermissionError(_build_permission_error_message("保存", path, retries)) from exc diff --git a/unilabos/devices/eit_synthesis_station/workstation_base.py b/unilabos/devices/eit_synthesis_station/workstation_base.py new file mode 100644 index 00000000..75fd7ea8 --- /dev/null +++ b/unilabos/devices/eit_synthesis_station/workstation_base.py @@ -0,0 +1,353 @@ +""" +工作站基类 +Workstation Base Class - 简化版 + +基于PLR Deck的简化工作站架构 +专注于核心物料系统和工作流管理 +""" + +import collections +import time +from typing import Dict, Any, List, Optional, Union +from abc import ABC, abstractmethod +from dataclasses import dataclass +from enum import Enum +from pylabrobot.resources import Deck, Plate, Resource as PLRResource + +from pylabrobot.resources.coordinate import Coordinate +from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode + +from unilabos.utils.log import logger + + +class WorkflowStatus(Enum): + """工作流状态""" + + IDLE = "idle" + INITIALIZING = "initializing" + RUNNING = "running" + PAUSED = "paused" + STOPPING = "stopping" + STOPPED = "stopped" + ERROR = "error" + COMPLETED = "completed" + + +@dataclass +class WorkflowInfo: + """工作流信息""" + + name: str + description: str + estimated_duration: float # 预估持续时间(秒) + required_materials: List[str] # 所需物料类型 + output_product: str # 输出产品类型 + parameters_schema: Dict[str, Any] # 参数架构 + + +class WorkStationContainer(Plate): + """ + WorkStation 专用 Container 类,继承自 Plate和TipRack + 注意这个物料必须通过plr_additional_res_reg.py注册到edge,才能正常序列化 + """ + + def __init__( + self, + name: str, + size_x: float, + size_y: float, + size_z: float, + category: str, + ordering: collections.OrderedDict, + model: Optional[str] = None, + ): + """ + 这里的初始化入参要和plr的保持一致 + """ + super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model) + self._unilabos_state = {} # 必须有此行,自己的类描述的是物料的 + + def load_state(self, state: Dict[str, Any]) -> None: + """从给定的状态加载工作台信息。""" + super().load_state(state) + self._unilabos_state = state + + def serialize_state(self) -> Dict[str, Dict[str, Any]]: + data = super().serialize_state() + data.update( + self._unilabos_state + ) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) + return data + + +def get_workstation_plate_resource(name: str) -> PLRResource: # 要给定一个返回plr的方法 + """ + 用于获取一些模板,例如返回一个带有特定信息/子物料的 Plate,这里需要到注册表注册,例如unilabos/registry/resources/organic/workstation.yaml + 可以直接运行该函数或者利用注册表补全机制,来检查是否资源出错 + :param name: 资源名称 + :return: Resource对象 + """ + plate = WorkStationContainer( + name, size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict() + ) + tip_rack = WorkStationContainer( + "tip_rack_inside_plate", + size_x=50, + size_y=50, + size_z=10, + category="tip_rack", + ordering=collections.OrderedDict(), + ) + plate.assign_child_resource(tip_rack, Coordinate.zero()) + return plate + + +class ResourceSynchronizer(ABC): + """资源同步器基类 + + 负责与外部物料系统的同步,并对 self.deck 做修改 + """ + + def __init__(self, workstation: "WorkstationBase"): + self.workstation = workstation + + @abstractmethod + def sync_from_external(self) -> bool: + """从外部系统同步物料到本地deck""" + pass + + @abstractmethod + def sync_to_external(self, plr_resource: PLRResource) -> bool: + """将本地物料同步到外部系统""" + pass + + @abstractmethod + def handle_external_change(self, change_info: Dict[str, Any]) -> bool: + """处理外部系统的变更通知""" + pass + + +class WorkstationBase(ABC): + """工作站基类 - 简化版 + + 核心功能: + 1. 基于 PLR Deck 的物料系统,支持格式转换 + 2. 可选的资源同步器支持外部物料系统 + 3. 简化的工作流管理 + """ + + _ros_node: ROS2WorkstationNode + + @property + def _children(self) -> Dict[str, Any]: # 不要删除这个下划线,不然会自动导入注册表,后面改成装饰器识别 + return self._ros_node.children + + async def update_resource_example(self): + return await self._ros_node.update_resource([get_workstation_plate_resource("test")]) + + def __init__( + self, + deck: Optional[Deck], + *args, + **kwargs, # 必须有kwargs + ): + # PLR 物料系统 + self.deck: Optional[Deck] = deck + self.plr_resources: Dict[str, PLRResource] = {} + + self.resource_synchronizer = None # type: Optional[ResourceSynchronizer] + # 硬件接口 + self.hardware_interface: Union[Any, str] = None + + # 工作流状态 + self.current_workflow_status = WorkflowStatus.IDLE + self.current_workflow_info = None + self.workflow_start_time = None + self.workflow_parameters = {} + + # 支持的工作流(静态预定义) + self.supported_workflows: Dict[str, WorkflowInfo] = {} + + def post_init(self, ros_node: ROS2WorkstationNode) -> None: + # 初始化物料系统 + self._ros_node = ros_node + + def _build_resource_mappings(self, deck: Deck): + """递归构建资源映射""" + + def add_resource_recursive(resource: PLRResource): + if hasattr(resource, "name"): + self.plr_resources[resource.name] = resource + + if hasattr(resource, "children"): + for child in resource.children: + add_resource_recursive(child) + + add_resource_recursive(deck) + + # ============ 硬件接口管理 ============ + + def set_hardware_interface(self, hardware_interface: Union[Any, str]): + """设置硬件接口""" + self.hardware_interface = hardware_interface + logger.info(f"工作站 {self._ros_node.device_id} 硬件接口设置: {type(hardware_interface).__name__}") + + def set_workstation_node(self, workstation_node: "ROS2WorkstationNode"): + """设置协议节点引用(用于代理模式)""" + self._ros_node = workstation_node + logger.info(f"工作站 {self._ros_node.device_id} 关联协议节点") + + # ============ 设备操作接口 ============ + + def call_device_method(self, method: str, *args, **kwargs) -> Any: + """调用设备方法的统一接口""" + # 1. 代理模式:通过协议节点转发 + if isinstance(self.hardware_interface, str) and self.hardware_interface.startswith("proxy:"): + if not self._ros_node: + raise RuntimeError("代理模式需要设置workstation_node") + + device_id = self.hardware_interface[6:] # 移除 "proxy:" 前缀 + return self._ros_node.call_device_method(device_id, method, *args, **kwargs) + + # 2. 直接模式:直接调用硬件接口方法 + elif self.hardware_interface and hasattr(self.hardware_interface, method): + return getattr(self.hardware_interface, method)(*args, **kwargs) + + else: + raise AttributeError(f"硬件接口不支持方法: {method}") + + def get_device_status(self) -> Dict[str, Any]: + """获取设备状态""" + try: + return self.call_device_method("get_status") + except AttributeError: + # 如果设备不支持get_status方法,返回基础状态 + return { + "status": "unknown", + "interface_type": type(self.hardware_interface).__name__, + "timestamp": time.time(), + } + + def is_device_available(self) -> bool: + """检查设备是否可用""" + try: + self.get_device_status() + return True + except: + return False + + # ============ 物料系统接口 ============ + + def get_deck(self) -> Deck: + """获取主 Deck""" + return self.deck + + def get_all_resources(self) -> Dict[str, PLRResource]: + """获取所有 PLR 资源""" + return self.plr_resources.copy() + + def find_resource_by_name(self, name: str) -> Optional[PLRResource]: + """按名称查找资源""" + return self.plr_resources.get(name) + + def find_resources_by_type(self, resource_type: type) -> List[PLRResource]: + """按类型查找资源""" + return [res for res in self.plr_resources.values() if isinstance(res, resource_type)] + + def sync_with_external_system(self) -> bool: + """与外部物料系统同步""" + if not self.resource_synchronizer: + logger.info(f"工作站 {self._ros_node.device_id} 没有配置资源同步器") + return True + + try: + success = self.resource_synchronizer.sync_from_external() + if success: + logger.info(f"工作站 {self._ros_node.device_id} 外部同步成功") + else: + logger.warning(f"工作站 {self._ros_node.device_id} 外部同步失败") + return success + except Exception as e: + logger.error(f"工作站 {self._ros_node.device_id} 外部同步异常: {e}") + return False + + # ============ 简化的工作流控制 ============ + + def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool: + """执行工作流""" + try: + # 设置工作流状态 + self.current_workflow_status = WorkflowStatus.INITIALIZING + self.workflow_parameters = parameters + self.workflow_start_time = time.time() + + # 委托给子类实现 + success = self._execute_workflow_impl(workflow_name, parameters) + + if success: + self.current_workflow_status = WorkflowStatus.RUNNING + logger.info(f"工作站 {self._ros_node.device_id} 工作流 {workflow_name} 启动成功") + else: + self.current_workflow_status = WorkflowStatus.ERROR + logger.error(f"工作站 {self._ros_node.device_id} 工作流 {workflow_name} 启动失败") + + return success + + except Exception as e: + self.current_workflow_status = WorkflowStatus.ERROR + logger.error(f"工作站 {self._ros_node.device_id} 执行工作流失败: {e}") + return False + + def stop_workflow(self, emergency: bool = False) -> bool: + """停止工作流""" + try: + if self.current_workflow_status in [WorkflowStatus.IDLE, WorkflowStatus.STOPPED]: + logger.warning(f"工作站 {self._ros_node.device_id} 没有正在运行的工作流") + return True + + self.current_workflow_status = WorkflowStatus.STOPPING + + # 委托给子类实现 + success = self._stop_workflow_impl(emergency) + + if success: + self.current_workflow_status = WorkflowStatus.STOPPED + logger.info(f"工作站 {self._ros_node.device_id} 工作流停止成功 (紧急: {emergency})") + else: + self.current_workflow_status = WorkflowStatus.ERROR + logger.error(f"工作站 {self._ros_node.device_id} 工作流停止失败") + + return success + + except Exception as e: + self.current_workflow_status = WorkflowStatus.ERROR + logger.error(f"工作站 {self._ros_node.device_id} 停止工作流失败: {e}") + return False + + # ============ 状态属性 ============ + + @property + def workflow_status(self) -> WorkflowStatus: + """获取当前工作流状态""" + return self.current_workflow_status + + @property + def is_busy(self) -> bool: + """检查工作站是否忙碌""" + return self.current_workflow_status in [ + WorkflowStatus.INITIALIZING, + WorkflowStatus.RUNNING, + WorkflowStatus.STOPPING, + ] + + @property + def workflow_runtime(self) -> float: + """获取工作流运行时间(秒)""" + if self.workflow_start_time is None: + return 0.0 + return time.time() - self.workflow_start_time + + +class ProtocolNode(WorkstationBase): + def __init__(self, protocol_type: List[str], deck: Optional[PLRResource], *args, **kwargs): + super().__init__(deck, *args, **kwargs) diff --git a/unilabos/devices/transport/__init__.py b/unilabos/devices/transport/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unilabos/devices/transport/agv_workstation.py b/unilabos/devices/transport/agv_workstation.py new file mode 100644 index 00000000..34263cae --- /dev/null +++ b/unilabos/devices/transport/agv_workstation.py @@ -0,0 +1,127 @@ +""" +AGV 通用转运工站 Driver + +继承 WorkstationBase,通过 WorkstationNodeCreator 自动获得 ROS2WorkstationNode 能力。 +Warehouse 作为 children 中的资源节点,由 attach_resource() 自动注册到 resource_tracker。 +deck=None,不使用 PLR Deck 抽象。 +""" + +from typing import Any, Dict, List, Optional + +from pylabrobot.resources import Deck + +from unilabos.devices.workstation.workstation_base import WorkstationBase +from unilabos.resources.warehouse import WareHouse +from unilabos.utils import logger + + +class AGVTransportStation(WorkstationBase): + """通用 AGV 转运工站 + + 初始化链路(零框架改动): + ROS2DeviceNode.__init__(): + issubclass(AGVTransportStation, WorkstationBase) → True + → WorkstationNodeCreator.create_instance(data): + data["deck"] = None + → DeviceClassCreator.create_instance(data) → AGVTransportStation(deck=None, ...) + → attach_resource(): children 中 type="warehouse" → resource_tracker.add_resource(wh) + → ROS2WorkstationNode(protocol_type=[...], children=[nav, arm], ...) + → driver.post_init(ros_node): + self.carrier 从 resource_tracker 中获取 WareHouse + """ + + def __init__( + self, + deck: Optional[Deck] = None, + children: Optional[List[Any]] = None, + route_table: Optional[Dict[str, Dict[str, str]]] = None, + device_roles: Optional[Dict[str, str]] = None, + **kwargs, + ): + super().__init__(deck=None, **kwargs) + self.route_table: Dict[str, Dict[str, str]] = route_table or {} + self.device_roles: Dict[str, str] = device_roles or {} + + # ============ 载具 (Warehouse) ============ + + @property + def carrier(self) -> Optional[WareHouse]: + """从 resource_tracker 中找到 AGV 载具 Warehouse""" + if not hasattr(self, "_ros_node"): + return None + for res in self._ros_node.resource_tracker.resources: + if isinstance(res, WareHouse): + return res + return None + + @property + def capacity(self) -> int: + """AGV 载具总容量(slot 数)""" + wh = self.carrier + if wh is None: + return 0 + return wh.num_items_x * wh.num_items_y * wh.num_items_z + + @property + def free_slots(self) -> List[str]: + """返回当前空闲 slot 名称列表""" + wh = self.carrier + if wh is None: + return [] + ordering = getattr(wh, "_ordering", {}) + return [name for name, site in ordering.items() if site.resource is None] + + @property + def occupied_slots(self) -> Dict[str, Any]: + """返回已占用的 slot → Resource 映射""" + wh = self.carrier + if wh is None: + return {} + ordering = getattr(wh, "_ordering", {}) + return {name: site.resource for name, site in ordering.items() if site.resource is not None} + + # ============ 路由查询 ============ + + def resolve_route(self, from_station: str, to_station: str) -> Dict[str, str]: + """查询路由表,返回导航和机械臂指令 + + Args: + from_station: 来源工站 ID + to_station: 目标工站 ID + + Returns: + {"nav_command": "...", "arm_pick": "...", "arm_place": "..."} + + Raises: + KeyError: 路由表中未找到对应路线 + """ + route_key = f"{from_station}->{to_station}" + if route_key not in self.route_table: + raise KeyError(f"路由表中未找到路线: {route_key}") + return self.route_table[route_key] + + def get_device_id(self, role: str) -> str: + """获取子设备 ID + + Args: + role: 设备角色,如 "navigator", "arm" + + Returns: + 设备 ID 字符串 + + Raises: + KeyError: 未配置该角色的设备 + """ + if role not in self.device_roles: + raise KeyError(f"未配置设备角色: {role},当前已配置: {list(self.device_roles.keys())}") + return self.device_roles[role] + + # ============ 生命周期 ============ + + def post_init(self, ros_node) -> None: + super().post_init(ros_node) + wh = self.carrier + if wh is not None: + logger.info(f"AGV {ros_node.device_id} 载具已就绪: {wh.name}, 容量={self.capacity}") + else: + logger.warning(f"AGV {ros_node.device_id} 未发现 Warehouse 载具资源") diff --git a/unilabos/devices/workstation/workstation_base.py b/unilabos/devices/workstation/workstation_base.py index 75fd7ea8..1cf920fc 100644 --- a/unilabos/devices/workstation/workstation_base.py +++ b/unilabos/devices/workstation/workstation_base.py @@ -197,6 +197,28 @@ def set_workstation_node(self, workstation_node: "ROS2WorkstationNode"): self._ros_node = workstation_node logger.info(f"工作站 {self._ros_node.device_id} 关联协议节点") + # ============ 物料转运回调 ============ + + def resource_tree_batch_transfer( + self, + transfers: list, + old_parents: list, + new_parents: list, + ) -> None: + """批量物料转运完成后的回调,供子类重写 + + 默认实现:逐个调用 resource_tree_transfer(如存在)。 + + Args: + transfers: 转移列表,每项包含 resource, from_parent, to_parent, to_site 等 + old_parents: 每个物料转移前的原父节点 + new_parents: 每个物料转移后的新父节点 + """ + func = getattr(self, "resource_tree_transfer", None) + if callable(func): + for t, old_parent, new_parent in zip(transfers, old_parents, new_parents): + func(old_parent, t["resource"], new_parent) + # ============ 设备操作接口 ============ def call_device_method(self, method: str, *args, **kwargs) -> Any: diff --git a/unilabos/messages/__init__.py b/unilabos/messages/__init__.py index 4ef5ac9f..0a1a1b4f 100644 --- a/unilabos/messages/__init__.py +++ b/unilabos/messages/__init__.py @@ -217,6 +217,24 @@ class AGVTransferProtocol(BaseModel): from_repo_position: str to_repo_position: str + +class BatchTransferItem(BaseModel): + """批量转运中的单个物料条目""" + resource_uuid: str = "" + resource_id: str = "" + from_position: str + to_position: str + + +class BatchTransferProtocol(BaseModel): + """批量物料转运协议 — 支持多物料一次性从来源工站转运到目标工站""" + from_repo: dict + to_repo: dict + transfer_resources: list # list[Resource dict],被转运的物料 + from_positions: list # list[str],来源 slot 位置(与 transfer_resources 平行) + to_positions: list # list[str],目标 slot 位置(与 transfer_resources 平行) + + #=============新添加的新的协议================ class AddProtocol(BaseModel): vessel: dict @@ -629,15 +647,16 @@ class HydrogenateProtocol(BaseModel): vessel: dict = Field(..., description="反应容器") __all__ = [ - "Point3D", "PumpTransferProtocol", "CleanProtocol", "SeparateProtocol", - "EvaporateProtocol", "EvacuateAndRefillProtocol", "AGVTransferProtocol", - "CentrifugeProtocol", "AddProtocol", "FilterProtocol", + "Point3D", "PumpTransferProtocol", "CleanProtocol", "SeparateProtocol", + "EvaporateProtocol", "EvacuateAndRefillProtocol", "AGVTransferProtocol", + "BatchTransferItem", "BatchTransferProtocol", + "CentrifugeProtocol", "AddProtocol", "FilterProtocol", "HeatChillProtocol", "HeatChillStartProtocol", "HeatChillStopProtocol", - "StirProtocol", "StartStirProtocol", "StopStirProtocol", - "TransferProtocol", "CleanVesselProtocol", "DissolveProtocol", + "StirProtocol", "StartStirProtocol", "StopStirProtocol", + "TransferProtocol", "CleanVesselProtocol", "DissolveProtocol", "FilterThroughProtocol", "RunColumnProtocol", "WashSolidProtocol", - "AdjustPHProtocol", "ResetHandlingProtocol", "DryProtocol", + "AdjustPHProtocol", "ResetHandlingProtocol", "DryProtocol", "RecrystallizeProtocol", "HydrogenateProtocol" ] # End Protocols diff --git a/unilabos/registry/devices/eit_agv.yaml b/unilabos/registry/devices/eit_agv.yaml new file mode 100644 index 00000000..baeeabd0 --- /dev/null +++ b/unilabos/registry/devices/eit_agv.yaml @@ -0,0 +1,163 @@ +eit_agv: + category: + - robot_agv + - eit_agv + class: + action_value_mappings: + auto-send: + feedback: {} + goal: {} + goal_default: + cmd: null + ex_data: '' + obj: receive_socket + handles: {} + placeholder_keys: {} + result: {} + schema: + description: AGV底层通信命令发送函数。通过TCP socket连接向AGV发送底层控制命令,支持pose(位置)、status(状态)、nav(导航)等命令类型。用于获取AGV当前位置坐标、运行状态或发送导航指令。该函数封装了AGV的通信协议,将命令转换为十六进制数据包并处理响应解析。 + properties: + feedback: {} + goal: + properties: + cmd: + type: string + ex_data: + default: '' + type: string + obj: + default: receive_socket + type: string + required: + - cmd + type: object + result: {} + required: + - goal + title: send参数 + type: object + type: UniLabJsonCommand + send_nav_task: + feedback: {} + goal: + command: command + goal_default: + command: '' + handles: {} + result: + success: success + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: SendCmd_Feedback + type: object + goal: + properties: + command: + type: string + required: + - command + title: SendCmd_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: SendCmd_Result + type: object + required: + - goal + title: SendCmd + type: object + type: SendCmd + transfer_manifest: + feedback: {} + goal: {} + goal_default: + manifest: null + handles: + input: + - data_key: manifest + data_source: handle + data_type: object + handler_key: manifest + io_type: source + label: 上游设备输出的转运 manifest + required: true + output: + - data_key: transfer_result + data_source: executor + data_type: object + handler_key: transfer_result + io_type: sink + label: AGV 转运执行结果 + result: + transfer_result: transfer_result + schema: + description: 执行标准化转运 manifest,供 UniLab 编排层调用。 + properties: + feedback: {} + goal: + properties: + manifest: + type: object + required: + - manifest + type: object + result: + properties: + transfer_result: + description: 转运执行结果,包含 success、transferred_trays、batches 等 + type: object + type: object + required: + - goal + title: transfer_manifest参数 + type: object + type: UniLabJsonCommand + module: unilabos.devices.eit_agv.controller.agv_controller:UniLabAGVController + status_types: + current_station: str + status: str + type: python + config_info: [] + description: EIT AGV自动导引车设备。复用SEER AGV驱动,用于EIT场景下的托盘搬运、站间转运与导航任务执行。 + handles: [] + icon: '' + init_param_schema: + config: + properties: + arm_ip: + type: string + arm_port: + type: integer + arm_timeout: + type: integer + deck: + type: object + protocol_type: + type: array + required: [] + type: object + data: + properties: + current_station: + type: string + status: + type: string + required: + - status + - current_station + type: object + version: 1.0.0 diff --git a/unilabos/registry/devices/eit_synthesis_station.yaml b/unilabos/registry/devices/eit_synthesis_station.yaml new file mode 100644 index 00000000..3e969828 --- /dev/null +++ b/unilabos/registry/devices/eit_synthesis_station.yaml @@ -0,0 +1,1070 @@ +eit_synthesis_station: + category: + - eit_synthesis_station + class: + action_value_mappings: + add_dilution: + feedback: {} + goal: {} + goal_default: + diluent_name: '' + dilution_volume_ul: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + diluent_name: + type: string + dilution_volume_ul: + type: number + required: + - diluent_name + - dilution_volume_ul + type: object + result: {} + required: + - goal + title: add_dilution参数 + type: object + type: UniLabJsonCommand + add_filter: + feedback: {} + goal: {} + goal_default: + filter_experiment_numbers: 全部 + filter_liquid_name: '' + filter_volume_ul: null + sampling_volume_ul: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + filter_experiment_numbers: + default: 全部 + type: string + filter_liquid_name: + type: string + filter_volume_ul: + type: number + sampling_volume_ul: + type: number + required: + - filter_liquid_name + - filter_volume_ul + - sampling_volume_ul + type: object + result: {} + required: + - goal + title: add_filter参数 + type: object + type: UniLabJsonCommand + add_magnet: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + experiment_no: + type: integer + required: [] + type: object + result: {} + required: + - goal + title: add_magnet参数 + type: object + type: UniLabJsonCommand + add_reaction: + feedback: {} + goal: {} + goal_default: + reaction_scale_mmol: 0.2 + reaction_temp_c: 40.0 + reaction_time_h: 8.0 + reactor_type: heat + stir_speed_rpm: 500 + wait_target_temp: false + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + reaction_scale_mmol: + default: 0.2 + type: number + reaction_temp_c: + default: 40.0 + type: number + reaction_time_h: + default: 8.0 + type: number + reactor_type: + default: heat + type: string + stir_speed_rpm: + default: 500 + type: integer + target_temp_c: + type: number + wait_target_temp: + default: false + type: boolean + required: [] + type: object + result: {} + required: + - goal + title: add_reaction参数 + type: object + type: UniLabJsonCommand + add_reagent: + feedback: {} + goal: {} + goal_default: + amount: null + name: '' + unit: '' + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + amount: + type: number + experiment_no: + type: integer + name: + type: string + unit: + type: string + required: + - name + - amount + - unit + type: object + result: {} + required: + - goal + title: add_reagent参数 + type: object + type: UniLabJsonCommand + add_reagent_list: + feedback: {} + goal: {} + goal_default: + formulation: null + handles: {} + placeholder_keys: + formulation: unilabos_formulation + result: {} + schema: + description: 批量添加试剂(前端统一使用 formulation 输入) + properties: + feedback: {} + goal: + properties: + formulation: + items: + properties: + experiment_no: + type: integer + liquids: + items: + properties: + name: + type: string + unit: + type: string + volume: + type: number + required: + - name + - volume + type: object + type: array + materials: + items: + properties: + mass: + type: number + name: + type: string + unit: + type: string + required: + - name + - mass + type: object + type: array + reagents: + items: + properties: + amount: + type: number + name: + type: string + unit: + type: string + required: + - name + - amount + type: object + type: array + type: object + type: array + required: + - formulation + type: object + result: {} + required: + - goal + title: add_reagent_list参数 + type: object + type: UniLabJsonCommand + add_stir: + feedback: {} + goal: {} + goal_default: + time_min: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + time_min: + type: number + required: + - time_min + type: object + result: {} + required: + - goal + title: add_stir参数 + type: object + type: UniLabJsonCommand + add_task: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: add_task参数 + type: object + type: UniLabJsonCommand + align_chemicals_with_file: + feedback: {} + goal: {} + goal_default: + file_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + type: string + required: + - file_path + type: object + result: {} + required: + - goal + title: align_chemicals_with_file参数 + type: object + type: UniLabJsonCommand + batch_in_tray_by_file: + feedback: {} + goal: {} + goal_default: + file_path: unilabos/devices/eit_synthesis_station/sheet/batch_in_tray.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + type: string + required: + - file_path + type: object + result: {} + required: + - goal + title: batch_in_tray_by_file参数 + type: object + type: UniLabJsonCommand + batch_in_tray_with_agv_transfer: + feedback: {} + goal: {} + goal_default: + block: true + file_path: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + block: + default: true + type: boolean + file_path: + type: string + required: [] + type: object + result: {} + required: + - goal + title: batch_in_tray_with_agv_transfer参数 + type: object + type: UniLabJsonCommand + batch_out_empty_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_empty_trays参数 + type: object + type: UniLabJsonCommand + batch_out_task_and_chemical_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: string + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_task_and_chemical_trays参数 + type: object + type: UniLabJsonCommand + batch_out_task_and_empty_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: string + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_task_and_empty_trays参数 + type: object + type: UniLabJsonCommand + batch_out_task_trays: + feedback: {} + goal: {} + goal_default: + ignore_missing: true + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ignore_missing: + default: true + type: boolean + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: string + timeout_s: + default: 900.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: batch_out_task_trays参数 + type: object + type: UniLabJsonCommand + batch_out_tray: + feedback: {} + goal: {} + goal_default: + layout_list: null + move_type: main_out + poll_interval_s: 1.0 + task_id: null + timeout_s: 900.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + layout_list: + items: + type: object + type: array + move_type: + default: main_out + type: string + poll_interval_s: + default: 1.0 + type: number + task_id: + type: integer + timeout_s: + default: 900.0 + type: number + required: + - layout_list + type: object + result: {} + required: + - goal + title: batch_out_tray参数 + type: object + type: UniLabJsonCommand + begin_task: + feedback: {} + goal: {} + goal_default: + auto_magnet: true + chemical_db_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + fixed_addition_order: false + max_error_mg: 1.0 + task_id: 0 + task_name: Auto_task + weighing_error_pct: 3.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + auto_magnet: + default: true + type: boolean + chemical_db_path: + type: string + fixed_addition_order: + default: false + type: boolean + max_error_mg: + default: 1.0 + type: number + task_id: + default: 0 + type: integer + task_name: + default: Auto_task + type: string + weighing_error_pct: + default: 3.0 + type: number + required: + - chemical_db_path + type: object + result: {} + required: + - goal + title: begin_task参数 + type: object + type: UniLabJsonCommand + check_resource_for_task: + feedback: {} + goal: {} + goal_default: + chemical_db_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + template_path: unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + chemical_db_path: + type: string + template_path: + type: string + required: + - template_path + - chemical_db_path + type: object + result: {} + required: + - goal + title: check_resource_for_task参数 + type: object + type: UniLabJsonCommand + create_task_by_file: + feedback: {} + goal: {} + goal_default: + chemical_db_path: unilabos/devices/eit_synthesis_station/sheet/chemical_list.xlsx + template_path: unilabos/devices/eit_synthesis_station/sheet/reaction_template.xlsx + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + chemical_db_path: + type: string + template_path: + type: string + required: + - template_path + - chemical_db_path + type: object + result: {} + required: + - goal + title: create_task_by_file参数 + type: object + type: UniLabJsonCommand + device_init: + feedback: {} + goal: {} + goal_default: + device_id: null + poll_interval_s: 1.0 + timeout_s: 600.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + device_id: + type: string + poll_interval_s: + default: 1.0 + type: number + timeout_s: + default: 600.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: device_init参数 + type: object + type: UniLabJsonCommand + execute_batch_in_payload: + feedback: {} + goal: {} + goal_default: + batch_in_payload: null + handles: + input: + - data_key: batch_in_payload + data_source: handle + data_type: array + handler_key: batch_in_payload + io_type: source + label: 单轮上料 payload 列表 + required: true + - data_key: transfer_result + data_source: handle + data_type: object + handler_key: transfer_result + io_type: source + label: AGV 转运结果(用于保证执行顺序) + required: false + result: + success: success + schema: + description: 执行手套箱单轮本地上料 payload。必须在 AGV 转运完成后才能调用。 + properties: + feedback: {} + goal: + properties: + batch_in_payload: + items: + type: object + type: array + required: + - batch_in_payload + type: object + result: + properties: + success: + description: 上料执行是否成功 + type: boolean + type: object + required: + - goal + title: execute_batch_in_payload参数 + type: object + type: UniLabJsonCommand + execute_task: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: execute_task参数 + type: object + type: UniLabJsonCommand + poll_analysis_run: + feedback: {} + goal: {} + goal_default: + poll_interval: 30.0 + task_id: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + poll_interval: + default: 30.0 + type: number + task_id: + type: string + required: [] + type: object + result: {} + required: + - goal + title: poll_analysis_run参数 + type: object + type: UniLabJsonCommand + prepare_batch_in_with_agv_manifest: + feedback: {} + goal: {} + goal_default: + file_path: null + handles: + output: + - data_key: manifest + data_source: executor + data_type: object + handler_key: manifest + io_type: sink + label: 上料 manifest(含 transfer_tasks + batch_in_payload) + - data_key: batch_in_payload + data_source: executor + data_type: array + handler_key: batch_in_payload + io_type: sink + label: 单轮上料 payload 列表 + result: + batch_in_payload: batch_in_payload + manifest: manifest + schema: + description: 生成供 UniLab 编排层使用的上料 manifest。 + properties: + feedback: {} + goal: + properties: + file_path: + type: string + required: [] + type: object + result: + properties: + batch_in_payload: + description: 单轮上料 payload 列表 + type: array + manifest: + description: 标准 AGV 转运 manifest,包含 transfer_tasks 和 batch_in_payload + type: object + type: object + required: + - goal + title: prepare_batch_in_with_agv_manifest参数 + type: object + type: UniLabJsonCommand + run_analysis: + feedback: {} + goal: {} + goal_default: + task_id: null + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + task_id: + type: string + required: [] + type: object + result: {} + required: + - goal + title: run_analysis参数 + type: object + type: UniLabJsonCommand + start_task: + feedback: {} + goal: {} + goal_default: + check_glovebox_env: true + oxygen_limit_ppm: 10.0 + task_id: null + water_limit_ppm: 10.0 + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + check_glovebox_env: + default: true + type: boolean + oxygen_limit_ppm: + default: 10.0 + type: number + task_id: + type: string + water_limit_ppm: + default: 10.0 + type: number + required: [] + type: object + result: {} + required: + - goal + title: start_task参数 + type: object + type: UniLabJsonCommand + submit_experiment_task: + feedback: {} + goal: {} + goal_default: + auto_magnet: true + chemical_db_path: null + diluent_name: '' + duration: '8' + fixed_order: false + internal_std_name: '' + reaction_type: heat + rows: null + stir_speed: '500' + stir_time_after_std: '' + target_temp: '30' + task_name: Unilab_Auto_Job + temperature: '40' + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + auto_magnet: + default: true + type: boolean + chemical_db_path: + type: string + diluent_name: + default: '' + type: string + duration: + default: '8' + type: string + fixed_order: + default: false + type: boolean + internal_std_name: + default: '' + type: string + reaction_type: + default: heat + type: string + rows: + type: array + stir_speed: + default: '500' + type: string + stir_time_after_std: + default: '' + type: string + target_temp: + default: '30' + type: string + task_name: + default: Unilab_Auto_Job + type: string + temperature: + default: '40' + type: string + required: + - chemical_db_path + type: object + result: {} + required: + - goal + title: submit_experiment_task参数 + type: object + type: UniLabJsonCommand + unload_task_and_empty_trays_return_manifest: + feedback: {} + goal: {} + goal_default: + task_id: null + handles: + output: + - data_key: manifest + data_source: executor + data_type: object + handler_key: manifest + io_type: sink + label: 下料 manifest(含 transfer_tasks) + result: + manifest: manifest + schema: + description: 执行手套箱本地下料并返回 AGV manifest。task_id 为空时自动选择最近完成的任务。 + properties: + feedback: {} + goal: + properties: + task_id: + description: 任务 ID,为空时自动选择最近完成的任务 + type: integer + required: [] + type: object + result: + properties: + manifest: + description: 标准 AGV 下料 manifest,包含 transfer_tasks 和 analysis_task_ids + type: object + type: object + required: + - goal + title: unload_task_and_empty_trays_return_manifest参数 + type: object + type: UniLabJsonCommand + wait_task_with_ops: + feedback: {} + goal: {} + goal_default: {} + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: wait_task_with_ops参数 + type: object + type: UniLabJsonCommand + 加内标: + feedback: {} + goal: {} + goal_default: + amount_ul_or_mg: null + name: '' + handles: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + amount_ul_or_mg: + type: number + name: + type: string + required: + - name + - amount_ul_or_mg + type: object + result: {} + required: + - goal + title: add_internal_standard参数 + type: object + type: UniLabJsonCommand + module: unilabos.devices.eit_synthesis_station.manager.station_manager:SynthesisStationManager + status_types: {} + type: python + config_info: [] + description: '' + handles: [] + icon: '' + init_param_schema: + config: + properties: + config: + type: string + deck: + type: string + settings: + type: string + required: [] + type: object + data: + properties: {} + required: [] + type: object + registry_type: device + version: 1.0.0 diff --git a/unilabos/registry/devices/liquid_handler.yaml b/unilabos/registry/devices/liquid_handler.yaml index b04d6317..fbc09e6a 100644 --- a/unilabos/registry/devices/liquid_handler.yaml +++ b/unilabos/registry/devices/liquid_handler.yaml @@ -4056,7 +4056,8 @@ liquid_handler: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: 0 + mix_times: + - 0 mix_vol: 0 none_keys: - '' @@ -4212,9 +4213,11 @@ liquid_handler: mix_stage: type: string mix_times: - maximum: 2147483647 - minimum: -2147483648 - type: integer + items: + maximum: 2147483647 + minimum: -2147483648 + type: integer + type: array mix_vol: maximum: 2147483647 minimum: -2147483648 @@ -5077,7 +5080,8 @@ liquid_handler.biomek: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: 0 + mix_times: + - 0 mix_vol: 0 none_keys: - '' @@ -5235,9 +5239,11 @@ liquid_handler.biomek: mix_stage: type: string mix_times: - maximum: 2147483647 - minimum: -2147483648 - type: integer + items: + maximum: 2147483647 + minimum: -2147483648 + type: integer + type: array mix_vol: maximum: 2147483647 minimum: -2147483648 @@ -10075,7 +10081,8 @@ liquid_handler.prcxi: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: 0 + mix_times: + - 0 mix_vol: 0 none_keys: - '' @@ -10231,9 +10238,11 @@ liquid_handler.prcxi: mix_stage: type: string mix_times: - maximum: 2147483647 - minimum: -2147483648 - type: integer + items: + maximum: 2147483647 + minimum: -2147483648 + type: integer + type: array mix_vol: maximum: 2147483647 minimum: -2147483648 diff --git a/unilabos/registry/devices/transport_agv.yaml b/unilabos/registry/devices/transport_agv.yaml new file mode 100644 index 00000000..3a56b511 --- /dev/null +++ b/unilabos/registry/devices/transport_agv.yaml @@ -0,0 +1,368 @@ +agv_transport_station: + category: + - work_station + - transport_agv + class: + action_value_mappings: + AGVTransferProtocol: + feedback: {} + goal: + from_repo: from_repo + from_repo_position: from_repo_position + to_repo: to_repo + to_repo_position: to_repo_position + goal_default: + from_repo: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + from_repo_position: '' + to_repo: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + to_repo_position: '' + handles: {} + result: {} + schema: + description: '' + properties: + feedback: + properties: + status: + type: string + required: + - status + title: AGVTransfer_Feedback + type: object + goal: + properties: + from_repo: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: from_repo + type: object + from_repo_position: + type: string + to_repo: + properties: + category: + type: string + children: + items: + type: string + type: array + config: + type: string + data: + type: string + id: + type: string + name: + type: string + parent: + type: string + pose: + properties: + orientation: + properties: + w: + type: number + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + - w + title: orientation + type: object + position: + properties: + x: + type: number + y: + type: number + z: + type: number + required: + - x + - y + - z + title: position + type: object + required: + - position + - orientation + title: pose + type: object + sample_id: + type: string + type: + type: string + required: + - id + - name + - sample_id + - children + - parent + - type + - category + - pose + - config + - data + title: to_repo + type: object + to_repo_position: + type: string + required: + - from_repo + - from_repo_position + - to_repo + - to_repo_position + title: AGVTransfer_Goal + type: object + result: + properties: + return_info: + type: string + success: + type: boolean + required: + - return_info + - success + title: AGVTransfer_Result + type: object + required: + - goal + title: AGVTransfer + type: object + type: AGVTransfer + BatchTransferProtocol: + feedback: {} + goal: + from_positions: from_positions + from_repo: from_repo + to_positions: to_positions + to_repo: to_repo + transfer_resources: transfer_resources + goal_default: + from_positions: [] + from_repo: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + to_positions: [] + to_repo: + category: '' + children: [] + config: '' + data: '' + id: '' + name: '' + parent: '' + pose: + orientation: + w: 1.0 + x: 0.0 + y: 0.0 + z: 0.0 + position: + x: 0.0 + y: 0.0 + z: 0.0 + sample_id: '' + type: '' + transfer_resources: [] + handles: {} + result: {} + schema: + description: AGV 批量物料转运协议 + properties: + feedback: {} + goal: + properties: + from_positions: + items: + type: string + type: array + from_repo: + type: object + to_positions: + items: + type: string + type: array + to_repo: + type: object + transfer_resources: + items: + type: object + type: array + required: + - from_repo + - to_repo + - transfer_resources + - from_positions + - to_positions + type: object + result: {} + required: + - goal + title: BatchTransferProtocol + type: object + type: BatchTransfer + module: unilabos.devices.transport.agv_workstation:AGVTransportStation + status_types: + agv_status: String + carrier_status: String + type: python + config_info: [] + description: 通用 AGV 转运工站。继承 WorkstationBase,通过 Warehouse 子资源管理物料中转态。支持单物料和批量物料转运协议,路由表和子设备配置全部由 + JSON 驱动。 + handles: [] + icon: '' + init_param_schema: + config: + properties: + device_roles: + properties: + arm: + type: string + navigator: + type: string + type: object + protocol_type: + items: + type: string + type: array + route_table: + type: object + type: object + data: + properties: + agv_status: + type: string + carrier_status: + type: string + type: object + version: 1.0.0 diff --git a/unilabos/registry/registry.py b/unilabos/registry/registry.py index 2a277664..cc9871f8 100644 --- a/unilabos/registry/registry.py +++ b/unilabos/registry/registry.py @@ -632,6 +632,10 @@ def _preserve_field_descriptions(self, new_schema: Dict[str, Any], previous_sche # 保留字段的 title(用户自定义的中文名) if "title" in prev_field and prev_field["title"]: field_schema["title"] = prev_field["title"] + # 保留旧 schema 中手动定义的复杂嵌套结构(如 items/properties/required) + for rich_key in ("items", "properties", "required"): + if rich_key in prev_field and rich_key not in field_schema: + field_schema[rich_key] = prev_field[rich_key] def _is_typed_dict(self, annotation: Any) -> bool: """ @@ -818,20 +822,26 @@ def _load_single_device_file( "goal_default": {i["name"]: i["default"] for i in v["args"]}, "handles": old_action_configs.get(f"auto-{k}", {}).get("handles", []), "placeholder_keys": { - i["name"]: ( - "unilabos_resources" - if i["type"] == "unilabos.registry.placeholder_type:ResourceSlot" - or i["type"] == ("list", "unilabos.registry.placeholder_type:ResourceSlot") - else "unilabos_devices" - ) - for i in v["args"] - if i.get("type", "") - in [ - "unilabos.registry.placeholder_type:ResourceSlot", - "unilabos.registry.placeholder_type:DeviceSlot", - ("list", "unilabos.registry.placeholder_type:ResourceSlot"), - ("list", "unilabos.registry.placeholder_type:DeviceSlot"), - ] + # 保留旧配置里手动定义的 placeholder(例如 unilabos_formulation) + **old_action_configs.get(f"auto-{k}", {}).get("placeholder_keys", {}), + # 自动补充 ResourceSlot / DeviceSlot 的占位符 + **{ + i["name"]: ( + "unilabos_resources" + if i["type"] == "unilabos.registry.placeholder_type:ResourceSlot" + or i["type"] + == ("list", "unilabos.registry.placeholder_type:ResourceSlot") + else "unilabos_devices" + ) + for i in v["args"] + if i.get("type", "") + in [ + "unilabos.registry.placeholder_type:ResourceSlot", + "unilabos.registry.placeholder_type:DeviceSlot", + ("list", "unilabos.registry.placeholder_type:ResourceSlot"), + ("list", "unilabos.registry.placeholder_type:DeviceSlot"), + ] + }, }, **({"always_free": True} if v.get("always_free") else {}), } diff --git a/unilabos/registry/resources/eit_agv/deck.yaml b/unilabos/registry/resources/eit_agv/deck.yaml new file mode 100644 index 00000000..cfed48dc --- /dev/null +++ b/unilabos/registry/resources/eit_agv/deck.yaml @@ -0,0 +1,14 @@ +EIT_AGV_Deck: + category: + - deck + - EIT + - eit_agv + class: + module: unilabos.resources.eit_agv.decks:EIT_AGV_Deck + type: pylabrobot + description: EIT AGV Deck + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 diff --git a/unilabos/registry/resources/eit_synthesis_station/bottle_carriers.yaml b/unilabos/registry/resources/eit_synthesis_station/bottle_carriers.yaml new file mode 100644 index 00000000..2f645e4c --- /dev/null +++ b/unilabos/registry/resources/eit_synthesis_station/bottle_carriers.yaml @@ -0,0 +1,169 @@ +2mL试剂瓶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_REAGENT_BOTTLE_TRAY_2ML + type: pylabrobot + description: EIT_REAGENT_BOTTLE_TRAY_2ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +8mL试剂瓶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_REAGENT_BOTTLE_TRAY_8ML + type: pylabrobot + description: EIT_REAGENT_BOTTLE_TRAY_8ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +40mL试剂瓶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_REAGENT_BOTTLE_TRAY_40ML + type: pylabrobot + description: EIT_REAGENT_BOTTLE_TRAY_40ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +125mL试剂瓶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_REAGENT_BOTTLE_TRAY_125ML + type: pylabrobot + description: EIT_REAGENT_BOTTLE_TRAY_125ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +30mL粉末桶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_POWDER_BUCKET_TRAY_30ML + type: pylabrobot + description: EIT_POWDER_BUCKET_TRAY_30ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +1mLTip头托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_TIP_TRAY_1ML + type: pylabrobot + description: EIT_TIP_TRAY_1ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +5mLTip头托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_TIP_TRAY_5ML + type: pylabrobot + description: EIT_TIP_TRAY_5ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +50μLTip头托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_TIP_TRAY_50UL + type: pylabrobot + description: EIT_TIP_TRAY_50UL + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +2mL反应试管托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_REACTION_TUBE_TRAY_2ML + type: pylabrobot + description: EIT_REACTION_TUBE_TRAY_2ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +2mL试管磁子托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_TEST_TUBE_MAGNET_TRAY_2ML + type: pylabrobot + description: EIT_TEST_TUBE_MAGNET_TRAY_2ML + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +反应密封盖托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_REACTION_SEAL_CAP_TRAY + type: pylabrobot + description: EIT_REACTION_SEAL_CAP_TRAY + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +闪滤瓶内瓶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_FLASH_FILTER_INNER_BOTTLE_TRAY + type: pylabrobot + description: EIT_FLASH_FILTER_INNER_BOTTLE_TRAY + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +闪滤瓶外瓶托盘: + category: + - bottle_carriers + - EIT + class: + module: unilabos.resources.eit_synthesis_station.bottle_carriers:EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY + type: pylabrobot + description: EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 \ No newline at end of file diff --git a/unilabos/registry/resources/eit_synthesis_station/bottles.yaml b/unilabos/registry/resources/eit_synthesis_station/bottles.yaml new file mode 100644 index 00000000..03506bce --- /dev/null +++ b/unilabos/registry/resources/eit_synthesis_station/bottles.yaml @@ -0,0 +1,110 @@ +2mL试剂瓶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_REAGENT_BOTTLE_2ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +8mL试剂瓶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_REAGENT_BOTTLE_8ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +40mL试剂瓶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_REAGENT_BOTTLE_40ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +125mL试剂瓶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_REAGENT_BOTTLE_125ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +30mL粉桶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_POWDER_BUCKET_30ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +闪滤瓶内瓶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_FLASH_FILTER_INNER_BOTTLE + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +闪滤瓶外瓶: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_FLASH_FILTER_OUTER_BOTTLE + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +反应密封盖: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_REACTION_SEAL_CAP + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +2mL反应试管: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_REACTION_TUBE_2ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 +2mL试管磁子: + category: + - items + - EIT + class: + module: unilabos.resources.eit_synthesis_station.items:EIT_TEST_TUBE_MAGNET_2ML + type: pylabrobot + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 \ No newline at end of file diff --git a/unilabos/registry/resources/eit_synthesis_station/deck.yaml b/unilabos/registry/resources/eit_synthesis_station/deck.yaml new file mode 100644 index 00000000..549cf09d --- /dev/null +++ b/unilabos/registry/resources/eit_synthesis_station/deck.yaml @@ -0,0 +1,13 @@ +EIT_Synthesis_Station_Deck: + category: + - deck + - EIT + class: + module: unilabos.resources.eit_synthesis_station.decks:EIT_Synthesis_Station_Deck + type: pylabrobot + description: EIT Synthesis Station Deck + handles: [] + icon: + init_param_schema: {} + registry_type: resource + version: 1.0.0 \ No newline at end of file diff --git a/unilabos/resources/eit_agv/decks.py b/unilabos/resources/eit_agv/decks.py new file mode 100644 index 00000000..e950bbac --- /dev/null +++ b/unilabos/resources/eit_agv/decks.py @@ -0,0 +1,96 @@ +""" +EIT AGV 资源甲板定义 + +设计目标: +1) 为 AGV 提供一个独立的前端可视化区域(独立设备,不并入任一工站 deck)。 +2) 结构:Deck → WareHouse("AGV") → ResourceHolder(AGV-1 ~ AGV-4),共 1x4 = 4 个车载槽位。 +3) 物料状态在运行时由 pick/put 操作实时追踪更新,不依赖定时轮询 shelf。 +4) 上报前端时使用与 Synthesis Station 相同的资源树扁平化逻辑,空 ResourceHolder 自然不显示。 +""" + +import uuid +from typing import Any +from typing import Self + +from pylabrobot.resources import Coordinate, Deck + +from unilabos.utils.log import logger + + +class EIT_AGV_Deck(Deck): + """ + AGV 车载 Deck。 + + 层级:Deck → WareHouse("AGV") → ResourceHolder(AGV-1 ~ AGV-4) + - WareHouse 由 eit_warehouse_AGV 工厂创建(1x4 布局) + - 物料由 pick_tray_with_material / put_tray_with_material 实时追踪更新 + - 上报时扁平化,空 ResourceHolder 不显示(与手套箱 TB 区一致) + """ + + def __init__( + self, + name: str = "EIT_AGV_Deck", + size_x: float = 800.0, + size_y: float = 200.0, + size_z: float = 200.0, + setup: bool = False, + **kwargs: Any, + ) -> None: + super().__init__(name=name, size_x=size_x, size_y=size_y, size_z=size_z) + if not getattr(self, "unilabos_uuid", None): + self.unilabos_uuid = str(uuid.uuid4()) + self.warehouses = {} + self.warehouse_locations = {} + if setup: + self.setup() + + @classmethod + def deserialize(cls, data, allow_marshal=False) -> Self: + """ + 反序列化:跳过 setup() 以避免重复生成 children。 + """ + data_modified = dict(data) + requested_setup = bool(data_modified.get("setup", False)) + data_modified["setup"] = False + resource = super().deserialize(data_modified, allow_marshal=allow_marshal) + if not resource.children and requested_setup: + logger.info("EIT AGV Deck 反序列化后无子资源,补执行 setup()") + resource.setup() + else: + # 从已有 children 中恢复 warehouses 字典 + resource.warehouses = {} + resource.warehouse_locations = {} + for child in resource.children: + if hasattr(child, "num_items_x"): # WareHouse 类型 + resource.warehouses[child.name] = child + return resource + + def _recursive_assign_uuid(self, resource: Any) -> None: + """递归补齐资源 UUID。""" + if not hasattr(resource, "unilabos_uuid") or not resource.unilabos_uuid: + resource.unilabos_uuid = str(uuid.uuid4()) + if hasattr(resource, "children"): + for child in resource.children: + self._recursive_assign_uuid(child) + + def setup(self) -> None: + """ + 构建 AGV 车载资源结构:Deck → WareHouse("AGV") → ResourceHolder × 4 + 使用 eit_warehouse_AGV 工厂函数创建 WareHouse(1x4 布局)。 + """ + from unilabos.resources.eit_synthesis_station.warehouses import eit_warehouse_AGV + + self.warehouses = {} + self.warehouse_locations = {} + + agv_wh = eit_warehouse_AGV("AGV") + self._recursive_assign_uuid(agv_wh) + loc = Coordinate(10.0, 10.0, 0.0) + self.assign_child_resource(agv_wh, location=loc) + self.warehouses["AGV"] = agv_wh + self.warehouse_locations["AGV"] = loc + + logger.info(f"已将仓库 AGV 挂载到 Deck") + + self._recursive_assign_uuid(self) + logger.info("EIT AGV Deck: 已构建 1x4 = 4 个车载槽位 (Deck → WareHouse → ResourceHolder)") diff --git a/unilabos/resources/eit_synthesis_station/bottle_carriers.py b/unilabos/resources/eit_synthesis_station/bottle_carriers.py new file mode 100644 index 00000000..e173221d --- /dev/null +++ b/unilabos/resources/eit_synthesis_station/bottle_carriers.py @@ -0,0 +1,236 @@ +from pylabrobot.resources import ( + ResourceHolder, + Coordinate, + create_ordered_items_2d +) +from unilabos.resources.itemized_carrier import BottleCarrier +from unilabos.devices.eit_synthesis_station.config.constants import TraySpec, ResourceCode +import uuid +from typing import Callable, Optional +from unilabos.resources.eit_synthesis_station.items import ( + EIT_REAGENT_BOTTLE_2ML, + EIT_REAGENT_BOTTLE_8ML, + EIT_REAGENT_BOTTLE_40ML, + EIT_REAGENT_BOTTLE_125ML, + EIT_FLASH_FILTER_INNER_BOTTLE, + EIT_FLASH_FILTER_OUTER_BOTTLE, + EIT_TEST_TUBE_MAGNET_2ML, + EIT_REACTION_SEAL_CAP, + EIT_REACTION_TUBE_2ML, + EIT_POWDER_BUCKET_30ML +) + +def _create_eit_tray( + name: str, + tray_type_enum: str, + size: tuple, + model_code: str, + item_factory: Optional[Callable[[str], object]] = None, + prefill_items: bool = True) -> BottleCarrier: + """通用的 EIT 托盘创建工厂函数""" + cols, rows = getattr(TraySpec, tray_type_enum) # 从 TraySpec 获取 (8, 6) 等规格 + + size_x, size_y, size_z = size + margin = 6.0 + min_cell_x = size_x / max(cols, 1) + min_cell_y = size_y / max(rows, 1) + bottle_diameter = 0.6 * min(min_cell_x, min_cell_y) + if cols <= 1 and rows <= 1: + bottle_diameter = 0.6 * min(size_x, size_y) + spacing_x = (size_x - 2 * margin - bottle_diameter) / (cols - 1) if cols > 1 else 0.0 + spacing_y = (size_y - 2 * margin - bottle_diameter) / (rows - 1) if rows > 1 else 0.0 + offset_x = (size_x - (cols - 1) * spacing_x - bottle_diameter) / 2.0 + offset_y = (size_y - (rows - 1) * spacing_y - bottle_diameter) / 2.0 + site_size_z = min(10.0, size_z * 0.5) + carrier_size_z = size_z + + sites = create_ordered_items_2d( + klass=ResourceHolder, + num_items_x=cols, + num_items_y=rows, + item_dx=spacing_x, + item_dy=spacing_y, + dz=site_size_z, + dx=offset_x, + dy=offset_y, + size_x=bottle_diameter, + size_y=bottle_diameter, + size_z=carrier_size_z, + ) + for key, site in sites.items(): + site.name = str(key) + + carrier = BottleCarrier( + name=name, + size_x=size[0], + size_y=size[1], + size_z=size[2], + sites=sites, + model=str(int(model_code)), + category="bottle_carrier", + hide_label=True, + ) + + carrier.unilabos_uuid = str(uuid.uuid4()) + for site in carrier.children: + site.unilabos_uuid = str(uuid.uuid4()) + + carrier.num_items_x = cols #cols列 + carrier.num_items_y = rows #rows行 + carrier.num_items_z = 1 + + if item_factory and prefill_items: + LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ordering = [] + + # 外层循环遍历列 (x 方向) + for x in range(cols): + # 内层循环遍历行 (y 方向) + for y in range(rows): + # LETTERS[y] 将 0->A, 1->B... + # x + 1 将列索引转换为从 1 开始的数字 + label = f"{LETTERS[y]}{x + 1}" + ordering.append(label) + for c in range(cols): + for r in range(rows): + idx = c * rows + r + carrier[idx] = item_factory(f"{ordering[idx]}@{name}") + + return carrier + +def EIT_REAGENT_BOTTLE_TRAY_2ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 2 mL 试剂瓶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="REAGENT_BOTTLE_TRAY_2ML", + size=(127.8, 85.5, 20.0), + model_code=ResourceCode.REAGENT_BOTTLE_TRAY_2ML, + item_factory=EIT_REAGENT_BOTTLE_2ML, + prefill_items=prefill_items, + ) + +def EIT_REAGENT_BOTTLE_TRAY_8ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 8 mL 试剂瓶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="REAGENT_BOTTLE_TRAY_8ML", + size=(127.8, 85.5, 20.0), + model_code=ResourceCode.REAGENT_BOTTLE_TRAY_8ML, + item_factory=EIT_REAGENT_BOTTLE_8ML, + prefill_items=prefill_items, + ) + +def EIT_REAGENT_BOTTLE_TRAY_40ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 40 mL 试剂瓶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="REAGENT_BOTTLE_TRAY_40ML", + size=(127.8, 85.5, 30.0), + model_code=ResourceCode.REAGENT_BOTTLE_TRAY_40ML, + item_factory=EIT_REAGENT_BOTTLE_40ML, + prefill_items=prefill_items, + ) + +def EIT_REAGENT_BOTTLE_TRAY_125ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 125 mL 试剂瓶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="REAGENT_BOTTLE_TRAY_125ML", + size=(127.8, 85.5, 40.0), + model_code=ResourceCode.REAGENT_BOTTLE_TRAY_125ML, + item_factory=EIT_REAGENT_BOTTLE_125ML, + prefill_items=prefill_items, + ) + +def EIT_POWDER_BUCKET_TRAY_30ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 30 mL 粉桶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="POWDER_BUCKET_TRAY_30ML", + size=(127.8, 85.5, 30.0), + model_code=ResourceCode.POWDER_BUCKET_TRAY_30ML, + item_factory=EIT_POWDER_BUCKET_30ML, + prefill_items=prefill_items, + ) + +def EIT_TIP_TRAY_1ML(name: str) -> BottleCarrier: + """创建 1 mL Tip 头托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="TIP_TRAY_1ML", + size=(127.8, 85.5, 40.0), + model_code=ResourceCode.TIP_TRAY_1ML + ) + +def EIT_TIP_TRAY_5ML(name: str) -> BottleCarrier: + """创建 5 mL Tip 头托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="TIP_TRAY_5ML", + size=(127.8, 85.5, 40.0), + model_code=ResourceCode.TIP_TRAY_5ML + ) + +def EIT_TIP_TRAY_50UL(name: str) -> BottleCarrier: + """创建 50 μL Tip 头托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="TIP_TRAY_50UL", + size=(127.8, 85.5, 40.0), + model_code=ResourceCode.TIP_TRAY_50UL + ) + +def EIT_REACTION_TUBE_TRAY_2ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 2 mL 反应试管托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="REACTION_TUBE_TRAY_2ML", + size=(127.8, 85.5, 30.0), + model_code=ResourceCode.REACTION_TUBE_TRAY_2ML, + item_factory=EIT_REACTION_TUBE_2ML, + prefill_items=prefill_items, + ) + +def EIT_TEST_TUBE_MAGNET_TRAY_2ML(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 2 mL 试管磁子托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="TEST_TUBE_MAGNET_TRAY_2ML", + size=(127.8, 85.5, 30.0), + model_code=ResourceCode.TEST_TUBE_MAGNET_TRAY_2ML, + item_factory=EIT_TEST_TUBE_MAGNET_2ML, + prefill_items=prefill_items, + ) + +def EIT_REACTION_SEAL_CAP_TRAY(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 反应密封盖托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="REACTION_SEAL_CAP_TRAY", + size=(127.8, 85.5, 20.0), + model_code=ResourceCode.REACTION_SEAL_CAP_TRAY, + item_factory=EIT_REACTION_SEAL_CAP, + prefill_items=prefill_items, + ) + +def EIT_FLASH_FILTER_INNER_BOTTLE_TRAY(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 闪滤瓶内瓶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="FLASH_FILTER_INNER_BOTTLE_TRAY", + size=(127.8, 85.5, 30.0), + model_code=ResourceCode.FLASH_FILTER_INNER_BOTTLE_TRAY, + item_factory=EIT_FLASH_FILTER_INNER_BOTTLE, + prefill_items=prefill_items, + ) + +def EIT_FLASH_FILTER_OUTER_BOTTLE_TRAY(name: str, prefill_items: bool = True) -> BottleCarrier: + """创建 闪滤瓶外瓶托盘""" + return _create_eit_tray( + name=name, + tray_type_enum="FLASH_FILTER_OUTER_BOTTLE_TRAY", + size=(127.8, 85.5, 30.0), + model_code=ResourceCode.FLASH_FILTER_OUTER_BOTTLE_TRAY, + item_factory=EIT_FLASH_FILTER_OUTER_BOTTLE, + prefill_items=prefill_items, + ) diff --git a/unilabos/resources/eit_synthesis_station/decks.py b/unilabos/resources/eit_synthesis_station/decks.py new file mode 100644 index 00000000..bf65101d --- /dev/null +++ b/unilabos/resources/eit_synthesis_station/decks.py @@ -0,0 +1,114 @@ +from __future__ import annotations +from typing import Self + +from pylabrobot.resources import Deck, Coordinate, Rotation +from unilabos.utils.log import logger +import uuid + +from unilabos.resources.eit_synthesis_station.warehouses import ( + eit_warehouse_W, + eit_warehouse_N, + eit_warehouse_TB, + eit_warehouse_AS, + eit_warehouse_FF, + eit_warehouse_MS, + eit_warehouse_MSB, + eit_warehouse_SC, + eit_warehouse_T, + eit_warehouse_TS, +) + +class EIT_Synthesis_Station_Deck(Deck): + def __init__( + self, + name: str = "Synthesis_Station_Deck", + size_x: float = 2800.0, + size_y: float = 1500.0, + size_z: float = 0.0, + category: str = "deck", + setup: bool = False, + **kwargs + ) -> None: + super().__init__(name=name, size_x=size_x, size_y=size_y, size_z=size_z) + if not getattr(self, "unilabos_uuid", None): + self.unilabos_uuid = str(uuid.uuid4()) + if setup: + self.setup() + + @classmethod + def deserialize(cls, data, allow_marshal=False) -> Self: + """ + 反序列化时默认跳过 setup(),由序列化数据提供 children,避免双重挂载。 + + 兼容设备图中仅声明空 deck + setup=true 的场景:如果反序列化后没有任何子资源, + 且原始数据请求 setup,则补跑一次 setup(),确保首次启动能自动生成 WareHouse。 + """ + data_modified = dict(data) + requested_setup = bool(data_modified.get("setup", False)) + data_modified["setup"] = False + resource = super().deserialize(data_modified, allow_marshal=allow_marshal) + if not resource.children and requested_setup: + logger.info("EIT Deck 反序列化后无子资源,按 setup=True 补执行初始化") + resource.setup() + else: + resource.warehouses = {} + resource.warehouse_locations = {} + for child in resource.children: + resource.warehouses[child.name] = child + if child.location is not None: + resource.warehouse_locations[child.name] = child.location + return resource + + def _recursive_assign_uuid(self, res): + """递归为资源及其所有现有子资源分配 UUID""" + if not hasattr(res, "unilabos_uuid") or not res.unilabos_uuid: + res.unilabos_uuid = str(uuid.uuid4()) + for child in res.children: + self._recursive_assign_uuid(child) + + + def setup(self) -> None: + self.warehouses = { + "W": eit_warehouse_W("W"), + "N": eit_warehouse_N("N"), + "TB": eit_warehouse_TB("TB"), + "AS": eit_warehouse_AS("AS"), + "FF": eit_warehouse_FF("FF"), + "MS": eit_warehouse_MS("MS"), + "MSB": eit_warehouse_MSB("MSB"), + "SC": eit_warehouse_SC("SC"), + "T": eit_warehouse_T("T"), + "TS": eit_warehouse_TS("TS"), + } + self.warehouse_locations = { + "W": Coordinate(80.0, 80.0, 0.0), + "TB": Coordinate(80.0, 560.0, 0.0), + "N": Coordinate(80.0, 848.0, 0.0), + "AS": Coordinate(1400.0, 80.0, 0.0), + "FF": Coordinate(1400.0, 360.0, 0.0), + "MS": Coordinate(1400.0, 540.0, 0.0), + "MSB": Coordinate(1400.0, 720.0, 0.0), + "SC": Coordinate(2100.0, 720.0, 0.0), + "T": Coordinate(2100.0, 80.0, 0.0), + "TS": Coordinate(2100.0, 360.0, 0.0), + } + + for zone_key, warehouse in self.warehouses.items(): + location = self.warehouse_locations.get(zone_key) + if location: + self._recursive_assign_uuid(warehouse) + self.assign_child_resource(warehouse, location) + logger.info(f"已将仓库 {zone_key} 挂载到 Deck") + + self._recursive_assign_uuid(self) + logger.info("EIT Deck 全量资源 UUID 校验完成") + + def _recursive_assign_uuid(self, res): + # 如果没有 uuid,则分配一个 + if not hasattr(res, "unilabos_uuid") or not res.unilabos_uuid: + res.unilabos_uuid = str(uuid.uuid4()) + + # 强制遍历所有 children + if hasattr(res, "children"): + for child in res.children: + self._recursive_assign_uuid(child) diff --git a/unilabos/resources/eit_synthesis_station/items.py b/unilabos/resources/eit_synthesis_station/items.py new file mode 100644 index 00000000..867a8a05 --- /dev/null +++ b/unilabos/resources/eit_synthesis_station/items.py @@ -0,0 +1,97 @@ +from unilabos.resources.itemized_carrier import Bottle +import uuid + +class EIT_Bottle(Bottle): + """ + EIT 专用瓶子类: + 拦截条码的序列化与反序列化逻辑,解决 pylabrobot 基类不支持字符串条码的 Bug。 + """ + + def serialize(self) -> dict: + """【输出同步】将对象转为 JSON 时,隐藏字符串条码防止基类报错""" + real_barcode = self.barcode + self.barcode = None # 临时隐藏 + data = super().serialize() + self.barcode = real_barcode # 恢复 + data["barcode"] = real_barcode # 手动存入字符串 + return data + + @classmethod + def deserialize(cls, data: dict, allow_marshal: bool = True): + """【输入重建】从 JSON 转为对象时,拦截字符串条码防止基类解析崩溃""" + # 1. 提取条码内容 + barcode_val = data.get("barcode") + + # 2. 如果条码是字符串,先将其设为 None, + # 这样 super().deserialize (pylabrobot) 就不会尝试调用 Barcode.deserialize(str) + if isinstance(barcode_val, str): + data["barcode"] = None + + # 3. 调用基类逻辑重建资源对象 + resource = super().deserialize(data, allow_marshal=allow_marshal) + + # 4. 重建完成后,将字符串条码重新赋值给实例属性 + if isinstance(barcode_val, str): + resource.barcode = barcode_val + + return resource + +def EIT_REAGENT_BOTTLE_2ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "502000353"}) + res = EIT_Bottle(name=name, diameter=7.5, height=45.0, max_volume=2000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_REAGENT_BOTTLE_8ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "220000005"}) + res = EIT_Bottle(name=name, diameter=15.0, height=60.0, max_volume=8000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_REAGENT_BOTTLE_40ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "220000092"}) + res = EIT_Bottle(name=name, diameter=22.0, height=85.0, max_volume=40000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_REAGENT_BOTTLE_125ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "220000008"}) + res = EIT_Bottle(name=name, diameter=34.0, height=120.0, max_volume=125000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_POWDER_BUCKET_30ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "201000816"}) + res = EIT_Bottle(name=name, diameter=23.0, height=60.0, max_volume=30000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_FLASH_FILTER_INNER_BOTTLE(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "220000320"}) + res = EIT_Bottle(name=name, diameter=8.0, height=55.0, max_volume=30000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_FLASH_FILTER_OUTER_BOTTLE(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "220000321"}) + res = EIT_Bottle(name=name, diameter=9.0, height=60.0, max_volume=40000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_REACTION_SEAL_CAP(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "211009427"}) + res = EIT_Bottle(name=name, diameter=45.0, height=12.0, max_volume=0.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_REACTION_TUBE_2ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "551000502"}) + res = EIT_Bottle(name=name, diameter=11.0, height=45.0, max_volume=2000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res + +def EIT_TEST_TUBE_MAGNET_2ML(name: str, **kwargs) -> EIT_Bottle: + kwargs.update({"model": "220000322"}) + res = EIT_Bottle(name=name, diameter=11.0, height=45.0, max_volume=2000.0, **kwargs) + res.unilabos_uuid = str(uuid.uuid4()) + return res diff --git a/unilabos/resources/eit_synthesis_station/warehouses.py b/unilabos/resources/eit_synthesis_station/warehouses.py new file mode 100644 index 00000000..5aff7e91 --- /dev/null +++ b/unilabos/resources/eit_synthesis_station/warehouses.py @@ -0,0 +1,244 @@ +from unilabos.resources.warehouse import WareHouse, warehouse_factory + +def eit_warehouse_W(name: str) -> WareHouse: + """创建eit W仓库 (左侧W区堆栈: W-1-1~W-4-8) + """ + return warehouse_factory( + name=name, + num_items_x=8, # 8列 + num_items_y=4, # 4行 + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, # 从01开始: A01, A02, A03, A04 + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_TB(name: str) -> WareHouse: + """创建eit TB仓库 (右侧TB区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=4, + num_items_y=2, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", + name_by_layout_code=True, + ) + +def eit_warehouse_N(name: str) -> WareHouse: + """创建eit N仓库 (右侧N区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=9, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", + name_by_layout_code=True, + ) + +def eit_warehouse_T(name: str) -> WareHouse: + """创建eit T仓库 (T区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=3, + num_items_y=2, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_MSB(name: str) -> WareHouse: + """创建eit MSB仓库""" + return warehouse_factory( + name=name, + num_items_x=2, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_SC(name: str) -> WareHouse: + """创建eit SC仓库 (SC区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=2, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_TS(name: str) -> WareHouse: + """创建eit TS仓库 (TS区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=2, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_MS(name: str) -> WareHouse: + """创建eit MS仓库 (MS区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=3, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_FF(name: str) -> WareHouse: + """创建eit FF仓库 (FF区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=2, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_AS(name: str) -> WareHouse: + """创建eit AS仓库 (AS区堆栈)""" + return warehouse_factory( + name=name, + num_items_x=2, + num_items_y=1, + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", # ⭐ 改为行优先排序 + name_by_layout_code=True, + ) + +def eit_warehouse_AGV(name: str) -> WareHouse: + """创建eit AGV车载仓库 (1x4 = 4 个车载槽位, agv_tray_1 ~ agv_tray_4)""" + return warehouse_factory( + name=name, + num_items_x=4, + num_items_y=1, # ← 车载只有 1 行 4 列 + num_items_z=1, + dx=10.0, + dy=10.0, + dz=10.0, + item_dx=140.0, + item_dy=98.0, + item_dz=120.0, + resource_size_x=127.8, + resource_size_y=85.5, + resource_size_z=40.0, + category="warehouse", + col_offset=0, + layout="row-major", + name_by_layout_code=True, + ) diff --git a/unilabos/resources/graphio.py b/unilabos/resources/graphio.py index 38f96968..87ef608d 100644 --- a/unilabos/resources/graphio.py +++ b/unilabos/resources/graphio.py @@ -617,8 +617,12 @@ def replace_plr_type_to_ulab(source: str): "tube": "tube", "bottle_carrier": "bottle_carrier", "plate_adapter": "plate_adapter", + "resource_holder": "resource_holder", "electrode_sheet": "electrode_sheet", "material_hole": "material_hole", + "material_plate": "material_plate", + "magazine_holder": "magazine_holder", + "resource_group": "resource_group", } if source in replace_info: return replace_info[source] diff --git a/unilabos/resources/itemized_carrier.py b/unilabos/resources/itemized_carrier.py index fe55c39e..c2109936 100644 --- a/unilabos/resources/itemized_carrier.py +++ b/unilabos/resources/itemized_carrier.py @@ -12,7 +12,7 @@ from pylabrobot.resources import Resource as ResourcePLR from pylabrobot.resources import Well, ResourceHolder from pylabrobot.resources.coordinate import Coordinate - +import uuid LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -111,6 +111,7 @@ def __init__( category: Optional[str] = "carrier", model: Optional[str] = None, invisible_slots: Optional[str] = None, + hide_label: bool = False, ): super().__init__( name=name, @@ -120,31 +121,56 @@ def __init__( category=category, model=model, ) + + # 1. 即使 sites 为空,也强制初始化这些属性,防止 serialize 报错 + self.child_locations: Dict[str, Coordinate] = {} + self.child_size: Dict[str, dict] = {} + self._ordering = {} + self._occupied_names: List[Optional[str]] = [] + + # 2. 自动补全载架自身的 UUID + if not getattr(self, "unilabos_uuid", None): + self.unilabos_uuid = str(uuid.uuid4()) + self.num_items = len(sites) self.num_items_x, self.num_items_y, self.num_items_z = num_items_x, num_items_y, num_items_z self.invisible_slots = [] if invisible_slots is None else invisible_slots + self.hide_label = hide_label self.layout = "z-y" if self.num_items_z > 1 and self.num_items_x == 1 else "x-z" if self.num_items_z > 1 and self.num_items_y == 1 else "x-y" if isinstance(sites, dict): sites = sites or {} - self.sites: List[Optional[ResourcePLR]] = list(sites.values()) + self.sites: List[Optional[ResourcePLR]] = [None] * len(sites) self._ordering = sites self.child_locations: Dict[str, Coordinate] = {} self.child_size: Dict[str, dict] = {} + self._occupied_names = [ + resource.name if resource is not None and not isinstance(resource, ResourceHolder) else None + for resource in sites.values() + ] + for spot, resource in sites.items(): if resource is not None and getattr(resource, "location", None) is None: raise ValueError(f"resource {resource} has no location") if resource is not None: + if not getattr(resource, "unilabos_uuid", None): + resource.unilabos_uuid = str(uuid.uuid4()) self.child_locations[spot] = resource.location self.child_size[spot] = {"width": resource._size_x, "height": resource._size_y, "depth": resource._size_z} else: self.child_locations[spot] = Coordinate.zero() self.child_size[spot] = {"width": 0, "height": 0, "depth": 0} + + for idx, (spot, resource) in enumerate(sites.items()): + if resource is not None: + self.assign_child_resource(resource, location=resource.location, spot=idx) + elif isinstance(sites, list): - # deserialize时走这里;还需要根据 self.sites 索引children + # 反序列化时保留 occupied_by,后续真正的 child 会再挂回对应槽位。 self.child_locations = {site["label"]: Coordinate(**site["position"]) for site in sites} self.child_size = {site["label"]: site["size"] for site in sites} - self.sites = [site["occupied_by"] for site in sites] + self.sites = [None] * len(sites) + self._occupied_names = [site.get("occupied_by") for site in sites] self._ordering = {site["label"]: site["position"] for site in sites} else: print("sites:", sites) @@ -158,6 +184,86 @@ def __len__(self) -> int: """Return the number of sites on this carrier.""" return len(self.sites) + def _extract_site_label(self, resource_name: Optional[str]) -> Optional[str]: + if resource_name is None: + return None + if resource_name in self.child_locations: + return resource_name + if "@" in resource_name: + candidate = resource_name.rsplit("@", 1)[-1].strip() + if candidate in self.child_locations: + return candidate + return None + + def _normalize_resource_name(self, resource_name: Optional[str]) -> Optional[str]: + if resource_name is None: + return None + return "".join(resource_name.split()) + + def _extract_resource_base_name(self, resource_name: Optional[str]) -> Optional[str]: + if resource_name is None: + return None + return self._normalize_resource_name(resource_name.split("@", 1)[0]) + + def _build_occupied_name(self, resource_name: Optional[str], idx: int) -> Optional[str]: + if resource_name is None: + return None + if self._extract_site_label(resource_name) is not None: + return resource_name + site_labels = list(self.child_locations.keys()) + if idx >= len(site_labels): + return resource_name + return f"{resource_name}@{site_labels[idx]}" + + def _resolve_site_index(self, resource: ResourcePLR, location: Optional[Coordinate]) -> Optional[int]: + resource_name = getattr(resource, "name", None) + resource_label = self._extract_site_label(resource_name) + normalized_resource_name = self._normalize_resource_name(resource_name) + resource_base_name = self._extract_resource_base_name(resource_name) + site_locations = list(self.child_locations.values()) + site_labels = list(self.child_locations.keys()) + + for i, occupied_name in enumerate(self._occupied_names): + if occupied_name == resource_name: + return i + if normalized_resource_name is not None and self._normalize_resource_name(occupied_name) == normalized_resource_name: + return i + occupied_label = self._extract_site_label(occupied_name) + if resource_label is not None and occupied_label == resource_label: + return i + + if resource_label is not None: + for i, site_label in enumerate(site_labels): + if site_label == resource_label: + return i + + if location is not None: + for i, site_location in enumerate(site_locations): + if ( + abs(site_location.x - location.x) < 1e-6 + and abs(site_location.y - location.y) < 1e-6 + and abs(site_location.z - location.z) < 1e-6 + ): + return i + + if resource_base_name is not None: + matching_indices = [ + i for i, occupied_name in enumerate(self._occupied_names) + if self._extract_resource_base_name(occupied_name) == resource_base_name + ] + if len(matching_indices) == 1: + return matching_indices[0] + for i in matching_indices: + if self.sites[i] is None or isinstance(self.sites[i], ResourceHolder): + return i + # setup() 已填充同名资源时,允许 reassign 覆盖已有的同 base_name 槽位 + for i in matching_indices: + existing = self.sites[i] + if existing is not None and self._extract_resource_base_name(getattr(existing, "name", None)) == resource_base_name: + return i + + return None + def assign_child_resource( self, resource: ResourcePLR, @@ -165,25 +271,26 @@ def assign_child_resource( reassign: bool = True, spot: Optional[int] = None, ): - idx = spot - # 如果只给 location,根据坐标和 deserialize 后的 self.sites(持有names)来寻找 resource 该摆放的位置 - if spot is not None: - idx = spot - else: - for i, site in enumerate(self.sites): - site_location = list(self.child_locations.values())[i] - if type(site) == str and site == resource.name: - idx = i - break - if site_location == location: - idx = i - break + idx = spot if spot is not None else self._resolve_site_index(resource, location) + if idx is None: + raise ValueError(f"Cannot resolve destination site for resource '{resource.name}' on carrier '{self.name}'") if not reassign and self.sites[idx] is not None: raise ValueError(f"a site with index {idx} already exists") + + old_resource = self.sites[idx] + if old_resource is not None and old_resource is not resource: + try: + super().unassign_child_resource(old_resource) + except Exception: + pass + self.sites[idx] = None + location = list(self.child_locations.values())[idx] super().assign_child_resource(resource, location=location, reassign=reassign) self.sites[idx] = resource + if idx < len(self._occupied_names) and not isinstance(resource, ResourceHolder): + self._occupied_names[idx] = self._build_occupied_name(resource.name, idx) def assign_resource_to_site(self, resource: ResourcePLR, spot: int): if self.sites[spot] is not None and not isinstance(self.sites[spot], ResourceHolder): @@ -195,6 +302,8 @@ def unassign_child_resource(self, resource: ResourcePLR): for spot, res in enumerate(self.sites): if res == resource: self.sites[spot] = None + if spot < len(self._occupied_names) and not isinstance(resource, ResourceHolder): + self._occupied_names[spot] = None found = True break if not found: @@ -428,31 +537,43 @@ def __delitem__(self, idx: int): def get_resources(self) -> List[ResourcePLR]: """Get all resources assigned to this carrier.""" - return [resource for resource in self.sites.values() if resource is not None] + return [resource for resource in self.sites if resource is not None] def __eq__(self, other): return super().__eq__(other) and self.sites == other.sites def get_free_sites(self) -> List[int]: - return [spot for spot, resource in self.sites.items() if resource is None] + return [spot for spot, resource in enumerate(self.sites) if resource is None] def serialize(self): + serialized_sites = [] + for idx, (identifier, location) in enumerate(self.child_locations.items()): + site_resource = self.sites[idx] if idx < len(self.sites) else None + occupied_name = ( + self._occupied_names[idx] + if idx < len(self._occupied_names) and self._occupied_names[idx] is not None + else site_resource.name + if isinstance(site_resource, ResourcePLR) and not isinstance(site_resource, ResourceHolder) + else None + ) + serialized_sites.append({ + "label": str(identifier), + "hide_label": self.hide_label, + "visible": False if identifier in self.invisible_slots else True, + "occupied_by": occupied_name, + "position": {"x": location.x, "y": location.y, "z": location.z}, + "size": self.child_size[identifier], + "content_type": ["bottle", "container", "tube", "bottle_carrier", "tip_rack"] + }) + return { **super().serialize(), "num_items_x": self.num_items_x, "num_items_y": self.num_items_y, "num_items_z": self.num_items_z, "layout": self.layout, - "sites": [{ - "label": str(identifier), - "visible": False if identifier in self.invisible_slots else True, - "occupied_by": self[identifier].name - if isinstance(self[identifier], ResourcePLR) and not isinstance(self[identifier], ResourceHolder) else - self[identifier] if isinstance(self[identifier], str) else None, - "position": {"x": location.x, "y": location.y, "z": location.z}, - "size": self.child_size[identifier], - "content_type": ["bottle", "container", "tube", "bottle_carrier", "tip_rack"] - } for identifier, location in self.child_locations.items()] + "hide_label": self.hide_label, + "sites": serialized_sites } @@ -469,6 +590,7 @@ def __init__( category: str = "bottle_carrier", model: Optional[str] = None, invisible_slots: List[str] = None, + hide_label: bool = False, **kwargs, ): super().__init__( @@ -480,4 +602,15 @@ def __init__( category=category, model=model, invisible_slots=invisible_slots, + hide_label=hide_label, ) + + def serialize(self): + data = super().serialize() + children = data.get("children") + if isinstance(children, list): + data["children"] = [ + child for child in children + if child.get("category") != "resource_holder" + ] + return data diff --git a/unilabos/resources/resource_tracker.py b/unilabos/resources/resource_tracker.py index b34d10cc..b08ede14 100644 --- a/unilabos/resources/resource_tracker.py +++ b/unilabos/resources/resource_tracker.py @@ -413,11 +413,12 @@ def replace_plr_type(source: str): "tip_spot": "tip_spot", "tube": "tube", "bottle_carrier": "bottle_carrier", - "material_hole": "material_hole", "container": "container", - "material_plate": "material_plate", - "electrode_sheet": "electrode_sheet", + "resource_holder": "resource_holder", "warehouse": "warehouse", + "electrode_sheet": "electrode_sheet", + "material_hole": "material_hole", + "material_plate": "material_plate", "magazine_holder": "magazine_holder", "resource_group": "resource_group", "trash": "trash", @@ -445,6 +446,11 @@ def build_uuid_mapping(res: "PLRResource", uuid_list: list, parent_uuid: Optiona uuid_list.append((uid, parent_uuid, extra)) for child in res.children: + if ( + getattr(res, "category", None) == "bottle_carrier" + and getattr(child, "category", None) == "resource_holder" + ): + continue build_uuid_mapping(child, uuid_list, uid) def resource_plr_inner( @@ -515,7 +521,8 @@ def resource_plr_inner( for resource in resources: # 构建uuid列表 uuid_list = [] - build_uuid_mapping(resource, uuid_list, getattr(resource.parent, "unilabos_uuid", None)) + parent_uuid = getattr(resource.parent, "unilabos_uuid", None) if resource.parent else None + build_uuid_mapping(resource, uuid_list, parent_uuid) serialized_data = resource.serialize() all_states = resource.serialize_all_state() @@ -544,8 +551,10 @@ def to_plr_resources(self, skip_devices=True) -> List["PLRResource"]: "deck": "Deck", "container": "RegularContainer", "tip_spot": "TipSpot", + "resource_holder": "ResourceHolder", + "bottle_carrier": "BottleCarrier", + "warehouse": "WareHouse", } - def collect_node_data(node: ResourceDictInstance, name_to_uuid: dict, all_states: dict, name_to_extra: dict): """一次遍历收集 name_to_uuid, all_states 和 name_to_extra""" name_to_uuid[node.res_content.name] = node.res_content.uuid @@ -674,6 +683,7 @@ def from_raw_dict_list(cls, raw_list: List[Dict[str, Any]]) -> "ResourceTreeSet" # 设置 parent 引用并建立 children 关系 if parent_instance: instance.res_content.parent = parent_instance.res_content + instance.res_content.parent_uuid = parent_instance.res_content.uuid # 将当前节点添加到父节点的 children 列表(避免重复添加) if instance not in parent_instance.children: parent_instance.children.append(instance) @@ -826,6 +836,7 @@ def merge_remote_resources(self, remote_tree_set: "ResourceTreeSet") -> "Resourc if remote_material_name not in local_sub_children_map: # 引入整个子树 remote_material.res_content.parent = local_sub_device.res_content + remote_material.res_content.parent_uuid = local_sub_device.res_content.uuid local_sub_device.children.append(remote_material) added_count += 1 else: @@ -849,6 +860,7 @@ def merge_remote_resources(self, remote_tree_set: "ResourceTreeSet") -> "Resourc remote_sub_name = remote_sub.res_content.name if remote_sub_name not in local_material_children_map: remote_sub.res_content.parent = local_material.res_content + remote_sub.res_content.parent_uuid = local_material.res_content.uuid local_material.children.append(remote_sub) added_count += 1 else: @@ -1200,6 +1212,11 @@ def add_resource(self, resource): Args: resource: 资源对象(可以是dict或实例) """ + try: + from pylabrobot.resources import Resource as ResourcePLR + except Exception: + ResourcePLR = None # type: ignore + root_uuids = {} for r in self.resources: res_uuid = r.get("uuid") if isinstance(r, dict) else getattr(r, "unilabos_uuid", None) @@ -1215,8 +1232,21 @@ def add_resource(self, resource): res_uuid = getattr(resource, "unilabos_uuid", None) if res_uuid in root_uuids: old_res = root_uuids[res_uuid] - # self.remove_resource(old_res) - logger.warning(f"资源{resource}已存在,旧资源: {old_res}") + + def _is_plr(obj) -> bool: + return ResourcePLR is not None and isinstance(obj, ResourcePLR) + + # 优先保留 PLR 资源,避免 dict + PLR 重复导致冲突 + if _is_plr(old_res) and not _is_plr(resource): + logger.warning(f"资源{resource}已存在(保留PLR实例),跳过新增,旧资源: {old_res}") + return + if _is_plr(resource) and not _is_plr(old_res): + logger.warning(f"资源{resource}已存在(替换为PLR实例),旧资源: {old_res}") + self.remove_resource(old_res) + else: + logger.warning(f"资源{resource}已存在(替换旧资源),旧资源: {old_res}") + self.remove_resource(old_res) + self.resources.append(resource) # 递归收集uuid映射 self._collect_uuid_mapping(resource) diff --git a/unilabos/resources/warehouse.py b/unilabos/resources/warehouse.py index 929a4e4d..3518267d 100644 --- a/unilabos/resources/warehouse.py +++ b/unilabos/resources/warehouse.py @@ -1,7 +1,7 @@ from typing import Dict, Optional, List, Union from pylabrobot.resources import Coordinate from pylabrobot.resources.carrier import ResourceHolder, create_homogeneous_resources - +import uuid from unilabos.resources.itemized_carrier import ItemizedCarrier, ResourcePLR @@ -13,14 +13,14 @@ def warehouse_factory( num_items_x: int = 1, num_items_y: int = 4, num_items_z: int = 4, - dx: float = 137.0, - dy: float = 96.0, + dx: float = 350.0, + dy: float = 160.0, dz: float = 120.0, item_dx: float = 10.0, item_dy: float = 10.0, item_dz: float = 10.0, - resource_size_x: float = 127.0, - resource_size_y: float = 86.0, + resource_size_x: float = 340.0, + resource_size_y: float = 150.0, resource_size_z: float = 25.0, removed_positions: Optional[List[int]] = None, empty: bool = False, @@ -29,6 +29,7 @@ def warehouse_factory( col_offset: int = 0, # 列起始偏移量,用于生成A05-D08等命名 row_offset: int = 0, # 行起始偏移量,用于生成F01-J03等命名 layout: str = "col-major", # 新增:排序方式,"col-major"=列优先,"row-major"=行优先 + name_by_layout_code: bool = False, ): # 创建位置坐标 locations = [] @@ -63,6 +64,12 @@ def warehouse_factory( resource_size_z=resource_size_z, name_prefix=name, ) + + for site in _sites.values(): + if not hasattr(site, "unilabos_uuid") or not site.unilabos_uuid: + # 建议使用基于名字的确定性 UUID (uuid5),或者直接用 uuid4 + site.unilabos_uuid = str(uuid.uuid4()) + len_x, len_y = (num_items_x, num_items_y) if num_items_z == 1 else (num_items_y, num_items_z) if num_items_x == 1 else (num_items_x, num_items_z) # 根据 layout 参数生成不同的排序方式 @@ -75,6 +82,29 @@ def warehouse_factory( # 列优先顺序: A01,B01,C01,D01, A02,B02,C02,D02 keys = [f"{LETTERS[j + row_offset]}{i + 1 + col_offset:02d}" for i in range(len_x) for j in range(len_y)] + if name_by_layout_code: + def _layout_code_from_key(key: str) -> str: + if "-" in key: + return key + row_letter = key[:1] + col_str = key[1:] + try: + row = LETTERS.index(row_letter) + 1 + except ValueError: + row = 1 + try: + col = int(col_str) + except ValueError: + col = 1 + if num_items_y == 1: + return f"{name}-{col}" + return f"{name}-{row}-{col}" + + layout_keys = [_layout_code_from_key(key) for key in keys] + for key, site in zip(layout_keys, _sites.values()): + site.name = key + keys = layout_keys + sites = {i: site for i, site in zip(keys, _sites.values())} return WareHouse( @@ -91,6 +121,7 @@ def warehouse_factory( sites=sites, category=category, model=model, + name_by_layout_code=name_by_layout_code, ) @@ -110,6 +141,7 @@ def __init__( category: str = "warehouse", model: Optional[str] = None, ordering_layout: str = "col-major", + name_by_layout_code: bool = False, **kwargs ): super().__init__( @@ -131,20 +163,46 @@ def __init__( # 保存排序方式,供graphio.py的坐标映射使用 # 使用独立属性避免与父类的layout冲突 self.ordering_layout = ordering_layout + self.name_by_layout_code = name_by_layout_code + + def _layout_code_from_key(self, key: str) -> str: + if "-" in key: + return key + row_letter = key[:1] + col_str = key[1:] + try: + row = LETTERS.index(row_letter) + 1 + except ValueError: + row = 1 + try: + col = int(col_str) + except ValueError: + col = 1 + if self.num_items_y == 1: + return f"{self.name}-{col}" + return f"{self.name}-{row}-{col}" def serialize(self) -> dict: """序列化时保存 ordering_layout 属性""" data = super().serialize() data['ordering_layout'] = self.ordering_layout + if self.name_by_layout_code: + for site in data.get("sites", []): + site["label"] = self._layout_code_from_key(site.get("label", "")) return data def get_site_by_layer_position(self, row: int, col: int, layer: int) -> ResourceHolder: - if not (0 <= layer < 4 and 0 <= row < 4 and 0 <= col < 1): - raise ValueError("无效的位置: layer={}, row={}, col={}".format(layer, row, col)) - - site_index = layer * 4 + row * 1 + col - return self.sites[site_index] - + if not (0 <= layer < self.num_items_z and 0 <= row < self.num_items_y and 0 <= col < self.num_items_x): + raise ValueError(f"无效的位置: layer={layer}, row={row}, col={col}") + + row_str = LETTERS[row] + col_str = f"{col + 1:02d}" + target_key = f"{row_str}{col_str}" + if self.name_by_layout_code: + target_key = self._layout_code_from_key(target_key) + + return self.get_item(target_key) + def add_rack_to_position(self, row: int, col: int, layer: int, rack) -> None: site = self.get_site_by_layer_position(row, col, layer) site.assign_child_resource(rack) diff --git a/unilabos/ros/msgs/message_converter.py b/unilabos/ros/msgs/message_converter.py index b526d5f5..2956bad0 100644 --- a/unilabos/ros/msgs/message_converter.py +++ b/unilabos/ros/msgs/message_converter.py @@ -141,12 +141,16 @@ ), Resource: lambda x: Resource( id=x.get("id", ""), + uuid=x.get("uuid", ""), name=x.get("name", ""), sample_id=x.get("sample_id", "") or "", + description=x.get("description", ""), children=list(x.get("children", [])), parent=x.get("parent", "") or "", + parent_uuid=x.get("parent_uuid", ""), type=x.get("type", ""), - category=x.get("class", "") or x.get("type", ""), + category=x.get("category", "") or x.get("class", "") or x.get("type", ""), + klass=x.get("class", "") or x.get("klass", ""), pose=( Pose( position=Point( @@ -159,15 +163,11 @@ else Pose() ), config=json.dumps(x.get("config", {})), - data=json.dumps(obtain_data_with_uuid(x)), + data=json.dumps(x.get("data", {})), + extra=json.dumps(x.get("extra", {})), ), } -def obtain_data_with_uuid(x: dict): - data = x.get("data", {}) - data["unilabos_uuid"] = x.get("uuid", None) - return data - def json_or_yaml_loads(data: str) -> Any: try: return json.loads(data) @@ -194,15 +194,20 @@ def json_or_yaml_loads(data: str) -> Any: Point: lambda x: Point3D(x=x.x, y=x.y, z=x.z), Resource: lambda x: { "id": x.id, + "uuid": x.uuid if x.uuid else None, "name": x.name, "sample_id": x.sample_id if x.sample_id else None, + "description": x.description if x.description else "", "children": list(x.children), "parent": x.parent if x.parent else None, + "parent_uuid": x.parent_uuid if x.parent_uuid else None, "type": x.type, - "class": "", + "class": x.klass if x.klass else "", + "category": x.category if x.category else "", "position": {"x": x.pose.position.x, "y": x.pose.position.y, "z": x.pose.position.z}, "config": json_or_yaml_loads(x.config or "{}"), "data": json_or_yaml_loads(x.data or "{}"), + "extra": json_or_yaml_loads(x.extra or "{}"), }, } diff --git a/unilabos/ros/nodes/base_device_node.py b/unilabos/ros/nodes/base_device_node.py index 772e667b..64d66448 100644 --- a/unilabos/ros/nodes/base_device_node.py +++ b/unilabos/ros/nodes/base_device_node.py @@ -612,15 +612,27 @@ async def sleep(self, rel_time: float, callback_group=None): async def create_task(cls, func, trace_error=True, **kwargs) -> Task: return ROS2DeviceNode.run_async_func(func, trace_error, **kwargs) - async def update_resource(self, resources: List["ResourcePLR"]): + async def update_resource( + self, + resources: List["ResourcePLR"], + resource_tree_dump: Optional[List[List[Dict[str, Any]]]] = None, + ): r = SerialCommand.Request() - tree_set = ResourceTreeSet.from_plr_resources(resources) - for tree in tree_set.trees: - root_node = tree.root_node - if not root_node.res_content.uuid_parent: - logger.warning(f"更新无父节点物料{root_node},自动以当前设备作为根节点") - root_node.res_content.parent_uuid = self.uuid - r.command = json.dumps({"data": {"data": tree_set.dump()}, "action": "update"}) + if resource_tree_dump is None: + tree_set = ResourceTreeSet.from_plr_resources(resources) + for tree in tree_set.trees: + root_node = tree.root_node + if not root_node.res_content.uuid_parent: + logger.warning(f"更新无父节点物料{root_node},自动以当前设备作为根节点") + root_node.res_content.parent_uuid = self.uuid + tree_dump = tree_set.dump() + else: + tree_dump = resource_tree_dump + for tree_nodes in tree_dump: + for node in tree_nodes: + if not node.get("parent_uuid"): + node["parent_uuid"] = self.uuid + r.command = json.dumps({"data": {"data": tree_dump}, "action": "update"}) response: SerialCommand_Response = await self._resource_clients["c2s_update_resource_tree"].call_async(r) # type: ignore try: uuid_maps = json.loads(response.response) @@ -753,6 +765,84 @@ def transfer_to_new_resource( f"物料{plr_resource}请求挂载{tree.root_node.res_content.name}的父节点{parent_resource}[{parent_uuid}]失败!\n{traceback.format_exc()}" ) + def batch_transfer_resources( + self, + transfers: List[Dict[str, Any]], + ) -> List["ResourcePLR"]: + """批量转移 PLR 资源:先全部 unassign,再全部 assign,最后一次性回调和同步 + + Args: + transfers: 转移列表,每项包含: + - "resource": PLR 资源对象 + - "from_parent": 原父节点 (PLR ResourceHolder/WareHouse) + - "to_parent": 目标父节点 + - "to_site": 目标 slot 名称(可选,用于 ItemizedCarrier) + + Returns: + 成功转移的目标 parent 列表 + """ + from pylabrobot.resources.resource import Resource as ResourcePLR + + if not transfers: + return [] + + # 第一遍:校验所有物料和目标 parent 的合法性 + for t in transfers: + resource = t["resource"] + to_parent = t["to_parent"] + if resource is None: + raise ValueError("转移列表中存在 resource=None") + if to_parent is None: + raise ValueError(f"物料 {resource} 的目标 parent 为 None") + + # 第二遍:批量 unassign + old_parents = [] + for t in transfers: + resource = t["resource"] + old_parent = resource.parent + old_parents.append(old_parent) + if old_parent is not None: + self.lab_logger().debug(f"批量 unassign: {resource.name} 从 {old_parent.name}") + old_parent.unassign_child_resource(resource) + # 从顶级资源列表中移除(避免 figure_resource 重复引用) + resource_id = id(resource) + for i, r in enumerate(self.resource_tracker.resources): + if id(r) == resource_id: + self.resource_tracker.resources.pop(i) + break + + # 第三遍:批量 assign + parents = [] + for t, old_parent in zip(transfers, old_parents): + resource = t["resource"] + to_parent = t["to_parent"] + to_site = t.get("to_site") + additional_params = {} + if to_site is not None: + spec = inspect.signature(to_parent.assign_child_resource) + if "spot" in spec.parameters: + ordering_dict = getattr(to_parent, "_ordering", None) + if ordering_dict and to_site in ordering_dict: + additional_params["spot"] = list(ordering_dict.keys()).index(to_site) + else: + additional_params["spot"] = to_site + self.lab_logger().debug(f"批量 assign: {resource.name} → {to_parent.name} site={to_site}") + to_parent.assign_child_resource(resource, location=None, **additional_params) + parents.append(to_parent) + + # 一次性触发 driver 回调 + func = getattr(self.driver_instance, "resource_tree_batch_transfer", None) + if callable(func): + func(transfers, old_parents, parents) + else: + # 兜底:逐个调用 resource_tree_transfer + single_func = getattr(self.driver_instance, "resource_tree_transfer", None) + if callable(single_func): + for t, old_parent, new_parent in zip(transfers, old_parents, parents): + single_func(old_parent, t["resource"], new_parent) + + return parents + async def s2c_resource_tree(self, req: SerialCommand_Request, res: SerialCommand_Response): """ 处理资源树更新请求 diff --git a/unilabos/ros/nodes/presets/workstation.py b/unilabos/ros/nodes/presets/workstation.py index 902e2967..1387ff69 100644 --- a/unilabos/ros/nodes/presets/workstation.py +++ b/unilabos/ros/nodes/presets/workstation.py @@ -243,8 +243,17 @@ async def execute_protocol(goal_handle: ServerGoalHandle): ) # type: ignore raw_data = json.loads(response.response) tree_set = ResourceTreeSet.from_raw_dict_list(raw_data) - target = tree_set.dump() - protocol_kwargs[k] = target[0][0] if v == "unilabos_msgs/Resource" else target + + # 传递 ResourceDictInstance(保留树结构),不再调用 dump() 扁平化 + if v == "unilabos_msgs/Resource": + # 单个资源:取第一棵树的根节点 + root_instance = tree_set.trees[0].root_node if tree_set.trees else None + protocol_kwargs[k] = root_instance.get_plr_nested_dict() if root_instance else protocol_kwargs[k] + else: + # 多个资源:取每棵树的根节点 + protocol_kwargs[k] = [ + tree.root_node.get_plr_nested_dict() for tree in tree_set.trees + ] except Exception as ex: self.lab_logger().error(f"查询资源失败: {k}, 错误: {ex}\n{traceback.format_exc()}") raise diff --git a/unilabos/test/experiments/workshop.json b/unilabos/test/experiments/workshop.json index 60d731d0..51b8776d 100644 --- a/unilabos/test/experiments/workshop.json +++ b/unilabos/test/experiments/workshop.json @@ -577,17 +577,38 @@ { "id": "AGV", "name": "AGV", - "children": ["zhixing_agv", "zhixing_ur_arm"], + "children": ["zhixing_agv", "zhixing_ur_arm", "agv_platform"], "parent": null, "type": "device", - "class": "workstation", + "class": "agv_transport_station", "position": { "x": 698.1111111111111, "y": 478, "z": 0 }, "config": { - "protocol_type": ["AGVTransferProtocol"] + "protocol_type": ["AGVTransferProtocol", "BatchTransferProtocol"], + "device_roles": { + "navigator": "zhixing_agv", + "arm": "zhixing_ur_arm" + }, + "route_table": { + "AiChemEcoHiWo->Revvity": { + "nav_command": "{\"target\": \"LM14\"}", + "arm_pick": "{\"task_name\": \"camera/250111_biaozhi.urp\"}", + "arm_place": "{\"task_name\": \"camera/250111_put_board.urp\"}" + }, + "Revvity->HPLC": { + "nav_command": "{\"target\": \"LM13\"}", + "arm_pick": "{\"task_name\": \"camera/250111_lfp.urp\"}", + "arm_place": "{\"task_name\": \"camera/250111_hplc.urp\"}" + }, + "HPLC->Revvity": { + "nav_command": "{\"target\": \"LM13\"}", + "arm_pick": "{\"task_name\": \"camera/250111_hplc.urp\"}", + "arm_place": "{\"task_name\": \"camera/250111_lfp.urp\"}" + } + } }, "data": { } @@ -627,6 +648,27 @@ }, "data": { } + }, + { + "id": "agv_platform", + "name": "agv_platform", + "children": [], + "parent": "AGV", + "type": "warehouse", + "class": "", + "position": { + "x": 698.1111111111111, + "y": 478, + "z": 0 + }, + "config": { + "name": "agv_platform", + "num_items_x": 2, + "num_items_y": 1, + "num_items_z": 1 + }, + "data": { + } } ], "links": [ diff --git a/unilabos_msgs/CMakeLists.txt b/unilabos_msgs/CMakeLists.txt index 46028fd9..00dfca10 100644 --- a/unilabos_msgs/CMakeLists.txt +++ b/unilabos_msgs/CMakeLists.txt @@ -96,6 +96,7 @@ set(action_files "action/WorkStationRun.action" "action/AGVTransfer.action" + "action/BatchTransfer.action" "action/DispenStationSolnPrep.action" "action/DispenStationVialFeed.action" diff --git a/unilabos_msgs/action/BatchTransfer.action b/unilabos_msgs/action/BatchTransfer.action new file mode 100644 index 00000000..207b85b4 --- /dev/null +++ b/unilabos_msgs/action/BatchTransfer.action @@ -0,0 +1,11 @@ +# 批量物料转运 +Resource from_repo +Resource to_repo +Resource[] transfer_resources +string[] from_positions +string[] to_positions +--- +string return_info +bool success +--- +string status diff --git a/unilabos_msgs/msg/Resource.msg b/unilabos_msgs/msg/Resource.msg index 6d52a035..d2c1da17 100644 --- a/unilabos_msgs/msg/Resource.msg +++ b/unilabos_msgs/msg/Resource.msg @@ -1,11 +1,16 @@ string id +string uuid string name string sample_id +string description string[] children string parent +string parent_uuid string type string category +string klass geometry_msgs/Pose pose string config -string data \ No newline at end of file +string data +string extra