Skip to content

Commit ad35c13

Browse files
committed
chore(tools): move system tools
Signed-off-by: satoshi-ota <satoshi.ota928@gmail.com>
1 parent d4d1119 commit ad35c13

12 files changed

+465
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cmake_minimum_required(VERSION 3.14)
2+
project(rqt_diagnostic_graph_monitor)
3+
4+
find_package(autoware_cmake REQUIRED)
5+
autoware_package()
6+
ament_python_install_package(${PROJECT_NAME} PACKAGE_DIR python)
7+
install(FILES plugin.xml DESTINATION share/${PROJECT_NAME})
8+
install(PROGRAMS script/rqt_diagnostic_graph_monitor DESTINATION lib/${PROJECT_NAME})
9+
ament_auto_package(INSTALL_TO_SHARE script)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# System diagnostic monitor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0"?>
2+
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<package format="3">
4+
<name>rqt_diagnostic_graph_monitor</name>
5+
<version>0.1.0</version>
6+
<description>The rqt_diagnostic_graph_monitor package</description>
7+
<maintainer email="isamu.takagi@tier4.jp">Takagi, Isamu</maintainer>
8+
<license>Apache License 2.0</license>
9+
10+
<buildtool_depend>ament_cmake_auto</buildtool_depend>
11+
<buildtool_depend>autoware_cmake</buildtool_depend>
12+
13+
<exec_depend>python_qt_binding</exec_depend>
14+
<exec_depend>rqt_gui</exec_depend>
15+
<exec_depend>rqt_gui_py</exec_depend>
16+
17+
<test_depend>ament_lint_auto</test_depend>
18+
<test_depend>autoware_lint_common</test_depend>
19+
20+
<export>
21+
<build_type>ament_cmake</build_type>
22+
<rqt_gui plugin="${prefix}/plugin.xml"/>
23+
</export>
24+
</package>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<library path="python">
2+
<class name="DiagnosticGraphMonitor" type="rqt_diagnostic_graph_monitor.MonitorPlugin" base_class_type="rqt_gui_py::Plugin">
3+
<description>
4+
</description>
5+
<qtgui>
6+
<group>
7+
<label>Robot Tools</label>
8+
<icon type="theme">folder</icon>
9+
<statustip></statustip>
10+
</group>
11+
<label>Diagnostic Graph Monitor</label>
12+
<icon type="theme">utilities-system-monitor</icon>
13+
<statustip></statustip>
14+
</qtgui>
15+
</class>
16+
</library>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2023 The Autoware Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from rqt_gui_py.plugin import Plugin
16+
17+
from .module import MonitorModule
18+
from .widget import MonitorWidget
19+
20+
21+
class MonitorPlugin(Plugin):
22+
def __init__(self, context):
23+
super().__init__(context)
24+
self.widget = MonitorWidget()
25+
self.module = MonitorModule(context.node)
26+
self.module.append_struct_callback(self.widget.on_graph)
27+
context.add_widget(self.widget)
28+
29+
def shutdown_plugin(self):
30+
self.module.shutdown()
31+
self.widget.shutdown()
32+
33+
def save_settings(self, plugin_settings, instance_settings):
34+
pass
35+
36+
def restore_settings(self, plugin_settings, instance_settings):
37+
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Copyright 2023 The Autoware Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from diagnostic_msgs.msg import DiagnosticStatus
16+
from rclpy.time import Time
17+
18+
19+
class DummyStatus:
20+
def __init__(self):
21+
self.level = DiagnosticStatus.STALE
22+
23+
24+
class BaseUnit:
25+
def __init__(self, status=DummyStatus()):
26+
self._parents = []
27+
self._children = []
28+
self._path = None
29+
self._type = None
30+
self._status = status
31+
32+
@property
33+
def parents(self):
34+
return self._parents
35+
36+
@property
37+
def children(self):
38+
return self._children
39+
40+
@property
41+
def path(self):
42+
return self._path
43+
44+
@property
45+
def kind(self):
46+
return self._type
47+
48+
49+
class NodeUnit(BaseUnit):
50+
def __init__(self, struct):
51+
super().__init__()
52+
self._path = struct.path
53+
self._type = struct.type
54+
55+
def update(self, status):
56+
self._status = status
57+
58+
@property
59+
def level(self):
60+
return self._status.level
61+
62+
63+
class DiagUnit(BaseUnit):
64+
def __init__(self, struct):
65+
super().__init__()
66+
self._path = struct.path
67+
self._name = struct.name
68+
self._type = "diag"
69+
70+
def update(self, status):
71+
self._status = status
72+
73+
@property
74+
def level(self):
75+
return self._status.level
76+
77+
78+
class UnitLink:
79+
def __init__(self, parent: BaseUnit, child: BaseUnit):
80+
self._parent = parent
81+
self._child = child
82+
parent._children.append(self)
83+
child._parents.append(self)
84+
85+
def update(self, status):
86+
self.status = status
87+
88+
@property
89+
def parent(self):
90+
return self._parent
91+
92+
@property
93+
def child(self):
94+
return self._child
95+
96+
97+
class Graph:
98+
def __init__(self, msg):
99+
self._struct_stamp = Time.from_msg(msg.stamp)
100+
self._status_stamp = None
101+
self._id = msg.id
102+
self._nodes = [NodeUnit(struct) for struct in msg.nodes]
103+
self._diags = [DiagUnit(struct) for struct in msg.diags]
104+
self._units = self._nodes + self._diags
105+
self._links = []
106+
for struct in msg.links:
107+
units = self._diags if struct.is_leaf else self._nodes
108+
nodes = self._nodes
109+
self._links.append(UnitLink(nodes[struct.parent], units[struct.child]))
110+
111+
def update(self, msg):
112+
if msg.id == self._id:
113+
self._status_stamp = Time.from_msg(msg.stamp)
114+
for node, status in zip(self._nodes, msg.nodes):
115+
node.update(status)
116+
for diag, status in zip(self._diags, msg.diags):
117+
diag.update(status)
118+
for link, status in zip(self._links, msg.links):
119+
link.update(status)
120+
121+
@property
122+
def units(self):
123+
return self._units
124+
125+
@property
126+
def links(self):
127+
return self._links
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright 2024 The Autoware Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from diagnostic_msgs.msg import DiagnosticStatus
17+
from python_qt_binding import QtGui
18+
from python_qt_binding import QtWidgets
19+
20+
from .graph import BaseUnit
21+
from .graph import UnitLink
22+
23+
24+
class MonitorIcons:
25+
def __init__(self):
26+
self.disable = QtGui.QIcon.fromTheme("dialog-question")
27+
self.unknown = QtGui.QIcon.fromTheme("system-search")
28+
self.ok = QtGui.QIcon.fromTheme("emblem-default")
29+
self.warn = QtGui.QIcon.fromTheme("emblem-important")
30+
self.error = QtGui.QIcon.fromTheme("dialog-error")
31+
self.stale = QtGui.QIcon.fromTheme("appointment-missed")
32+
33+
self._levels = {}
34+
self._levels[DiagnosticStatus.OK] = self.ok
35+
self._levels[DiagnosticStatus.WARN] = self.warn
36+
self._levels[DiagnosticStatus.ERROR] = self.error
37+
self._levels[DiagnosticStatus.STALE] = self.stale
38+
39+
def get(self, level):
40+
return self._levels.get(level, self.unknown)
41+
42+
43+
class MonitorItem:
44+
icons = MonitorIcons()
45+
46+
def __init__(self, link: UnitLink, unit: BaseUnit):
47+
item_text = f"{unit.path} ({unit.kind})" if unit.path else f"({unit.kind})"
48+
self.item = QtWidgets.QTreeWidgetItem([item_text])
49+
self.link = link
50+
self.unit = unit
51+
self.item.setIcon(0, self.icons.stale)
52+
53+
def update(self):
54+
self.item.setIcon(0, self.icons.get(self.unit.level))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright 2023 The Autoware Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
from rclpy.node import Node
17+
from tier4_system_msgs.msg import DiagGraphStatus
18+
from tier4_system_msgs.msg import DiagGraphStruct
19+
20+
from .graph import Graph
21+
from .utils import default_qos
22+
from .utils import durable_qos
23+
from .utils import foreach
24+
25+
26+
class MonitorModule:
27+
def __init__(self, node: Node):
28+
self.graph = None
29+
self.struct_callbacks = []
30+
self.status_callbacks = []
31+
self.node = node
32+
self.sub_struct = self.subscribe_struct()
33+
self.sub_status = self.subscribe_status()
34+
35+
def append_struct_callback(self, callback):
36+
self.struct_callbacks.append(callback)
37+
38+
def append_status_callback(self, callback):
39+
self.status_callbacks.append(callback)
40+
41+
def on_struct(self, msg):
42+
self.graph = Graph(msg)
43+
foreach(self.struct_callbacks, lambda callback: callback(self.graph))
44+
45+
def on_status(self, msg):
46+
self.graph.update(msg)
47+
foreach(self.status_callbacks, lambda callback: callback(self.graph))
48+
49+
def subscribe_struct(self):
50+
return self.node.create_subscription(
51+
DiagGraphStruct, "/diagnostics_graph/struct", self.on_struct, durable_qos(1)
52+
)
53+
54+
def subscribe_status(self):
55+
return self.node.create_subscription(
56+
DiagGraphStatus, "/diagnostics_graph/status", self.on_status, default_qos(1)
57+
)
58+
59+
def shutdown(self):
60+
self.node.destroy_subscription(self.sub_struct)
61+
self.node.destroy_subscription(self.sub_status)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2024 The Autoware Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from rclpy.qos import QoSDurabilityPolicy
16+
from rclpy.qos import QoSProfile
17+
18+
19+
def default_qos(depth):
20+
return QoSProfile(depth=depth)
21+
22+
23+
def durable_qos(depth):
24+
return QoSProfile(depth=depth, durability=QoSDurabilityPolicy.TRANSIENT_LOCAL)
25+
26+
27+
def foreach(iterable, function):
28+
for item in iterable:
29+
function(item)

0 commit comments

Comments
 (0)