ci(deps): bump actions/deploy-pages from 4 to 5 (#251)

Bumps [actions/deploy-pages](https://github.com/actions/deploy-pages) from 4 to 5.
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/deploy-pages
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

ci(deps): bump actions/configure-pages from 5 to 6 (#252)

Bumps [actions/configure-pages](https://github.com/actions/configure-pages) from 5 to 6.
- [Release notes](https://github.com/actions/configure-pages/releases)
- [Commits](https://github.com/actions/configure-pages/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/configure-pages
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

ci(deps): bump actions/upload-pages-artifact from 4 to 5 (#260)

Bumps [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-pages-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

ci(deps): bump conda-incubator/setup-miniconda from 3 to 4 (#261)

Bumps [conda-incubator/setup-miniconda](https://github.com/conda-incubator/setup-miniconda) from 3 to 4.
- [Release notes](https://github.com/conda-incubator/setup-miniconda/releases)
- [Changelog](https://github.com/conda-incubator/setup-miniconda/blob/main/CHANGELOG.md)
- [Commits](https://github.com/conda-incubator/setup-miniconda/compare/v3...v4)

---
updated-dependencies:
- dependency-name: conda-incubator/setup-miniconda
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add PLC communication guide (#264)

* Add post process station and related resources

- Created JSON configuration for post_process_station and its child post_process_deck.
- Added YAML definitions for post_process_station, bottle carriers, bottles, and deck resources.
- Implemented Python classes for bottle carriers, bottles, decks, and warehouses to manage resources in the post process.
- Established a factory method for creating warehouses with customizable dimensions and layouts.
- Defined the structure and behavior of the post_process_deck and its associated warehouses.

* feat(post_process): add post_process_station and related warehouse functionality

- Introduced post_process_station.json to define the post-processing station structure.
- Implemented post_process_warehouse.py to create warehouse configurations with customizable layouts.
- Added warehouses.py for specific warehouse configurations (4x3x1).
- Updated post_process_station.yaml to reflect new module paths for OpcUaClient.
- Refactored bottle carriers and bottles YAML files to point to the new module paths.
- Adjusted deck.yaml to align with the new organizational structure for post_process_deck.

* Add PLC communication guide for AI4M

Add a comprehensive developer guide (docs/developer_guide/add_PLC.md) describing the PLC integration standard used by Uni-Lab for workstation devices, using the AI4M implementation as reference. Covers rationale for using OPC UA, the opcua_nodes_*.csv node-table format, communication base classes (BaseOpcUaClient / OpcUaClientWithSubscription), data types, and subscription/cache/reconnect behavior. Documents driver patterns for AI4MDevice, three handshake paradigms (pulse, parameter handshake, id-based), registry/graph configuration (YAML/JSON), debugging tips (KEPServerEX sim, standalone run), and a checklist for onboarding new PLC-controlled equipment.
This commit is contained in:
Roy
2026-05-23 23:35:54 +08:00
committed by Xuwznln
parent 35de4a5fee
commit 19ca6b5db8
20 changed files with 971 additions and 45 deletions

View File

@@ -45,6 +45,7 @@ from unilabos.resources.graphio import (
)
from unilabos.resources.plr_additional_res_reg import register
from unilabos.ros.msgs.message_converter import (
String,
convert_to_ros_msg,
convert_from_ros_msg_with_mapping,
convert_to_ros_msg_with_mapping,
@@ -250,7 +251,8 @@ class PropertyPublisher:
):
self.node = node
self.name = name
self.msg_type = msg_type
self.msg_type = self._normalize_msg_type(msg_type)
self.original_msg_type = msg_type
self.get_method = get_method
self.timer_period = initial_period
self.print_publish = print_publish
@@ -258,16 +260,36 @@ class PropertyPublisher:
self._value = None
try:
self.publisher_ = node.create_publisher(msg_type, f"{name}", qos)
self.publisher_ = node.create_publisher(self.msg_type, f"{name}", qos)
except Exception as e:
self.node.lab_logger().error(
f"StatusError, DeviceId: {self.node.device_id} 创建发布者 {name} 失败,可能由于注册表有误,类型: {msg_type},错误: {e}"
f"StatusError, DeviceId: {self.node.device_id} 创建发布者 {name} 失败,"
f"可能由于注册表有误,类型: {msg_type},错误: {e}"
)
self.msg_type = String
try:
self.publisher_ = node.create_publisher(self.msg_type, f"{name}", qos)
self.node.lab_logger().warning(
f"属性 {name} 的发布类型已降级为 String原始类型: {msg_type}"
)
except Exception:
self.publisher_ = None
self.timer = node.create_timer(self.timer_period, self.publish_property)
self.__loop = ROS2DeviceNode.get_asyncio_loop()
str_msg_type = str(msg_type)[8:-2]
str_msg_type = str(self.msg_type)[8:-2]
self.node.lab_logger().trace(f"发布属性: {name}, 类型: {str_msg_type}, 周期: {initial_period}秒, QoS: {qos}")
@staticmethod
def _normalize_msg_type(msg_type):
if msg_type in (dict, list, tuple, set) or msg_type in ("dict", "list", "tuple", "set"):
return String
return msg_type
def _normalize_value(self, value):
if self.msg_type is String and isinstance(value, (dict, list, tuple, set)):
return json.dumps(value, ensure_ascii=False, cls=TypeEncoder)
return value
def get_property(self):
if asyncio.iscoroutinefunction(self.get_method):
# 如果是异步函数,运行事件循环并等待结果
@@ -302,12 +324,16 @@ class PropertyPublisher:
pass
# self.node.lab_logger().trace(f"【.publish_property】发布 {self.msg_type}: {value}")
if value is not None:
if self.publisher_ is None:
return
value = self._normalize_value(value)
msg = convert_to_ros_msg(self.msg_type, value)
self.publisher_.publish(msg)
# self.node.lab_logger().trace(f"【.publish_property】属性 {self.name} 发布成功")
except Exception as e:
topic = getattr(self.publisher_, "topic", self.name)
self.node.lab_logger().error(
f"【.publish_property】发布属性 {self.publisher_.topic} 出错: {str(e)}\n{traceback.format_exc()}"
f"【.publish_property】发布属性 {topic} 出错: {str(e)}\n{traceback.format_exc()}"
)
def change_frequency(self, period):