Closes #3. Closes #12.

Closes #3. Closes #12.
* Update README and MQTTClient for installation instructions and code improvements

* feat: 支持local_config启动
add: 增加对crt path的说明,为传入config.py的相对路径
move: web component

* add: registry description

* feat: node_info_update srv
fix: OTDeck cant create

* close #12
feat: slave node registry

* feat: show machine name
fix: host node registry not uploaded

* feat: add hplc registry

* feat: add hplc registry

* fix: hplc status typo

* fix: devices/

* fix: device.class possible null

* fix: HPLC additions with online service

* fix: slave mode spin not working

* fix: slave mode spin not working

* feat: 多ProtocolNode 允许子设备ID相同
feat: 上报发现的ActionClient
feat: Host重启动,通过discover机制要求slaveNode重新注册,实现信息及时上报

---------

Co-authored-by: Harvey Que <Q-Query@outlook.com>
This commit is contained in:
Xuwznln
2025-05-01 14:58:36 +08:00
committed by GitHub
parent 74ae2a88ac
commit 01ac3415ae
15 changed files with 300 additions and 106 deletions

View File

@@ -92,19 +92,7 @@ def setup_web_pages(router: APIRouter) -> None:
# 获取已加载的设备
if lab_registry:
# 设备类型
for device_id, device_info in lab_registry.device_type_registry.items():
msg = {
"id": device_id,
"name": device_info.get("name", "未命名"),
"file_path": device_info.get("file_path", ""),
"class_json": json.dumps(
device_info.get("class", {}), indent=4, ensure_ascii=False, cls=TypeEncoder
),
}
mqtt_client.publish_registry(device_id, device_info)
devices.append(msg)
devices = json.loads(json.dumps(lab_registry.obtain_registry_device_info(), ensure_ascii=False, cls=TypeEncoder))
# 资源类型
for resource_id, resource_info in lab_registry.resource_type_registry.items():
resources.append(

View File

@@ -96,17 +96,19 @@
<tr>
<th>设备ID</th>
<th>命名空间</th>
<th>机器名称</th>
<th>状态</th>
</tr>
{% for device_id, device_info in host_node_info.devices.items() %}
<tr>
<td>{{ device_id }}</td>
<td>{{ device_info.namespace }}</td>
<td>{{ device_info.machine_name }}</td>
<td><span class="status-badge online">{{ "在线" if device_info.is_online else "离线" }}</span></td>
</tr>
{% else %}
<tr>
<td colspan="3" class="empty-state">没有发现已管理的设备</td>
<td colspan="4" class="empty-state">没有发现已管理的设备</td>
</tr>
{% endfor %}
</table>
@@ -218,6 +220,7 @@
<th>Device ID</th>
<th>节点名称</th>
<th>命名空间</th>
<th>机器名称</th>
<th>状态项</th>
<th>动作数</th>
</tr>
@@ -227,6 +230,7 @@
<td>{{ device_id }}</td>
<td>{{ device_info.node_name }}</td>
<td>{{ device_info.namespace }}</td>
<td>{{ device_info.machine_name|default("本地") }}</td>
<td>{{ ros_node_info.device_topics.get(device_id, {})|length }}</td>
<td>{{ ros_node_info.device_actions.get(device_id, {})|length }} <span class="toggle-indicator"></span></td>
</tr>
@@ -329,8 +333,13 @@
<tr id="device-info-{{ loop.index }}" class="detail-row" style="display: none;">
<td colspan="5">
<div class="content-full">
<pre>{{ device.class_json }}</pre>
{% if device.class %}
<pre>{{ device.class | tojson(indent=4) }}</pre>
{% else %}
<!-- 这里可以放占位内容,比如 -->
<pre>// No data</pre>
{% endif %}
{% if device.is_online %}
<div class="status-badge"><span class="online-status">在线</span></div>
{% endif %}
@@ -362,7 +371,12 @@
<button class="copy-btn" onclick="copyToClipboard(this.previousElementSibling.textContent, event)">复制</button>
<button class="debug-btn" onclick="toggleDebugInfo(this, event)">调试</button>
<div class="debug-info" style="display:none;">
<pre>{{ action_info|tojson(indent=2) }}</pre>
{% if action_info %}
<pre>{{ action_info | tojson(indent=4) }}</pre>
{% else %}
<!-- 这里可以放占位内容,比如 -->
<pre>// No data</pre>
{% endif %}
</div>
</div>

View File

@@ -30,20 +30,19 @@ def get_host_node_info() -> Dict[str, Any]:
return host_info
host_info["available"] = True
host_info["devices"] = {
device_id: {
edge_device_id: {
"namespace": namespace,
"is_online": f"{namespace}/{device_id}" in host_node._online_devices,
"key": f"{namespace}/{device_id}" if namespace.startswith("/") else f"/{namespace}/{device_id}",
"is_online": f"{namespace}/{edge_device_id}" in host_node._online_devices,
"key": f"{namespace}/{edge_device_id}" if namespace.startswith("/") else f"/{namespace}/{edge_device_id}",
"machine_name": host_node.device_machine_names.get(edge_device_id, "未知"),
}
for device_id, namespace in host_node.devices_names.items()
for edge_device_id, namespace in host_node.devices_names.items()
}
# 获取已订阅的主题
host_info["subscribed_topics"] = sorted(list(host_node._subscribed_topics))
# 获取动作客户端信息
for action_id, client in host_node._action_clients.items():
host_info["action_clients"] = {
action_id: get_action_info(client, full_name=action_id)
}
host_info["action_clients"] = {action_id: get_action_info(client, full_name=action_id)}
# 获取设备状态
host_info["device_status"] = host_node.device_status

View File

@@ -12,6 +12,7 @@ from unilabos.app.web.utils.action_utils import get_action_info
# 存储 ROS 节点信息的全局变量
ros_node_info = {"online_devices": {}, "device_topics": {}, "device_actions": {}}
def get_ros_node_info() -> Dict[str, Any]:
"""获取 ROS 节点信息,包括设备节点、发布的状态和动作
@@ -35,6 +36,13 @@ def update_ros_node_info() -> Dict[str, Any]:
try:
from unilabos.ros.nodes.base_device_node import registered_devices
from unilabos.ros.nodes.presets.host_node import HostNode
# 尝试获取主机节点实例
host_node = HostNode.get_instance(0)
device_machine_names = {}
if host_node:
device_machine_names = host_node.device_machine_names
for device_id, device_info in registered_devices.items():
# 设备基本信息
@@ -42,6 +50,7 @@ def update_ros_node_info() -> Dict[str, Any]:
"node_name": device_info["node_name"],
"namespace": device_info["namespace"],
"uuid": device_info["uuid"],
"machine_name": device_machine_names.get(device_id, "本地"),
}
# 设备话题(状态)信息
@@ -55,10 +64,7 @@ def update_ros_node_info() -> Dict[str, Any]:
}
# 设备动作信息
result["device_actions"][device_id] = {
k: get_action_info(v, k)
for k, v in device_info["actions"].items()
}
result["device_actions"][device_id] = {k: get_action_info(v, k) for k, v in device_info["actions"].items()}
# 更新全局变量
ros_node_info = result
except Exception as e: