mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 21:11:12 +00:00
feat(workstation): 支持多种输入类型的 station_resource 并优化物料系统初始化
- 新增 `_coerce_station_resource_input` 函数以支持 dict、list 和其他类型转换为 Deck - 添加对 Modbus 客户端方法的兼容性封装,确保 slave/unit 参数正确传递- 在初始化时根据 station_resource 动态创建或赋值 deck - 自动构建默认物料台面及三个料盘,并分配初始电极片资源 - 移除旧有的硬编码物料系统注释代码 - 更新资源导出逻辑以使用工作站实例中的 deck 属性
This commit is contained in:
@@ -1,11 +1,15 @@
|
|||||||
|
|
||||||
import csv
|
import csv
|
||||||
|
import inspect
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import types
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
from pylabrobot.resources import Resource as PLRResource
|
from functools import wraps
|
||||||
|
from pylabrobot.resources import Deck, Resource as PLRResource
|
||||||
from unilabos_msgs.msg import Resource
|
from unilabos_msgs.msg import Resource
|
||||||
from unilabos.device_comms.modbus_plc.client import ModbusTcpClient
|
from unilabos.device_comms.modbus_plc.client import ModbusTcpClient
|
||||||
from unilabos.devices.workstation.workstation_base import WorkstationBase
|
from unilabos.devices.workstation.workstation_base import WorkstationBase
|
||||||
@@ -15,12 +19,99 @@ from unilabos.devices.workstation.coin_cell_assembly.button_battery_station impo
|
|||||||
from unilabos.ros.nodes.base_device_node import ROS2DeviceNode, BaseROS2DeviceNode
|
from unilabos.ros.nodes.base_device_node import ROS2DeviceNode, BaseROS2DeviceNode
|
||||||
from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode
|
from unilabos.ros.nodes.presets.workstation import ROS2WorkstationNode
|
||||||
from unilabos.devices.workstation.coin_cell_assembly.button_battery_station import CoincellDeck
|
from unilabos.devices.workstation.coin_cell_assembly.button_battery_station import CoincellDeck
|
||||||
|
from unilabos.resources.graphio import convert_resources_to_type
|
||||||
|
from unilabos.utils.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_modbus_slave_kw_alias(modbus_client):
|
||||||
|
if modbus_client is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
method_names = [
|
||||||
|
"read_coils",
|
||||||
|
"write_coils",
|
||||||
|
"write_coil",
|
||||||
|
"read_discrete_inputs",
|
||||||
|
"read_holding_registers",
|
||||||
|
"write_register",
|
||||||
|
"write_registers",
|
||||||
|
]
|
||||||
|
|
||||||
|
def _wrap(func):
|
||||||
|
signature = inspect.signature(func)
|
||||||
|
has_var_kwargs = any(param.kind == param.VAR_KEYWORD for param in signature.parameters.values())
|
||||||
|
accepts_unit = has_var_kwargs or "unit" in signature.parameters
|
||||||
|
accepts_slave = has_var_kwargs or "slave" in signature.parameters
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def _wrapped(self, *args, **kwargs):
|
||||||
|
if "slave" in kwargs and not accepts_slave:
|
||||||
|
slave_value = kwargs.pop("slave")
|
||||||
|
if accepts_unit and "unit" not in kwargs:
|
||||||
|
kwargs["unit"] = slave_value
|
||||||
|
if "unit" in kwargs and not accepts_unit:
|
||||||
|
unit_value = kwargs.pop("unit")
|
||||||
|
if accepts_slave and "slave" not in kwargs:
|
||||||
|
kwargs["slave"] = unit_value
|
||||||
|
return func(self, *args, **kwargs)
|
||||||
|
|
||||||
|
_wrapped._has_slave_alias = True
|
||||||
|
return _wrapped
|
||||||
|
|
||||||
|
for name in method_names:
|
||||||
|
if not hasattr(modbus_client, name):
|
||||||
|
continue
|
||||||
|
bound_method = getattr(modbus_client, name)
|
||||||
|
func = getattr(bound_method, "__func__", None)
|
||||||
|
if func is None:
|
||||||
|
continue
|
||||||
|
if getattr(func, "_has_slave_alias", False):
|
||||||
|
continue
|
||||||
|
wrapped = _wrap(func)
|
||||||
|
setattr(modbus_client, name, types.MethodType(wrapped, modbus_client))
|
||||||
|
|
||||||
|
|
||||||
|
def _coerce_station_resource_input(station_resource: Any) -> Optional[Deck]:
|
||||||
|
if station_resource is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(station_resource, Deck):
|
||||||
|
return station_resource
|
||||||
|
|
||||||
|
if isinstance(station_resource, PLRResource):
|
||||||
|
return station_resource if isinstance(station_resource, Deck) else None
|
||||||
|
|
||||||
|
candidates = None
|
||||||
|
if isinstance(station_resource, dict):
|
||||||
|
if "nodes" in station_resource and isinstance(station_resource["nodes"], list):
|
||||||
|
candidates = station_resource["nodes"]
|
||||||
|
else:
|
||||||
|
candidates = [station_resource]
|
||||||
|
elif isinstance(station_resource, list):
|
||||||
|
candidates = station_resource
|
||||||
|
|
||||||
|
if candidates is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
converted = convert_resources_to_type(resources_list=candidates, resource_type=Deck)
|
||||||
|
if isinstance(converted, Deck):
|
||||||
|
return converted
|
||||||
|
if isinstance(converted, list):
|
||||||
|
for item in converted:
|
||||||
|
if isinstance(item, Deck):
|
||||||
|
return item
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning(f"station_resource 转换 Deck 失败: {exc}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
#构建物料系统
|
#构建物料系统
|
||||||
|
|
||||||
class CoinCellAssemblyWorkstation(WorkstationBase):
|
class CoinCellAssemblyWorkstation(WorkstationBase):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
station_resource: Optional[Any] = None,
|
||||||
deck: Deck=None,
|
deck: Deck=None,
|
||||||
address: str = "172.21.32.20",
|
address: str = "172.21.32.20",
|
||||||
port: str = "502",
|
port: str = "502",
|
||||||
@@ -28,6 +119,16 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
|
|||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
if station_resource is None and "station_resource" in kwargs:
|
||||||
|
station_resource = kwargs.pop("station_resource")
|
||||||
|
else:
|
||||||
|
kwargs.pop("station_resource", None)
|
||||||
|
|
||||||
|
normalized_station_resource = _coerce_station_resource_input(station_resource)
|
||||||
|
|
||||||
|
if deck is None and isinstance(normalized_station_resource, Deck):
|
||||||
|
deck = normalized_station_resource
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
#桌子
|
#桌子
|
||||||
deck=deck,
|
deck=deck,
|
||||||
@@ -35,10 +136,34 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
self.debug_mode = debug_mode
|
self.debug_mode = debug_mode
|
||||||
self.deck = deck
|
|
||||||
|
self.station_resource = normalized_station_resource if isinstance(normalized_station_resource, Deck) else None
|
||||||
|
|
||||||
|
if self.deck is None:
|
||||||
|
self.deck = CoincellDeck(size_x=1000, size_y=1000, size_z=900)
|
||||||
|
self.station_resource = self.deck
|
||||||
|
# 创建料盘1并添加极片
|
||||||
|
liaopan1 = MaterialPlate(name="liaopan1", size_x=120.8, size_y=120.5, size_z=10.0, fill=True)
|
||||||
|
self.deck.assign_child_resource(liaopan1, Coordinate(x=0, y=0, z=0))
|
||||||
|
for i in range(16):
|
||||||
|
jipian = ElectrodeSheet(name=f"jipian1_{i}", size_x=12, size_y=12, size_z=0.1)
|
||||||
|
liaopan1.children[i].assign_child_resource(jipian, location=None)
|
||||||
|
# 创建料盘2
|
||||||
|
liaopan2 = MaterialPlate(name="liaopan2", size_x=120.8, size_y=120.5, size_z=10.0, fill=True)
|
||||||
|
self.deck.assign_child_resource(liaopan2, Coordinate(x=500, y=0, z=0))
|
||||||
|
# 创建电池料盘
|
||||||
|
liaopan3 = MaterialPlate(name="电池料盘", size_x=120.8, size_y=160.5, size_z=10.0, fill=True)
|
||||||
|
self.deck.assign_child_resource(liaopan3, Coordinate(x=100, y=100, z=0))
|
||||||
|
else:
|
||||||
|
if self.station_resource is None:
|
||||||
|
self.station_resource = self.deck
|
||||||
|
elif self.deck is not self.station_resource:
|
||||||
|
self.deck = self.station_resource
|
||||||
|
|
||||||
""" 连接初始化 """
|
""" 连接初始化 """
|
||||||
modbus_client = TCPClient(addr=address, port=port)
|
modbus_client = TCPClient(addr=address, port=port)
|
||||||
print("modbus_client", modbus_client)
|
print("modbus_client", modbus_client)
|
||||||
|
_ensure_modbus_slave_kw_alias(modbus_client.client)
|
||||||
if not debug_mode:
|
if not debug_mode:
|
||||||
modbus_client.client.connect()
|
modbus_client.client.connect()
|
||||||
count = 100
|
count = 100
|
||||||
@@ -62,8 +187,6 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
|
|||||||
self.csv_export_file = None
|
self.csv_export_file = None
|
||||||
self.coin_num_N = 0 #已组装电池数量
|
self.coin_num_N = 0 #已组装电池数量
|
||||||
#创建一个物料台面,包含两个极片板
|
#创建一个物料台面,包含两个极片板
|
||||||
#self.deck = create_a_coin_cell_deck()
|
|
||||||
|
|
||||||
#self._ros_node.update_resource(self.deck)
|
#self._ros_node.update_resource(self.deck)
|
||||||
|
|
||||||
#ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{
|
#ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{
|
||||||
@@ -907,7 +1030,10 @@ class CoinCellAssemblyWorkstation(WorkstationBase):
|
|||||||
ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{
|
ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{
|
||||||
"resources": [self.station_resource]
|
"resources": [self.station_resource]
|
||||||
})
|
})
|
||||||
time.sleep(4)
|
# for i in range(40):
|
||||||
|
# print(f"fun_wuliao_test 运行结束{i}")
|
||||||
|
# time.sleep(1)
|
||||||
|
# time.sleep(40)
|
||||||
# 数据读取与输出
|
# 数据读取与输出
|
||||||
def func_read_data_and_output(self, file_path: str="D:\\coin_cell_data"):
|
def func_read_data_and_output(self, file_path: str="D:\\coin_cell_data"):
|
||||||
# 检查CSV导出是否正在运行,已运行则跳出,防止同时启动两个while循环
|
# 检查CSV导出是否正在运行,已运行则跳出,防止同时启动两个while循环
|
||||||
@@ -1145,8 +1271,11 @@ if __name__ == "__main__":
|
|||||||
#liaopan2.children[1].assign_child_resource(jipian1, location=None)
|
#liaopan2.children[1].assign_child_resource(jipian1, location=None)
|
||||||
##print(jipian2.parent)
|
##print(jipian2.parent)
|
||||||
|
|
||||||
liaopan1 = deck.get_resource("liaopan1")
|
# 使用 Coin_Cell 对象的 deck 属性
|
||||||
liaopan2 = deck.get_resource("liaopan2")
|
deck = Coin_Cell.deck
|
||||||
|
|
||||||
|
liaopan1 = Coin_Cell.deck.get_resource("liaopan1")
|
||||||
|
liaopan2 = Coin_Cell.deck.get_resource("liaopan2")
|
||||||
for i in range(16):
|
for i in range(16):
|
||||||
#找到liaopan1上每一个jipian
|
#找到liaopan1上每一个jipian
|
||||||
jipian_linshi = liaopan1.children[i].children[0]
|
jipian_linshi = liaopan1.children[i].children[0]
|
||||||
@@ -1172,7 +1301,7 @@ if __name__ == "__main__":
|
|||||||
BasicConfig.sk = "15d4f25e-3512-4f9c-9bfb-43ab85e7b561"
|
BasicConfig.sk = "15d4f25e-3512-4f9c-9bfb-43ab85e7b561"
|
||||||
from unilabos.app.web.client import http_client
|
from unilabos.app.web.client import http_client
|
||||||
|
|
||||||
resources = convert_resources_from_type([deck], [Resource])
|
resources = convert_resources_from_type([Coin_Cell.deck], [Resource])
|
||||||
json.dump({"nodes": resources, "links": []}, open("button_battery_station_resources_unilab.json", "w"), indent=2)
|
json.dump({"nodes": resources, "links": []}, open("button_battery_station_resources_unilab.json", "w"), indent=2)
|
||||||
|
|
||||||
#print(resources)
|
#print(resources)
|
||||||
@@ -1181,3 +1310,5 @@ if __name__ == "__main__":
|
|||||||
http_client.resource_add(resources)
|
http_client.resource_add(resources)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user