diff --git a/system/diagnostic_graph_aggregator/CMakeLists.txt b/system/diagnostic_graph_aggregator/CMakeLists.txt
index 574978ec5adce..905cc07d81da1 100644
--- a/system/diagnostic_graph_aggregator/CMakeLists.txt
+++ b/system/diagnostic_graph_aggregator/CMakeLists.txt
@@ -5,32 +5,27 @@ find_package(autoware_cmake REQUIRED)
autoware_package()
ament_auto_add_library(${PROJECT_NAME} SHARED
+ src/common/graph/error.cpp
+ src/common/graph/data.cpp
src/common/graph/config.cpp
- src/common/graph/debug.cpp
+ src/common/graph/loader.cpp
src/common/graph/graph.cpp
src/common/graph/units.cpp
)
ament_auto_add_executable(aggregator
src/node/aggregator.cpp
- src/node/plugin/modes.cpp
+ src/node/availability.cpp
)
target_include_directories(aggregator PRIVATE src/common)
-ament_auto_add_executable(converter
- src/node/converter.cpp
-)
-target_include_directories(converter PRIVATE src/common)
-
ament_auto_add_executable(tree
src/tool/tree.cpp
- src/tool/utils/loader.cpp
)
target_include_directories(tree PRIVATE src/common)
ament_auto_add_executable(plantuml
src/tool/plantuml.cpp
- src/tool/utils/loader.cpp
)
target_include_directories(plantuml PRIVATE src/common)
@@ -45,10 +40,4 @@ if(BUILD_TESTING)
target_include_directories(gtest_${PROJECT_NAME} PRIVATE src/common)
endif()
-install(PROGRAMS
- script/dump.py
- RENAME dump
- DESTINATION lib/${PROJECT_NAME}
-)
-
ament_auto_package(INSTALL_TO_SHARE config example launch)
diff --git a/system/diagnostic_graph_aggregator/README.md b/system/diagnostic_graph_aggregator/README.md
index cc49ed88ba98f..c0cd78e0610c2 100644
--- a/system/diagnostic_graph_aggregator/README.md
+++ b/system/diagnostic_graph_aggregator/README.md
@@ -3,20 +3,25 @@
## Overview
The diagnostic graph aggregator node subscribes to diagnostic array and publishes aggregated diagnostic graph.
-As shown in the diagram below, this node introduces extra diagnostic status for intermediate functional unit.
-Diagnostic status dependencies will be directed acyclic graph (DAG).
+As shown in the diagram below, this node introduces extra diagnostic status for intermediate functional units.

-## Diagnostics graph message
+## Diagnostic graph structures
-The diagnostics graph that this node outputs is a combination of diagnostic status and connections between them.
-This graph consists of an array of diagnostic nodes, and each node has a status and links.
-This link contains an index indicating the position of the node in the graph.
-Therefore, the graph can be reconstructed from the array of nodes using links.
-The following is an example of a message representing the graph in the overview section.
+The diagnostic graph is actually a set of fault tree analysis (FTA) for each operation mode of Autoware.
+Since the status of the same node may be referenced by multiple nodes, the overall structure is a directed acyclic graph (DAG).
+Each node in the diagnostic graph represents the diagnostic status of a specific functional unit, including the input diagnostics.
+So we define this as "unit", and call the unit corresponding to the input diagnosis "diag unit" and the others "node unit".
-
+Every unit has an error level that is the same as DiagnosticStatus, a unit type, and optionally a unit path.
+In addition, every diag unit has a message, a hardware_id, and values that are the same as DiagnosticStatus.
+The unit type represents how the unit status is calculated, such as AND or OR.
+The unit path is any unique string that represents the functionality of the unit.
+
+NOTE: This feature is currently under development.
+The diagnostic graph also supports "link" because there are cases where connections between units have additional status.
+For example, it is natural that many functional units will have an error status until initialization is complete.
## Operation mode availability
@@ -34,11 +39,13 @@ This feature breaks the generality of the graph and may be changed to a plugin o
## Interfaces
-| Interface Type | Interface Name | Data Type | Description |
-| -------------- | ------------------------------------- | ------------------------------------------------- | ------------------ |
-| subscription | `/diagnostics` | `diagnostic_msgs/msg/DiagnosticArray` | Diagnostics input. |
-| publisher | `/diagnostics_graph` | `tier4_system_msgs/msg/DiagnosticGraph` | Diagnostics graph. |
-| publisher | `/system/operation_mode/availability` | `tier4_system_msgs/msg/OperationModeAvailability` | mode availability. |
+| Interface Type | Interface Name | Data Type | Description |
+| -------------- | ------------------------------------- | ------------------------------------------------- | ---------------------------------- |
+| subscription | `/diagnostics` | `diagnostic_msgs/msg/DiagnosticArray` | Diagnostics input. |
+| publisher | `/diagnostics_graph/unknowns` | `diagnostic_msgs/msg/DiagnosticArray` | Diagnostics not included in graph. |
+| publisher | `/diagnostics_graph/struct` | `tier4_system_msgs/msg/DiagGraphStruct` | Diagnostic graph (static part). |
+| publisher | `/diagnostics_graph/status` | `tier4_system_msgs/msg/DiagGraphStatus` | Diagnostic graph (dynamic part). |
+| publisher | `/system/operation_mode/availability` | `tier4_system_msgs/msg/OperationModeAvailability` | Operation mode availability. |
## Parameters
@@ -49,7 +56,6 @@ This feature breaks the generality of the graph and may be changed to a plugin o
| `input_qos_depth` | `uint` | QoS depth of input array topic. |
| `graph_qos_depth` | `uint` | QoS depth of output graph topic. |
| `use_operation_mode_availability` | `bool` | Use operation mode availability publisher. |
-| `use_debug_mode` | `bool` | Use debug output to stdout. |
## Examples
@@ -73,13 +79,12 @@ ros2 launch diagnostic_graph_aggregator example-edit.launch.xml
## Debug tools
-- [dump](./doc/tool/dump.md)
-- [converter](./doc/tool/converter.md)
- [tree](./doc/tool/tree.md)
+- [diagnostic_graph_utils](../diagnostic_graph_utils/README.md)
## Graph file format
- [graph](./doc/format/graph.md)
- [path](./doc/format/path.md)
-- [node](./doc/format/node.md)
+- [unit](./doc/format/unit.md)
- [edit](./doc/format/edit.md)
diff --git a/system/diagnostic_graph_aggregator/config/default.param.yaml b/system/diagnostic_graph_aggregator/config/default.param.yaml
index 2660396bd86df..4fb2303bbd898 100644
--- a/system/diagnostic_graph_aggregator/config/default.param.yaml
+++ b/system/diagnostic_graph_aggregator/config/default.param.yaml
@@ -1,7 +1,6 @@
/**:
ros__parameters:
use_operation_mode_availability: true
- use_debug_mode: false
rate: 10.0
input_qos_depth: 1000
graph_qos_depth: 1
diff --git a/system/diagnostic_graph_aggregator/doc/format/edit/remove.md b/system/diagnostic_graph_aggregator/doc/format/edit/remove.md
index 6cd4b25e98828..9a9847e5c6dd3 100644
--- a/system/diagnostic_graph_aggregator/doc/format/edit/remove.md
+++ b/system/diagnostic_graph_aggregator/doc/format/edit/remove.md
@@ -1,10 +1,10 @@
# Remove
-The `remove` object is a edit that removes other nodes.
+The `remove` object is a edit that removes other units.
## Format
| Name | Type | Required | Description |
| ------ | -------- | -------- | ---------------------------------------- |
| `type` | `string` | yes | Specify `remove` when using this object. |
-| `path` | `string` | yes | The path of the node to remove. |
+| `path` | `string` | yes | The path of the unit to remove. |
diff --git a/system/diagnostic_graph_aggregator/doc/format/graph.md b/system/diagnostic_graph_aggregator/doc/format/graph.md
index 9dace8a2f0e6d..efe85c6e6aaff 100644
--- a/system/diagnostic_graph_aggregator/doc/format/graph.md
+++ b/system/diagnostic_graph_aggregator/doc/format/graph.md
@@ -7,5 +7,5 @@ The graph object is the top level structure that makes up the configuration file
| Name | Type | Required | Description |
| ------- | -------------------------------------- | -------- | ------------------------------------------------- |
| `files` | list\[[path](./path.md)\]
| no | List of path objects for importing subgraphs. |
-| `nodes` | list\[[node](./node.md)\]
| no | List of node objects that make up the graph. |
+| `units` | list\[[unit](./unit.md)\]
| no | List of unit objects that make up the graph. |
| `edits` | list\[[edit](./edit.md)\]
| no | List of edit objects to partially edit the graph. |
diff --git a/system/diagnostic_graph_aggregator/doc/format/node/remap.md b/system/diagnostic_graph_aggregator/doc/format/node/remap.md
deleted file mode 100644
index abf0d11fae12d..0000000000000
--- a/system/diagnostic_graph_aggregator/doc/format/node/remap.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Constant
-
-!!! warning
-
- This object is under development. It may be removed in the future.
-
-The remapping object is a node that converts error levels.
-
-## Format
-
-| Name | Type | Required | Description |
-| ------ | -------------------------------------- | -------- | ---------------------------------------------------- |
-| `type` | `string` | yes | Specify remapping type when using this object. |
-| `list` | list\[[node](../node.md)]
| yes | List of input node objects. The list size must be 1. |
-
-## Remapping types
-
-The supported remapping types are as follows.
-
-- `warn-to-ok`
-- `warn-to-error`
diff --git a/system/diagnostic_graph_aggregator/doc/format/node.md b/system/diagnostic_graph_aggregator/doc/format/unit.md
similarity index 59%
rename from system/diagnostic_graph_aggregator/doc/format/node.md
rename to system/diagnostic_graph_aggregator/doc/format/unit.md
index 0f4b52b9b8a72..6df4671259f9d 100644
--- a/system/diagnostic_graph_aggregator/doc/format/node.md
+++ b/system/diagnostic_graph_aggregator/doc/format/unit.md
@@ -1,24 +1,24 @@
-# Node
+# Unit
-The `node` is a base object that makes up the diagnostic graph.
-Any derived object can be used where a node object is required.
+The `unit` is a base object that makes up the diagnostic graph.
+Any derived object can be used where a unit object is required.
## Format
| Name | Type | Required | Description |
| ------ | -------- | -------- | ------------------------------------------------- |
| `type` | `string` | yes | The string indicating the type of derived object. |
-| `path` | `string` | no | Any string to reference from other nodes. |
+| `path` | `string` | no | Any string to reference from other units. |
## Derived objects
-- [diag](./node/diag.md)
-- [and](./node/and.md)
-- [or](./node/or.md)
-- [remapping](./node/remap.md)
+- [diag](./unit/diag.md)
+- [and](./unit/and.md)
+- [or](./unit/or.md)
+- [remapping](./unit/remap.md)
- warn-to-ok
- warn-to-error
-- [constant](./node/const.md)
+- [constant](./unit/const.md)
- ok
- warn
- error
diff --git a/system/diagnostic_graph_aggregator/doc/format/node/and.md b/system/diagnostic_graph_aggregator/doc/format/unit/and.md
similarity index 71%
rename from system/diagnostic_graph_aggregator/doc/format/node/and.md
rename to system/diagnostic_graph_aggregator/doc/format/unit/and.md
index 562018bf0995b..ea14d5101a53c 100644
--- a/system/diagnostic_graph_aggregator/doc/format/node/and.md
+++ b/system/diagnostic_graph_aggregator/doc/format/unit/and.md
@@ -1,6 +1,6 @@
# And
-The `and` object is a node that is evaluated as the maximum error level of the input nodes.
+The `and` object is a unit that is evaluated as the maximum error level of the input units.
Note that error level `stale` is treated as `error`.
## Format
@@ -8,7 +8,7 @@ Note that error level `stale` is treated as `error`.
| Name | Type | Required | Description |
| ------ | -------------------------------------- | -------- | ------------------------------------------------------------ |
| `type` | string
| yes | Specify `and` or `short-circuit-and` when using this object. |
-| `list` | list\[[node](../node.md)]
| yes | List of input node objects. |
+| `list` | list\[[unit](../unit.md)]
| yes | List of input unit objects. |
## Short-circuit evaluation
diff --git a/system/diagnostic_graph_aggregator/doc/format/node/const.md b/system/diagnostic_graph_aggregator/doc/format/unit/const.md
similarity index 86%
rename from system/diagnostic_graph_aggregator/doc/format/node/const.md
rename to system/diagnostic_graph_aggregator/doc/format/unit/const.md
index 13495be6cdbda..dac095814a616 100644
--- a/system/diagnostic_graph_aggregator/doc/format/node/const.md
+++ b/system/diagnostic_graph_aggregator/doc/format/unit/const.md
@@ -1,6 +1,6 @@
# Constant
-The constant object is a node with a fixed error level.
+The constant object is a unit with a fixed error level.
## Format
diff --git a/system/diagnostic_graph_aggregator/doc/format/node/diag.md b/system/diagnostic_graph_aggregator/doc/format/unit/diag.md
similarity index 84%
rename from system/diagnostic_graph_aggregator/doc/format/node/diag.md
rename to system/diagnostic_graph_aggregator/doc/format/unit/diag.md
index beba8ed0df5b4..377284322d9a9 100644
--- a/system/diagnostic_graph_aggregator/doc/format/node/diag.md
+++ b/system/diagnostic_graph_aggregator/doc/format/unit/diag.md
@@ -1,6 +1,6 @@
# Diag
-The `diag` object is a node that refers to a specific status within the source diagnostics.
+The `diag` object is a unit that refers to a specific status within the source diagnostics.
## Format
diff --git a/system/diagnostic_graph_aggregator/doc/format/node/link.md b/system/diagnostic_graph_aggregator/doc/format/unit/link.md
similarity index 67%
rename from system/diagnostic_graph_aggregator/doc/format/node/link.md
rename to system/diagnostic_graph_aggregator/doc/format/unit/link.md
index c23aa92575e54..3649bc49df112 100644
--- a/system/diagnostic_graph_aggregator/doc/format/node/link.md
+++ b/system/diagnostic_graph_aggregator/doc/format/unit/link.md
@@ -1,10 +1,10 @@
# Link
-The `link` object is a node that refers to other nodes.
+The `link` object is a unit that refers to another unit.
## Format
| Name | Type | Required | Description |
| ------ | -------- | -------- | -------------------------------------- |
| `type` | `string` | yes | Specify `link` when using this object. |
-| `link` | `string` | yes | The path of the node to reference. |
+| `link` | `string` | yes | The path of the unit to reference. |
diff --git a/system/diagnostic_graph_aggregator/doc/format/node/or.md b/system/diagnostic_graph_aggregator/doc/format/unit/or.md
similarity index 66%
rename from system/diagnostic_graph_aggregator/doc/format/node/or.md
rename to system/diagnostic_graph_aggregator/doc/format/unit/or.md
index 74e94ffd628e3..66d0d5fa75125 100644
--- a/system/diagnostic_graph_aggregator/doc/format/node/or.md
+++ b/system/diagnostic_graph_aggregator/doc/format/unit/or.md
@@ -1,6 +1,6 @@
# Or
-The `or` object is a node that is evaluated as the minimum error level of the input nodes.
+The `or` object is a unit that is evaluated as the minimum error level of the input units.
Note that error level `stale` is treated as `error`.
## Format
@@ -8,4 +8,4 @@ Note that error level `stale` is treated as `error`.
| Name | Type | Required | Description |
| ------ | -------------------------------------- | -------- | ------------------------------------ |
| `type` | string
| yes | Specify `or` when using this object. |
-| `list` | list\[[node](../node.md)]
| yes | List of input node objects. |
+| `list` | list\[[unit](../unit.md)]
| yes | List of input unit objects. |
diff --git a/system/diagnostic_graph_aggregator/doc/format/unit/remap.md b/system/diagnostic_graph_aggregator/doc/format/unit/remap.md
new file mode 100644
index 0000000000000..35bbf9cb5b408
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/doc/format/unit/remap.md
@@ -0,0 +1,21 @@
+# Constant
+
+!!! warning
+
+ This object is under development. It may be removed in the future.
+
+The remapping object is a unit that converts error levels.
+
+## Format
+
+| Name | Type | Required | Description |
+| ------ | ------------------------------- | -------- | ---------------------------------------------- |
+| `type` | `string` | yes | Specify remapping type when using this object. |
+| `item` | [unit](../unit.md)
| yes | Input unit object. |
+
+## Remapping types
+
+The supported remapping types are as follows.
+
+- `warn-to-ok`
+- `warn-to-error`
diff --git a/system/diagnostic_graph_aggregator/doc/message.drawio.svg b/system/diagnostic_graph_aggregator/doc/message.drawio.svg
deleted file mode 100644
index aef836dccf235..0000000000000
--- a/system/diagnostic_graph_aggregator/doc/message.drawio.svg
+++ /dev/null
@@ -1,677 +0,0 @@
-
diff --git a/system/diagnostic_graph_aggregator/doc/tool/converter.md b/system/diagnostic_graph_aggregator/doc/tool/converter.md
deleted file mode 100644
index 2351bc1e01054..0000000000000
--- a/system/diagnostic_graph_aggregator/doc/tool/converter.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Converter tool
-
-This tool converts `/diagnostics_graph` to `/diagnostics_agg` so it can be read by tools such as `rqt_runtime_monitor` and `rqt_robot_monitor`.
-
-## Usage
-
-```bash
-ros2 launch diagnostic_graph_aggregator converter.launch.xml complement:=false
-```
-
-The `complement` argument specifies whether to add an intermediate path that does not exist.
-This means that if the graph contains paths `/A/B` and `/A/B/C/D/E`, the intermediate paths `/A`, `/A/B/C` and `/A/B/C/D` will be added.
-This is useful for tree view in `rqt_robot_monitor`. The completed node has an error level of `STALE`.
-
-## Examples
-
-```bash
-ros2 launch diagnostic_graph_aggregator example-main.launch.xml complement:=false
-ros2 run rqt_runtime_monitor rqt_runtime_monitor --ros-args -r diagnostics:=diagnostics_agg
-```
-
-
-
-```bash
-ros2 launch diagnostic_graph_aggregator example-main.launch.xml complement:=true
-ros2 run rqt_robot_monitor rqt_robot_monitor
-```
-
-
diff --git a/system/diagnostic_graph_aggregator/doc/tool/dump.md b/system/diagnostic_graph_aggregator/doc/tool/dump.md
deleted file mode 100644
index 33f05ff866603..0000000000000
--- a/system/diagnostic_graph_aggregator/doc/tool/dump.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Dump tool
-
-This tool displays `/diagnostics_graph` in table format.
-
-## Usage
-
-```bash
-ros2 run diagnostic_graph_aggregator dump
-```
-
-## Examples
-
-```bash
-ros2 launch diagnostic_graph_aggregator example-main.launch.xml
-ros2 run diagnostic_graph_aggregator dump
-```
-
-```txt
-| ----- | ----- | -------------------------------- | ----- |
-| index | level | name | links |
-| ----- | ----- | -------------------------------- | ----- |
-| 0 | OK | /sensing/radars/front | |
-| 1 | OK | /sensing/lidars/front | |
-| 2 | ERROR | /sensing/lidars/top | |
-| 3 | OK | /functions/obstacle_detection | 1 0 |
-| 4 | ERROR | /functions/pose_estimation | 2 |
-| 5 | OK | /external/remote_command | |
-| 6 | OK | /external/joystick_command | |
-| 7 | ERROR | /autoware/modes/pull_over | 4 3 |
-| 8 | OK | /autoware/modes/comfortable_stop | 3 |
-| 9 | OK | /autoware/modes/emergency_stop | |
-| 10 | OK | /autoware/modes/remote | 5 |
-| 11 | OK | /autoware/modes/local | 6 |
-| 12 | ERROR | /autoware/modes/autonomous | 4 3 |
-| 13 | OK | /autoware/modes/stop | |
-```
diff --git a/system/diagnostic_graph_aggregator/doc/tool/images/rqt_robot_monitor.png b/system/diagnostic_graph_aggregator/doc/tool/images/rqt_robot_monitor.png
deleted file mode 100644
index e4aa169f02add..0000000000000
Binary files a/system/diagnostic_graph_aggregator/doc/tool/images/rqt_robot_monitor.png and /dev/null differ
diff --git a/system/diagnostic_graph_aggregator/doc/tool/tree.md b/system/diagnostic_graph_aggregator/doc/tool/tree.md
index 79fa61f527038..12b664ecc48d6 100644
--- a/system/diagnostic_graph_aggregator/doc/tool/tree.md
+++ b/system/diagnostic_graph_aggregator/doc/tool/tree.md
@@ -11,30 +11,30 @@ ros2 run diagnostic_graph_aggregator tree
## Examples
```bash
-ros2 run diagnostic_graph_aggregator tree system/diagnostic_graph_aggregator/example/graph/main.yaml
+ros2 run diagnostic_graph_aggregator tree '$(find-pkg-share diagnostic_graph_aggregator)/example/graph/main.yaml'
```
```txt
-===== root nodes =================================
+===== Top-level trees ============================
+- /autoware/modes/autonomous (and)
+ - /functions/pose_estimation (and)
+ - /functions/obstacle_detection (or)
- /autoware/modes/local (and)
- /external/joystick_command (diag)
+- /autoware/modes/remote (and)
+ - /external/remote_command (diag)
- /autoware/modes/comfortable_stop (and)
- /functions/obstacle_detection (or)
- /autoware/modes/pull_over (and)
- /functions/pose_estimation (and)
- /functions/obstacle_detection (or)
-- /autoware/modes/autonomous (and)
- - /functions/pose_estimation (and)
- - /functions/obstacle_detection (or)
-- /autoware/modes/remote (and)
- - /external/remote_command (diag)
-===== intermediate nodes =========================
+===== Subtrees ===================================
+- /functions/pose_estimation (and)
+ - /sensing/lidars/top (diag)
- /functions/obstacle_detection (or)
- /sensing/lidars/front (diag)
- /sensing/radars/front (diag)
-- /functions/pose_estimation (and)
- - /sensing/lidars/top (diag)
-===== isolated nodes =============================
-- /autoware/modes/stop (const)
-- /autoware/modes/emergency_stop (const)
+===== Isolated units =============================
+- /autoware/modes/stop (ok)
+- /autoware/modes/emergency_stop (ok)
```
diff --git a/system/diagnostic_graph_aggregator/example/dummy-diags.py b/system/diagnostic_graph_aggregator/example/dummy-diags.py
index 08f81bcd738ed..210462271a1ea 100755
--- a/system/diagnostic_graph_aggregator/example/dummy-diags.py
+++ b/system/diagnostic_graph_aggregator/example/dummy-diags.py
@@ -53,9 +53,9 @@ def create_status(name: str):
if __name__ == "__main__":
diags = [
- "lidar_driver/top: status",
- "lidar_driver/front: status",
- "radar_driver/front: status",
+ "lidar_driver_top: status",
+ "lidar_driver_front: status",
+ "radar_driver_front: status",
"external_command_checker: joystick_command",
"external_command_checker: remote_command",
]
diff --git a/system/diagnostic_graph_aggregator/example/example-edit.launch.xml b/system/diagnostic_graph_aggregator/example/example-edit.launch.xml
index c168b76f19c21..d56f76b1726cf 100644
--- a/system/diagnostic_graph_aggregator/example/example-edit.launch.xml
+++ b/system/diagnostic_graph_aggregator/example/example-edit.launch.xml
@@ -1,10 +1,6 @@
-
-
+
-
-
-
diff --git a/system/diagnostic_graph_aggregator/example/example-main.launch.xml b/system/diagnostic_graph_aggregator/example/example-main.launch.xml
index cdc1bc8afca53..b7019640e993c 100644
--- a/system/diagnostic_graph_aggregator/example/example-main.launch.xml
+++ b/system/diagnostic_graph_aggregator/example/example-main.launch.xml
@@ -1,10 +1,6 @@
-
-
-
-
diff --git a/system/diagnostic_graph_aggregator/example/graph/main.yaml b/system/diagnostic_graph_aggregator/example/graph/main.yaml
index 2bea907d9c119..b48c1ebe801ea 100644
--- a/system/diagnostic_graph_aggregator/example/graph/main.yaml
+++ b/system/diagnostic_graph_aggregator/example/graph/main.yaml
@@ -2,7 +2,7 @@ files:
- { path: $(dirname)/module1.yaml }
- { path: $(dirname)/module2.yaml }
-nodes:
+units:
- path: /autoware/modes/stop
type: ok
diff --git a/system/diagnostic_graph_aggregator/example/graph/module1.yaml b/system/diagnostic_graph_aggregator/example/graph/module1.yaml
index 07d4951b89446..1e1ae9b994587 100644
--- a/system/diagnostic_graph_aggregator/example/graph/module1.yaml
+++ b/system/diagnostic_graph_aggregator/example/graph/module1.yaml
@@ -1,4 +1,4 @@
-nodes:
+units:
- path: /functions/pose_estimation
type: and
list:
@@ -12,12 +12,15 @@ nodes:
- path: /sensing/lidars/top
type: diag
- diag: "lidar_driver/top: status"
+ node: lidar_driver_top
+ name: status
- path: /sensing/lidars/front
type: diag
- diag: "lidar_driver/front: status"
+ node: lidar_driver_front
+ name: status
- path: /sensing/radars/front
type: diag
- diag: "radar_driver/front: status"
+ node: radar_driver_front
+ name: status
diff --git a/system/diagnostic_graph_aggregator/example/graph/module2.yaml b/system/diagnostic_graph_aggregator/example/graph/module2.yaml
index a03701b661ba9..47e100087c650 100644
--- a/system/diagnostic_graph_aggregator/example/graph/module2.yaml
+++ b/system/diagnostic_graph_aggregator/example/graph/module2.yaml
@@ -1,8 +1,10 @@
-nodes:
+units:
- path: /external/joystick_command
type: diag
- diag: "external_command_checker: joystick_command"
+ node: external_command_checker
+ name: joystick_command
- path: /external/remote_command
type: diag
- diag: "external_command_checker: remote_command"
+ node: external_command_checker
+ name: remote_command
diff --git a/system/diagnostic_graph_aggregator/launch/converter.launch.xml b/system/diagnostic_graph_aggregator/launch/converter.launch.xml
deleted file mode 100644
index 9111338bf622a..0000000000000
--- a/system/diagnostic_graph_aggregator/launch/converter.launch.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/system/diagnostic_graph_aggregator/script/dump.py b/system/diagnostic_graph_aggregator/script/dump.py
deleted file mode 100755
index 9abcaeb7a080c..0000000000000
--- a/system/diagnostic_graph_aggregator/script/dump.py
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright 2024 The Autoware Contributors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import argparse
-
-from diagnostic_msgs.msg import DiagnosticStatus
-import rclpy
-import rclpy.node
-from tier4_system_msgs.msg import DiagnosticGraph
-
-
-def print_table(lines: list, header: list):
- widths = [0 for _ in range(len(header))]
- lines.insert(0, header)
- for line in lines:
- widths = map(max, widths, map(len, line))
- widths = list(widths)
- lines.insert(0, ["-" * w for w in widths])
- lines.insert(2, ["-" * w for w in widths])
- for line in lines:
- line = map(lambda v, w: f"{v:{w}}", line, widths)
- line = " | ".join(line)
- print(f"| {line} |")
-
-
-def get_level_text(level: int):
- if level == DiagnosticStatus.OK:
- return "OK"
- if level == DiagnosticStatus.WARN:
- return "WARN"
- if level == DiagnosticStatus.ERROR:
- return "ERROR"
- if level == DiagnosticStatus.STALE:
- return "STALE"
- return "-----"
-
-
-class NodeData:
- def __init__(self, index, node):
- self.index = index
- self._node = node
-
- @property
- def level(self):
- return get_level_text(self._node.status.level)
-
- @property
- def name(self):
- return self._node.status.name
-
- @property
- def links(self):
- return " ".join(str(link.index) for link in self._node.links)
-
- @property
- def line(self):
- return [str(self.index), self.level, self.name, self.links]
-
-
-class DumpNode(rclpy.node.Node):
- def __init__(self, args):
- super().__init__("dump_diagnostic_graph")
- self.sub = self.create_subscription(DiagnosticGraph, args.topic, self.callback, 1)
-
- def callback(self, msg):
- nodes = [NodeData(index, node) for index, node in enumerate(msg.nodes)]
- table = [node.line for node in nodes]
- print_table(table, ["index", "level", "name", "links"])
-
-
-if __name__ == "__main__":
- parser = argparse.ArgumentParser()
- parser.add_argument("--topic", default="/diagnostics_graph")
- args, unparsed = parser.parse_known_args()
-
- try:
- rclpy.init(args=unparsed)
- rclpy.spin(DumpNode(args))
- rclpy.shutdown()
- except KeyboardInterrupt:
- pass
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/config.cpp b/system/diagnostic_graph_aggregator/src/common/graph/config.cpp
index 39044d1c82bf9..9005084f84503 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/config.cpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/config.cpp
@@ -14,341 +14,297 @@
#include "config.hpp"
-#include "error.hpp"
+#include "names.hpp"
+#include "types.hpp"
#include
+#include
#include
-#include
+#include
#include
-#include
#include
#include
#include
-#include
-
-// DEBUG
-#include
namespace diagnostic_graph_aggregator
{
-template
-void extend(std::vector & u, const std::vector & v)
+std::string resolve_substitution(const std::string & substitution, const TreeData & data)
{
- u.insert(u.end(), v.begin(), v.end());
-}
+ std::stringstream ss(substitution);
+ std::string word;
+ std::vector words;
+ while (getline(ss, word, ' ')) {
+ words.push_back(word);
+ }
-template
-auto enumerate(const std::vector & v)
-{
- std::vector> result;
- for (size_t i = 0; i < v.size(); ++i) {
- result.push_back(std::make_pair(i, v[i]));
+ if (words.size() == 2 && words[0] == "find-pkg-share") {
+ return ament_index_cpp::get_package_share_directory(words[1]);
}
- return result;
+ if (words.size() == 1 && words[0] == "dirname") {
+ return std::filesystem::path(data.path().file()).parent_path();
+ }
+ throw UnknownSubstitution(data.path(), substitution);
}
-ConfigData::ConfigData(const std::string & path)
+std::string resolve_file_path(const std::string & path, const TreeData & data)
{
- file = path;
+ static const std::regex pattern(R"(\$\(([^()]*)\))");
+ std::smatch m;
+ std::string result = path;
+ while (std::regex_search(result, m, pattern)) {
+ const std::string prefix = m.prefix();
+ const std::string suffix = m.suffix();
+ result = prefix + resolve_substitution(m.str(1), data) + suffix;
+ }
+ return result;
}
-ConfigData ConfigData::load(YAML::Node yaml)
+FileLoader::FileLoader(const PathConfig * path)
{
- if (!yaml.IsMap()) {
- throw error("object is not a dict type", *this);
- }
- for (const auto & kv : yaml) {
- object[kv.first.as()] = kv.second;
+ if (!std::filesystem::exists(path->resolved)) {
+ throw FileNotFound(path->data.path(), path->resolved);
}
- return *this;
+
+ TreeData tree = TreeData::Load(path->resolved);
+ const auto paths = tree.optional("files").children("files");
+ const auto edits = tree.optional("edits").children("edits");
+ const auto units = tree.optional("units").children("units");
+ for (const auto & data : paths) create_path_config(data);
+ for (const auto & data : edits) create_edit_config(data);
+ for (const auto & data : units) create_unit_config(data);
}
-ConfigData ConfigData::type(const std::string & name) const
+PathConfig * FileLoader::create_path_config(const TreeData & data)
{
- ConfigData data(file);
- data.mark = mark.empty() ? name : mark + "-" + name;
- return data;
+ const auto path = paths_.emplace_back(std::make_unique(data)).get();
+ path->original = path->data.required("path").text();
+ path->resolved = resolve_file_path(path->original, data);
+ return path;
}
-ConfigData ConfigData::node(const size_t index) const
+EditConfig * FileLoader::create_edit_config(const TreeData & data)
{
- return type(std::to_string(index));
+ const auto edit = edits_.emplace_back(std::make_unique(data)).get();
+ edit->type = edit->data.required("type").text();
+ return edit;
}
-std::optional ConfigData::take_yaml(const std::string & name)
+UnitConfig * FileLoader::create_unit_config(const TreeData & data)
{
- if (!object.count(name)) {
- return std::nullopt;
- }
+ const auto unit = units_.emplace_back(std::make_unique(data)).get();
+ unit->type = unit->data.required("type").text();
+ unit->path = unit->data.optional("path").text();
- const auto yaml = object.at(name);
- object.erase(name);
- return yaml;
+ const auto item = unit->data.optional("item").child("c");
+ if (item.is_valid()) {
+ unit->item = create_link_config(item, unit);
+ }
+ const auto list = unit->data.optional("list").children();
+ for (const auto & data : list) {
+ unit->list.push_back(create_link_config(data, unit));
+ }
+ return unit;
}
-std::string ConfigData::take_text(const std::string & name)
+LinkConfig * FileLoader::create_link_config(const TreeData & data, UnitConfig * unit)
{
- if (!object.count(name)) {
- throw error("required field is not found", name, *this);
- }
-
- const auto yaml = object.at(name);
- object.erase(name);
- return yaml.as();
+ const auto link = links_.emplace_back(std::make_unique()).get();
+ link->parent = unit;
+ link->child = create_unit_config(data);
+ return link;
}
-std::string ConfigData::take_text(const std::string & name, const std::string & fail)
+void FileLoader::release(FileConfig & config)
{
- if (!object.count(name)) {
- return fail;
- }
-
- const auto yaml = object.at(name);
- object.erase(name);
- return yaml.as();
+ for (auto & path : paths_) config.paths.push_back(std::move(path));
+ for (auto & edit : edits_) config.edits.push_back(std::move(edit));
+ for (auto & unit : units_) config.units.push_back(std::move(unit));
+ for (auto & link : links_) config.links.push_back(std::move(link));
}
-std::vector ConfigData::take_list(const std::string & name)
+TreeLoader TreeLoader::Load(const std::string & path)
{
- if (!object.count(name)) {
- return std::vector();
- }
-
- const auto yaml = object.at(name);
- object.erase(name);
+ PathConfig root(TreeData::None());
+ root.original = path;
+ root.resolved = resolve_file_path(path, root.data);
+ return TreeLoader(&root);
+}
- if (!yaml.IsSequence()) {
- throw error("field is not a list type", name, *this);
+TreeLoader::TreeLoader(const PathConfig * root)
+{
+ std::queue paths;
+ paths.push(root);
+
+ // TODO(Takagi, Isamu): check include loop.
+ while (!paths.empty()) {
+ files_.emplace_back(paths.front());
+ paths.pop();
+ for (const auto & path : files_.back().paths()) {
+ paths.push(path.get());
+ }
}
- return std::vector(yaml.begin(), yaml.end());
}
-void resolve_link_nodes(RootConfig & root)
+auto create_path_mapping(const std::vector> & units)
{
- std::unordered_map paths;
- for (const auto & node : root.nodes) {
- if (node->path.empty()) {
+ std::unordered_map path_to_unit;
+ for (const auto & unit : units) {
+ if (unit->path.empty()) {
continue;
}
- if (paths.count(node->path)) {
- throw error("object path is not unique", node->path);
+ if (path_to_unit.count(unit->path)) {
+ throw PathConflict(unit->path);
}
- paths[node->path] = node;
+ path_to_unit[unit->path] = unit.get();
}
+ return path_to_unit;
+}
- std::vector nodes;
- std::vector links;
- for (const auto & node : root.nodes) {
- if (node->type == "link") {
- links.push_back(node);
+void apply_links(FileConfig & config)
+{
+ // Separate units into link types and others.
+ std::vector> link_units;
+ std::vector> node_units;
+ for (auto & unit : config.units) {
+ if (unit->type == unit_name::link) {
+ link_units.push_back(std::move(unit));
} else {
- nodes.push_back(node);
+ node_units.push_back(std::move(unit));
}
}
- std::unordered_map targets;
- for (const auto & node : nodes) {
- targets[node] = node;
- }
- for (const auto & node : links) {
- const auto path = node->data.take_text("link");
- if (!paths.count(path)) {
- throw error("link path is not found", path, node->data);
- }
- const auto link = paths.at(path);
- if (link->type == "link") {
- throw error("link target is link type", path, node->data);
+ // Create a mapping from path to unit.
+ const auto path_to_unit = create_path_mapping(node_units);
+
+ // Create a mapping from unit to unit.
+ std::unordered_map unit_to_unit;
+ for (const auto & unit : link_units) {
+ const auto path = unit->data.required("link").text();
+ if (path_to_unit.count(path) == 0) {
+ throw PathNotFound(unit->data.path(), path);
}
- targets[node] = link;
+ unit_to_unit[unit.get()] = path_to_unit.at(path);
}
- for (const auto & node : nodes) {
- for (auto & child : node->children) {
- child = targets.at(child);
+
+ // Update links.
+ for (const auto & link : config.links) {
+ if (unit_to_unit.count(link->child) != 0) {
+ link->child = unit_to_unit.at(link->child);
}
}
- root.nodes = nodes;
+
+ // Remove link type units from the graph.
+ config.units = std::move(node_units);
}
-void resolve_remove_edits(RootConfig & root)
+void apply_edits(FileConfig & config)
{
- std::unordered_map paths;
- for (const auto & node : root.nodes) {
- paths[node->path] = node;
- }
-
- std::unordered_set removes;
- for (const auto & edit : root.edits) {
- if (edit->type == "remove") {
- if (!paths.count(edit->path)) {
- throw error("remove path is not found", edit->path, edit->data);
+ // Create a mapping from path to unit.
+ const auto path_to_unit = create_path_mapping(config.units);
+
+ // List units to remove and links from/to them.
+ std::unordered_set remove_units;
+ std::unordered_set remove_links;
+ for (const auto & edit : config.edits) {
+ if (edit->type == edit_name::remove) {
+ const auto path = edit->data.required("path").text();
+ if (path_to_unit.count(path) == 0) {
+ throw PathNotFound(edit->data.path(), path);
}
- removes.insert(paths.at(edit->path));
+ remove_units.insert(path_to_unit.at(path));
}
}
-
- const auto filter = [removes](const std::vector & nodes) {
- std::vector result;
- for (const auto & node : nodes) {
- if (!removes.count(node)) {
- result.push_back(node);
- }
+ for (const auto & link : config.links) {
+ if (remove_units.count(link->parent) || remove_units.count(link->child)) {
+ remove_links.insert(link.get());
}
- return result;
- };
- for (const auto & node : root.nodes) {
- node->children = filter(node->children);
- }
- root.nodes = filter(root.nodes);
-}
-
-std::string complement_node_type(ConfigData & data)
-{
- if (data.object.count("diag")) {
- return "diag";
}
- return data.take_text("type");
-}
-std::string resolve_substitution(const std::string & substitution, const ConfigData & data)
-{
- std::stringstream ss(substitution);
- std::string word;
- std::vector words;
- while (getline(ss, word, ' ')) {
- words.push_back(word);
+ // Filter references to the removed links.
+ for (const auto & unit : config.units) {
+ if (remove_links.count(unit->item) != 0) {
+ unit->item = nullptr;
+ }
+ std::vector filtered_list;
+ for (const auto & link : unit->list) {
+ if (remove_links.count(link) == 0) {
+ filtered_list.push_back(link);
+ }
+ unit->list = filtered_list;
+ }
}
- if (words.size() == 2 && words[0] == "find-pkg-share") {
- return ament_index_cpp::get_package_share_directory(words[1]);
- }
- if (words.size() == 1 && words[0] == "dirname") {
- return std::filesystem::path(data.file).parent_path();
+ // Remove units and links.
+ std::vector> filtered_units;
+ std::vector> filtered_links;
+ for (auto & unit : config.units) {
+ if (remove_units.count(unit.get()) == 0) {
+ filtered_units.push_back(std::move(unit));
+ }
}
- throw error("unknown substitution", substitution, data);
-}
-
-std::string resolve_file_path(const std::string & path, const ConfigData & data)
-{
- static const std::regex pattern(R"(\$\(([^()]*)\))");
- std::smatch m;
- std::string result = path;
- while (std::regex_search(result, m, pattern)) {
- const std::string prefix = m.prefix();
- const std::string suffix = m.suffix();
- result = prefix + resolve_substitution(m.str(1), data) + suffix;
+ for (auto & link : config.links) {
+ if (remove_links.count(link.get()) == 0) {
+ filtered_links.push_back(std::move(link));
+ }
}
- return result;
+ config.units = std::move(filtered_units);
+ config.links = std::move(filtered_links);
}
-PathConfig::SharedPtr parse_path_config(const ConfigData & data)
+void topological_sort(FileConfig & config)
{
- const auto path = std::make_shared(data);
- path->original = path->data.take_text("path");
- path->resolved = resolve_file_path(path->original, path->data);
- return path;
-}
-
-UnitConfig::SharedPtr parse_node_config(const ConfigData & data)
-{
- const auto node = std::make_shared(data);
- node->path = node->data.take_text("path", "");
- node->type = node->data.take_text("type", "");
-
- if (node->type.empty()) {
- node->type = complement_node_type(node->data);
+ std::unordered_map degrees;
+ std::deque units;
+ std::deque result;
+ std::deque buffer;
+
+ // Create a list of raw pointer units.
+ for (const auto & unit : config.units) units.push_back(unit.get());
+
+ // Count degrees of each unit.
+ for (const auto & unit : units) {
+ if (const auto & link = unit->item) ++degrees[link->child];
+ for (const auto & link : unit->list) ++degrees[link->child];
}
- for (const auto & [index, yaml] : enumerate(node->data.take_list("list"))) {
- const auto child = data.node(index).load(yaml);
- node->children.push_back(parse_node_config(child));
+ // Find initial units that are zero degrees.
+ for (const auto & unit : units) {
+ if (degrees[unit] == 0) buffer.push_back(unit);
}
- return node;
-}
-
-EditConfig::SharedPtr parse_edit_config(const ConfigData & data)
-{
- const auto edit = std::make_shared(data);
- edit->path = edit->data.take_text("path", "");
- edit->type = edit->data.take_text("type", "");
- return edit;
-}
-FileConfig::SharedPtr parse_file_config(const ConfigData & data)
-{
- const auto file = std::make_shared(data);
- const auto path_data = data.type("file");
- const auto node_data = data.type("node");
- const auto edit_data = data.type("edit");
- const auto paths = file->data.take_list("files");
- const auto nodes = file->data.take_list("nodes");
- const auto edits = file->data.take_list("edits");
-
- for (const auto & [index, yaml] : enumerate(paths)) {
- const auto path = path_data.node(index).load(yaml);
- file->paths.push_back(parse_path_config(path));
- }
- for (const auto & [index, yaml] : enumerate(nodes)) {
- const auto node = node_data.node(index).load(yaml);
- file->nodes.push_back(parse_node_config(node));
- }
- for (const auto & [index, yaml] : enumerate(edits)) {
- const auto edit = edit_data.node(index).load(yaml);
- file->edits.push_back(parse_edit_config(edit));
+ // Sort by topological order.
+ while (!buffer.empty()) {
+ const auto unit = buffer.front();
+ buffer.pop_front();
+ if (const auto & link = unit->item) {
+ if (--degrees[link->child] == 0) {
+ buffer.push_back(link->child);
+ }
+ }
+ for (const auto & link : unit->list) {
+ if (--degrees[link->child] == 0) {
+ buffer.push_back(link->child);
+ }
+ }
+ result.push_back(unit);
}
- return file;
-}
-FileConfig::SharedPtr load_file_config(PathConfig & config)
-{
- const auto path = std::filesystem::path(config.resolved);
- if (!std::filesystem::exists(path)) {
- throw error("file is not found", path, config.data);
+ // Detect circulation because the result does not include the units on the loop.
+ if (result.size() != units.size()) {
+ throw GraphStructure("detect graph circulation");
}
- const auto file = ConfigData(path).load(YAML::LoadFile(path));
- return parse_file_config(file);
}
-RootConfig load_root_config(const PathConfig::SharedPtr root)
+FileConfig TreeLoader::construct()
{
- std::vector paths;
- paths.push_back(root);
-
- std::vector files;
- for (size_t i = 0; i < paths.size(); ++i) {
- const auto path = paths[i];
- const auto file = load_file_config(*path);
- files.push_back(file);
- extend(paths, file->paths);
- }
-
- std::vector nodes;
- std::vector edits;
- for (const auto & file : files) {
- extend(nodes, file->nodes);
- extend(edits, file->edits);
- }
- for (size_t i = 0; i < nodes.size(); ++i) {
- const auto node = nodes[i];
- extend(nodes, node->children);
- }
-
- RootConfig config;
- config.files = files;
- config.nodes = nodes;
- config.edits = edits;
- resolve_link_nodes(config);
- resolve_remove_edits(config);
+ FileConfig config;
+ for (auto & file : files_) file.release(config);
+ apply_links(config);
+ apply_edits(config);
+ topological_sort(config); // Check graph structure.
return config;
}
-RootConfig load_root_config(const std::string & path)
-{
- const auto root = std::make_shared(ConfigData("root-file"));
- root->original = path;
- root->resolved = path;
- return load_root_config(root);
-}
-
} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/config.hpp b/system/diagnostic_graph_aggregator/src/common/graph/config.hpp
index 40f16235ed5d6..a22daf907a27e 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/config.hpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/config.hpp
@@ -15,114 +15,87 @@
#ifndef COMMON__GRAPH__CONFIG_HPP_
#define COMMON__GRAPH__CONFIG_HPP_
-#include
+#include "data.hpp"
+#include "types.hpp"
#include
-#include
#include
-#include
#include
namespace diagnostic_graph_aggregator
{
-struct ConfigData
+struct PathConfig
{
- explicit ConfigData(const std::string & file);
- ConfigData load(YAML::Node yaml);
- ConfigData type(const std::string & name) const;
- ConfigData node(const size_t index) const;
-
- template
- T take(const std::string & name, const T & fail)
- {
- const auto yaml = take_yaml(name);
- return yaml ? yaml.value().as() : fail;
- }
-
- std::optional take_yaml(const std::string & name);
- std::string take_text(const std::string & name);
- std::string take_text(const std::string & name, const std::string & fail);
- std::vector take_list(const std::string & name);
-
- std::string file;
- std::string mark;
- std::unordered_map object;
-};
-
-struct BaseConfig
-{
- explicit BaseConfig(const ConfigData & data) : data(data) {}
- ConfigData data;
-};
-
-struct PathConfig : public BaseConfig
-{
- using SharedPtr = std::shared_ptr;
- using BaseConfig::BaseConfig;
+ explicit PathConfig(const TreeData & data) : data(data) {}
+ TreeData data;
std::string original;
std::string resolved;
};
-struct UnitConfig : public BaseConfig
+struct EditConfig
{
- using SharedPtr = std::shared_ptr;
- using BaseConfig::BaseConfig;
+ explicit EditConfig(const TreeData & data) : data(data) {}
+ TreeData data;
std::string type;
- std::string path;
- std::vector children;
};
-struct EditConfig : public BaseConfig
+struct LinkConfig
{
- using SharedPtr = std::shared_ptr;
- using BaseConfig::BaseConfig;
- std::string type;
- std::string path;
+ UnitConfig * parent = nullptr;
+ UnitConfig * child = nullptr;
};
-struct FileConfig : public BaseConfig
+struct UnitConfig
{
- using SharedPtr = std::shared_ptr;
- using BaseConfig::BaseConfig;
- std::vector paths;
- std::vector nodes;
- std::vector edits;
+ explicit UnitConfig(const TreeData & data) : data(data) {}
+ TreeData data;
+ std::string type;
+ std::string path;
+ LinkConfig * item = nullptr;
+ std::vector list;
+ size_t index;
};
-struct RootConfig
+struct FileConfig
{
- std::vector files;
- std::vector nodes;
- std::vector edits;
+ // Note: keep order correspondence between links and unit children for viewer.
+ std::vector> paths;
+ std::vector> edits;
+ std::vector> units;
+ std::vector> links;
};
-template
-T error(const std::string & text, const ConfigData & data)
-{
- const auto hint = data.mark.empty() ? data.file : data.mark + ":" + data.file;
- return T(text + " (" + hint + ")");
-}
-
-template
-T error(const std::string & text)
+class FileLoader
{
- return T(text);
-}
-
-template
-T error(const std::string & text, const std::string & value, const ConfigData & data)
-{
- return error(text + ": " + value, data);
-}
+public:
+ explicit FileLoader(const PathConfig * path);
+ const auto & paths() const { return paths_; }
+ void release(FileConfig & config);
+
+private:
+ PathConfig * create_path_config(const TreeData & data);
+ EditConfig * create_edit_config(const TreeData & data);
+ UnitConfig * create_unit_config(const TreeData & data);
+ LinkConfig * create_link_config(const TreeData & data, UnitConfig * unit);
+
+ // Note: keep order correspondence between links and unit children for viewer.
+ std::vector> paths_;
+ std::vector> edits_;
+ std::vector> units_;
+ std::vector> links_;
+};
-template
-T error(const std::string & text, const std::string & value)
+class TreeLoader
{
- return error(text + ": " + value);
-}
+public:
+ static TreeLoader Load(const std::string & path);
+ explicit TreeLoader(const PathConfig * root);
+ FileConfig construct();
-RootConfig load_root_config(const std::string & path);
+private:
+ std::vector files_;
+};
} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/data.cpp b/system/diagnostic_graph_aggregator/src/common/graph/data.cpp
new file mode 100644
index 0000000000000..ff186098c32e3
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/src/common/graph/data.cpp
@@ -0,0 +1,97 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "data.hpp"
+
+namespace diagnostic_graph_aggregator
+{
+
+TreeData TreeData::Load(const std::string & path)
+{
+ return TreeData(YAML::LoadFile(path), TreePath(path));
+}
+
+TreeData TreeData::None()
+{
+ return TreeData(YAML::Node(), TreePath(""));
+}
+
+TreeData::TreeData(const YAML::Node & yaml, const TreePath & path) : path_(path)
+{
+ yaml_ = yaml;
+}
+
+TreeData::Item TreeData::required(const std::string & name)
+{
+ // TODO(Takagi, Isamu): check map type.
+ const auto path = path_.field(name);
+ if (!yaml_[name]) {
+ throw FieldNotFound(path);
+ }
+ const auto data = yaml_[name];
+ yaml_.remove(name);
+ return TreeData(data, path);
+}
+
+TreeData::Item TreeData::optional(const std::string & name)
+{
+ // TODO(Takagi, Isamu): check map type.
+ const auto path = path_.field(name);
+ if (!yaml_[name]) {
+ return TreeData(YAML::Node(YAML::NodeType::Undefined), path);
+ }
+ const auto data = yaml_[name];
+ yaml_.remove(name);
+ return TreeData(data, path);
+}
+
+bool TreeData::is_valid() const
+{
+ return yaml_.Type() != YAML::NodeType::Undefined;
+}
+
+TreeData::Item TreeData::child(const std::string & path)
+{
+ return TreeData(yaml_, path_.child(path));
+}
+
+TreeData::List TreeData::children(const std::string & path)
+{
+ if (yaml_.Type() == YAML::NodeType::Undefined) {
+ return {};
+ }
+ if (yaml_.Type() != YAML::NodeType::Sequence) {
+ throw InvalidType(path_);
+ }
+
+ TreeData::List result;
+ for (size_t i = 0; i < yaml_.size(); ++i) {
+ result.emplace_back(yaml_[i], path_.child(path, i));
+ }
+ return result;
+}
+
+std::string TreeData::text(const std::string & fail)
+{
+ // TODO(Takagi, Isamu): check conversion failure
+ return yaml_.as(fail);
+}
+
+double TreeData::real(double fail)
+{
+ // TODO(Takagi, Isamu): check conversion failure
+ return yaml_.as(fail);
+}
+
+} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/data.hpp b/system/diagnostic_graph_aggregator/src/common/graph/data.hpp
new file mode 100644
index 0000000000000..437e3793df398
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/src/common/graph/data.hpp
@@ -0,0 +1,56 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COMMON__GRAPH__DATA_HPP_
+#define COMMON__GRAPH__DATA_HPP_
+
+#include "error.hpp"
+
+#include
+
+#include
+#include
+#include
+
+namespace diagnostic_graph_aggregator
+{
+
+class TreeData
+{
+public:
+ using Item = TreeData;
+ using List = std::vector;
+
+ static TreeData Load(const std::string & path);
+ static TreeData None();
+ TreeData(const YAML::Node & yaml, const TreePath & path);
+
+ const auto & path() const { return path_; }
+ Item required(const std::string & name);
+ Item optional(const std::string & name);
+ bool is_valid() const;
+
+ Item child(const std::string & path);
+ List children(const std::string & path = "");
+ std::string text(const std::string & fail = "");
+ double real(double fail);
+
+private:
+ YAML::Node yaml_;
+ TreePath path_;
+};
+
+} // namespace diagnostic_graph_aggregator
+
+#endif // COMMON__GRAPH__DATA_HPP_
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/debug.cpp b/system/diagnostic_graph_aggregator/src/common/graph/debug.cpp
deleted file mode 100644
index 8f2b741edd805..0000000000000
--- a/system/diagnostic_graph_aggregator/src/common/graph/debug.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2023 The Autoware Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "debug.hpp"
-
-#include "graph.hpp"
-#include "types.hpp"
-#include "units.hpp"
-
-#include
-#include
-#include
-#include
-
-namespace diagnostic_graph_aggregator
-{
-
-std::string get_level_text(DiagnosticLevel level)
-{
- switch (level) {
- case DiagnosticStatus::OK:
- return "OK";
- case DiagnosticStatus::WARN:
- return "WARN";
- case DiagnosticStatus::ERROR:
- return "ERROR";
- case DiagnosticStatus::STALE:
- return "STALE";
- }
- return "UNKNOWN";
-}
-
-void Graph::debug()
-{
- std::vector lines;
- for (const auto & node : nodes_) {
- const auto level_name = get_level_text(node->level());
- const auto index_name = std::to_string(node->index());
- lines.push_back({index_name, level_name, node->path(), node->type()});
- }
- for (const auto & [name, level] : unknowns_) {
- const auto level_name = get_level_text(level);
- lines.push_back({"*", level_name, name, "unknown"});
- }
-
- std::array widths = {};
- for (const auto & line : lines) {
- for (size_t i = 0; i < diag_debug_size; ++i) {
- widths[i] = std::max(widths[i], line[i].length());
- }
- }
-
- const size_t total_width = std::accumulate(widths.begin(), widths.end(), 0);
- std::cout << std::string(total_width + 3 * diag_debug_size + 1, '=') << std::endl;
-
- for (const auto & line : lines) {
- for (size_t i = 0; i < diag_debug_size; ++i) {
- std::cout << "| " << std::left << std::setw(widths[i]) << line[i] << " ";
- }
- std::cout << "|" << std::endl;
- }
-}
-
-} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/debug.hpp b/system/diagnostic_graph_aggregator/src/common/graph/debug.hpp
deleted file mode 100644
index 69a0eeb2c8023..0000000000000
--- a/system/diagnostic_graph_aggregator/src/common/graph/debug.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2023 The Autoware Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef COMMON__GRAPH__DEBUG_HPP_
-#define COMMON__GRAPH__DEBUG_HPP_
-
-#include
-#include
-
-namespace diagnostic_graph_aggregator
-{
-
-constexpr size_t diag_debug_size = 4;
-using DiagDebugData = std::array;
-
-} // namespace diagnostic_graph_aggregator
-
-#endif // COMMON__GRAPH__DEBUG_HPP_
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/error.cpp b/system/diagnostic_graph_aggregator/src/common/graph/error.cpp
new file mode 100644
index 0000000000000..e78a444c5ab7f
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/src/common/graph/error.cpp
@@ -0,0 +1,58 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "error.hpp"
+
+namespace diagnostic_graph_aggregator
+{
+
+TreePath::TreePath(const std::string & file)
+{
+ file_ = file;
+}
+
+TreePath TreePath::field(const std::string & name)
+{
+ TreePath result(file_);
+ result.node_ = node_;
+ result.name_ = name;
+ return result;
+}
+
+TreePath TreePath::child(const std::string & path)
+{
+ const auto sep = node_.empty() ? "" : "-";
+ TreePath result(file_);
+ result.node_ = node_ + sep + path;
+ return result;
+}
+
+TreePath TreePath::child(const std::string & path, const size_t index)
+{
+ const auto sep = path.empty() ? "" : "-";
+ return child(path + sep + std::to_string(index));
+}
+
+TreePath TreePath::child(const size_t index)
+{
+ return child(std::to_string(index));
+}
+
+std::string TreePath::text() const
+{
+ const auto sep = node_.empty() ? "" : ":";
+ return " (" + file_ + sep + node_ + ")";
+}
+
+} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/error.hpp b/system/diagnostic_graph_aggregator/src/common/graph/error.hpp
index dadfab450c783..d923e12caf783 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/error.hpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/error.hpp
@@ -16,51 +16,113 @@
#define COMMON__GRAPH__ERROR_HPP_
#include
+#include
namespace diagnostic_graph_aggregator
{
+struct TreePath
+{
+ explicit TreePath(const std::string & file);
+ TreePath field(const std::string & name);
+ TreePath child(const std::string & path);
+ TreePath child(const std::string & path, const size_t index);
+ TreePath child(const size_t index);
+ std::string text() const;
+ std::string file() const { return file_; }
+ std::string node() const { return node_; }
+ std::string name() const { return name_; }
+
+private:
+ std::string file_;
+ std::string node_;
+ std::string name_;
+};
+
struct Exception : public std::runtime_error
{
using runtime_error::runtime_error;
};
-class FileNotFound : public Exception
+struct FileNotFound : public Exception
{
- using Exception::Exception;
+ explicit FileNotFound(const TreePath & path, const std::string & file)
+ : Exception(format(path, file))
+ {
+ }
+ static std::string format(const TreePath & path, const std::string & file)
+ {
+ return "file is not found: " + file + path.text();
+ }
};
-class UnknownType : public Exception
+struct FieldNotFound : public Exception
{
- using Exception::Exception;
+ explicit FieldNotFound(const TreePath & path) : Exception(format(path)) {}
+ static std::string format(const TreePath & path)
+ {
+ return "required field is not found: " + path.name() + path.text();
+ }
};
-class InvalidType : public Exception
+struct InvalidType : public Exception
{
- using Exception::Exception;
+ explicit InvalidType(const TreePath & path) : Exception(format(path)) {}
+ static std::string format(const TreePath & path)
+ {
+ return "field is not a list type: " + path.name() + path.text();
+ }
};
-class InvalidValue : public Exception
+struct ModeNotFound : public Exception
{
- using Exception::Exception;
+ explicit ModeNotFound(const std::string & path) : Exception(format(path)) {}
+ static std::string format(const std::string & path) { return "mode path is not found: " + path; }
};
-class FieldNotFound : public Exception
+struct PathConflict : public Exception
{
- using Exception::Exception;
+ explicit PathConflict(const std::string & path) : Exception(format(path)) {}
+ static std::string format(const std::string & path) { return "node path is not unique: " + path; }
};
-class PathConflict : public Exception
+struct PathNotFound : public Exception
{
- using Exception::Exception;
+ explicit PathNotFound(const TreePath & path, const std::string & link)
+ : Exception(format(path, link))
+ {
+ }
+ static std::string format(const TreePath & path, const std::string & link)
+ {
+ return "path is not found: " + link + path.text();
+ }
};
-class PathNotFound : public Exception
+struct UnknownSubstitution : public Exception
{
- using Exception::Exception;
+ explicit UnknownSubstitution(const TreePath & path, const std::string & substitution)
+ : Exception(format(path, substitution))
+ {
+ }
+ static std::string format(const TreePath & path, const std::string & substitution)
+ {
+ return "unknown substitution: " + substitution + path.text();
+ }
+};
+
+struct UnknownUnitType : public Exception
+{
+ explicit UnknownUnitType(const TreePath & path, const std::string & type)
+ : Exception(format(path, type))
+ {
+ }
+ static std::string format(const TreePath & path, const std::string & type)
+ {
+ return "unknown unit type: " + type + path.text();
+ }
};
-class GraphStructure : public Exception
+struct GraphStructure : public Exception
{
using Exception::Exception;
};
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/graph.cpp b/system/diagnostic_graph_aggregator/src/common/graph/graph.cpp
index 5d7c11879a9cc..0a7d78a5ce982 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/graph.cpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/graph.cpp
@@ -16,211 +16,64 @@
#include "config.hpp"
#include "error.hpp"
+#include "loader.hpp"
#include "units.hpp"
-#include
-#include
-#include
#include
-#include
-#include
-
-// DEBUG
-#include
namespace diagnostic_graph_aggregator
{
-BaseUnit::UniquePtrList topological_sort(BaseUnit::UniquePtrList && input)
-{
- std::unordered_map degrees;
- std::deque nodes;
- std::deque result;
- std::deque buffer;
-
- // Create a list of raw pointer nodes.
- for (const auto & node : input) {
- nodes.push_back(node.get());
- }
-
- // Count degrees of each node.
- for (const auto & node : nodes) {
- for (const auto & child : node->children()) {
- ++degrees[child];
- }
- }
-
- // Find initial nodes that are zero degrees.
- for (const auto & node : nodes) {
- if (degrees[node] == 0) {
- buffer.push_back(node);
- }
- }
-
- // Sort by topological order.
- while (!buffer.empty()) {
- const auto node = buffer.front();
- buffer.pop_front();
- for (const auto & child : node->children()) {
- if (--degrees[child] == 0) {
- buffer.push_back(child);
- }
- }
- result.push_back(node);
- }
-
- // Detect circulation because the result does not include the nodes on the loop.
- if (result.size() != nodes.size()) {
- throw error("detect graph circulation");
- }
-
- // Reverse the result to process from leaf node.
- result = std::deque(result.rbegin(), result.rend());
-
- // Replace node vector.
- std::unordered_map indices;
- for (size_t i = 0; i < result.size(); ++i) {
- indices[result[i]] = i;
- }
- std::vector> sorted(input.size());
- for (auto & node : input) {
- sorted[indices[node.get()]] = std::move(node);
- }
- return sorted;
-}
-
-BaseUnit::UniquePtr make_node(const UnitConfig::SharedPtr & config)
-{
- if (config->type == "diag") {
- return std::make_unique(config->path);
- }
- if (config->type == "and") {
- return std::make_unique(config->path, false);
- }
- if (config->type == "short-circuit-and") {
- return std::make_unique(config->path, true);
- }
- if (config->type == "or") {
- return std::make_unique(config->path);
- }
- if (config->type == "warn-to-ok") {
- return std::make_unique(config->path, DiagnosticStatus::OK);
- }
- if (config->type == "warn-to-error") {
- return std::make_unique(config->path, DiagnosticStatus::ERROR);
- }
- if (config->type == "ok") {
- return std::make_unique(config->path, DiagnosticStatus::OK);
- }
- if (config->type == "warn") {
- return std::make_unique(config->path, DiagnosticStatus::WARN);
- }
- if (config->type == "error") {
- return std::make_unique(config->path, DiagnosticStatus::ERROR);
- }
- if (config->type == "stale") {
- return std::make_unique(config->path, DiagnosticStatus::STALE);
- }
- throw error("unknown node type", config->type, config->data);
-}
-
-Graph::Graph()
+void Graph::create(const std::string & file, const std::string & id)
{
- // for unique_ptr
+ GraphLoader graph(file);
+ nodes_ = graph.release_nodes();
+ diags_ = graph.release_diags();
+ links_ = graph.release_links();
+ for (const auto & diag : diags_) names_[diag->name()] = diag.get();
+ for (const auto & node : nodes_) units_.push_back(node.get());
+ for (const auto & diag : diags_) units_.push_back(diag.get());
+
+ id_ = id;
}
-Graph::~Graph()
+void Graph::update(const rclcpp::Time & stamp)
{
- // for unique_ptr
+ for (const auto & diag : diags_) diag->on_time(stamp);
}
-void Graph::init(const std::string & file)
+bool Graph::update(const rclcpp::Time & stamp, const DiagnosticStatus & status)
{
- BaseUnit::UniquePtrList nodes;
- BaseUnit::NodeDict dict;
-
- for (const auto & config : load_root_config(file).nodes) {
- const auto node = nodes.emplace_back(make_node(config)).get();
- dict.configs[config] = node;
- dict.paths[config->path] = node;
- }
- dict.paths.erase("");
-
- for (const auto & [config, node] : dict.configs) {
- node->init(config, dict);
- }
-
- // Sort units in topological order for update dependencies.
- nodes = topological_sort(std::move(nodes));
-
- // List diag nodes that have diag name.
- for (const auto & node : nodes) {
- const auto diag = dynamic_cast(node.get());
- if (diag) {
- diags_[diag->name()] = diag;
- }
- }
-
- // List unit nodes that have path name.
- for (const auto & node : nodes) {
- if (!node->path().empty()) {
- units_.push_back(node.get());
- }
- }
-
- // Set unit index.
- for (size_t index = 0; index < units_.size(); ++index) {
- units_[index]->set_index(index);
- }
-
- nodes_ = std::move(nodes);
+ const auto iter = names_.find(status.name);
+ if (iter == names_.end()) return false;
+ iter->second->on_diag(stamp, status);
+ return true;
}
-void Graph::callback(const rclcpp::Time & stamp, const DiagnosticArray & array)
+DiagGraphStruct Graph::create_struct(const rclcpp::Time & stamp) const
{
- for (const auto & status : array.status) {
- const auto iter = diags_.find(status.name);
- if (iter != diags_.end()) {
- iter->second->callback(stamp, status);
- } else {
- unknowns_[status.name] = status.level;
- }
- }
+ DiagGraphStruct msg;
+ msg.stamp = stamp;
+ msg.id = id_;
+ for (const auto & node : nodes_) msg.nodes.push_back(node->create_struct());
+ for (const auto & diag : diags_) msg.diags.push_back(diag->create_struct());
+ for (const auto & link : links_) msg.links.push_back(link->create_struct());
+ return msg;
}
-DiagnosticGraph Graph::report(const rclcpp::Time & stamp)
+DiagGraphStatus Graph::create_status(const rclcpp::Time & stamp) const
{
- for (const auto & node : nodes_) {
- node->update(stamp);
- }
-
- DiagnosticGraph message;
- message.stamp = stamp;
- message.nodes.reserve(units_.size());
- for (const auto & node : units_) {
- const auto report = node->report();
- DiagnosticNode temp;
- temp.status.name = node->path();
- temp.status.level = report.level;
- for (const auto & [ref, used] : report.links) {
- DiagnosticLink link;
- link.index = ref->index();
- link.used = used;
- temp.links.push_back(link);
- }
- message.nodes.push_back(temp);
- }
- return message;
+ DiagGraphStatus msg;
+ msg.stamp = stamp;
+ msg.id = id_;
+ for (const auto & node : nodes_) msg.nodes.push_back(node->create_status());
+ for (const auto & diag : diags_) msg.diags.push_back(diag->create_status());
+ for (const auto & link : links_) msg.links.push_back(link->create_status());
+ return msg;
}
-std::vector Graph::nodes() const
-{
- std::vector result;
- result.reserve(nodes_.size());
- for (const auto & node : nodes_) {
- result.push_back(node.get());
- }
- return result;
-}
+// For unique_ptr members.
+Graph::Graph() = default;
+Graph::~Graph() = default;
} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/graph.hpp b/system/diagnostic_graph_aggregator/src/common/graph/graph.hpp
index 8c29a303814b8..ed7bac0e96ce3 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/graph.hpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/graph.hpp
@@ -22,29 +22,34 @@
#include
#include
#include
-#include
#include
namespace diagnostic_graph_aggregator
{
-class Graph final
+class Graph
{
public:
- Graph();
- ~Graph();
-
- void init(const std::string & file);
- void callback(const rclcpp::Time & stamp, const DiagnosticArray & array);
- void debug();
- DiagnosticGraph report(const rclcpp::Time & stamp);
- std::vector nodes() const;
+ void create(const std::string & file, const std::string & id = "");
+ void update(const rclcpp::Time & stamp);
+ bool update(const rclcpp::Time & stamp, const DiagnosticStatus & status);
+ const auto & nodes() const { return nodes_; }
+ const auto & diags() const { return diags_; }
+ const auto & units() const { return units_; }
+ DiagGraphStruct create_struct(const rclcpp::Time & stamp) const;
+ DiagGraphStatus create_status(const rclcpp::Time & stamp) const;
+
+ Graph(); // For unique_ptr members.
+ ~Graph(); // For unique_ptr members.
private:
- std::vector> nodes_;
+ // Note: keep order correspondence between links and unit children for viewer.
+ std::vector> nodes_;
+ std::vector> diags_;
+ std::vector> links_;
std::vector units_;
- std::unordered_map diags_;
- std::unordered_map unknowns_;
+ std::unordered_map names_;
+ std::string id_;
};
} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/loader.cpp b/system/diagnostic_graph_aggregator/src/common/graph/loader.cpp
new file mode 100644
index 0000000000000..74636adab7415
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/src/common/graph/loader.cpp
@@ -0,0 +1,189 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "loader.hpp"
+
+#include "config.hpp"
+#include "error.hpp"
+#include "names.hpp"
+#include "types.hpp"
+#include "units.hpp"
+
+#include
+
+namespace diagnostic_graph_aggregator
+{
+
+struct UnitLoader::GraphLinks
+{
+ std::unordered_map config_links;
+ std::unordered_map> parent_links;
+};
+
+size_t UnitLoader::index() const
+{
+ return config_->index;
+}
+
+const std::string & UnitLoader::path() const
+{
+ return config_->path;
+}
+
+const std::string & UnitLoader::type() const
+{
+ return config_->type;
+}
+
+TreeData & UnitLoader::data() const
+{
+ return config_->data;
+}
+
+UnitLink * UnitLoader::child() const
+{
+ return links_.config_links.at(config_->item);
+}
+
+std::vector UnitLoader::children() const
+{
+ std::vector result;
+ for (const auto & config : config_->list) {
+ result.push_back(links_.config_links.at(config));
+ }
+ return result;
+}
+
+std::vector UnitLoader::parents() const
+{
+ return links_.parent_links.at(config_);
+}
+
+GraphLoader::GraphLoader(const std::string & file)
+{
+ TreeLoader tree = TreeLoader::Load(file);
+ FileConfig root = tree.construct();
+
+ // Init array index to be able get it from unit itself.
+ std::vector diags;
+ std::vector nodes;
+ for (const auto & config : root.units) {
+ (config->type == unit_name::diag ? diags : nodes).push_back(config.get());
+ }
+ for (size_t i = 0; i < diags.size(); ++i) diags[i]->index = i;
+ for (size_t i = 0; i < nodes.size(); ++i) nodes[i]->index = i;
+
+ // Create link objects.
+ UnitLoader::GraphLinks graph_links;
+ for (const auto & config : root.units) {
+ graph_links.parent_links[config.get()] = {};
+ }
+ for (const auto & config : root.links) {
+ const auto link = links_.emplace_back(create_link()).get();
+ graph_links.config_links[config.get()] = link;
+ graph_links.parent_links[config->child].push_back(link);
+ }
+
+ // Create node objects.
+ std::unordered_map config_units;
+ for (const auto & config : diags) {
+ const auto unit = UnitLoader(config, graph_links);
+ const auto diag = diags_.emplace_back(create_diag(unit)).get();
+ config_units[config] = diag;
+ }
+ for (const auto & config : nodes) {
+ const auto unit = UnitLoader(config, graph_links);
+ const auto node = nodes_.emplace_back(create_node(unit)).get();
+ config_units[config] = node;
+ }
+
+ // Connect links and nodes;
+ for (const auto & [config, link] : graph_links.config_links) {
+ const auto parent = config_units.at(config->parent);
+ const auto child = config_units.at(config->child);
+ link->initialize_object(parent, child);
+ }
+
+ // Init struct.
+ for (auto & node : nodes_) node->initialize_struct();
+ for (auto & diag : diags_) diag->initialize_struct();
+ for (auto & link : links_) link->initialize_struct();
+
+ // Init status that needs struct.
+ for (auto & node : nodes_) node->initialize_status();
+ for (auto & diag : diags_) diag->initialize_status();
+ for (auto & link : links_) link->initialize_status();
+}
+
+std::vector> GraphLoader::release_links()
+{
+ return std::move(links_);
+}
+
+std::vector> GraphLoader::release_nodes()
+{
+ return std::move(nodes_);
+}
+
+std::vector> GraphLoader::release_diags()
+{
+ return std::move(diags_);
+}
+
+std::unique_ptr GraphLoader::create_link()
+{
+ return std::make_unique();
+}
+
+std::unique_ptr GraphLoader::create_diag(const UnitLoader & unit)
+{
+ if (unit.type() == unit_name::diag) {
+ return std::make_unique(unit);
+ }
+ throw UnknownUnitType(unit.data().path(), unit.type());
+}
+
+std::unique_ptr GraphLoader::create_node(const UnitLoader & unit)
+{
+ if (unit.type() == unit_name::max) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::short_circuit_max) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::min) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::warn_to_ok) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::warn_to_error) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::ok) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::warn) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::error) {
+ return std::make_unique(unit);
+ }
+ if (unit.type() == unit_name::stale) {
+ return std::make_unique(unit);
+ }
+ throw UnknownUnitType(unit.data().path(), unit.type());
+}
+
+} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/loader.hpp b/system/diagnostic_graph_aggregator/src/common/graph/loader.hpp
new file mode 100644
index 0000000000000..226b3ab279b22
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/src/common/graph/loader.hpp
@@ -0,0 +1,67 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COMMON__GRAPH__LOADER_HPP_
+#define COMMON__GRAPH__LOADER_HPP_
+
+#include "types.hpp"
+
+#include
+#include
+#include
+#include
+
+namespace diagnostic_graph_aggregator
+{
+
+class UnitLoader
+{
+public:
+ struct GraphLinks;
+ UnitLoader(UnitConfig * config, GraphLinks & links) : config_(config), links_(links) {}
+ const std::string & path() const;
+ const std::string & type() const;
+ size_t index() const;
+ TreeData & data() const;
+ UnitLink * child() const;
+ std::vector children() const;
+ std::vector parents() const;
+
+private:
+ UnitConfig * config_;
+ GraphLinks & links_;
+};
+
+class GraphLoader
+{
+public:
+ explicit GraphLoader(const std::string & file);
+ std::vector> release_nodes();
+ std::vector> release_diags();
+ std::vector> release_links();
+
+private:
+ std::unique_ptr create_link();
+ std::unique_ptr create_diag(const UnitLoader & unit);
+ std::unique_ptr create_node(const UnitLoader & unit);
+
+ // Note: keep order correspondence between links and unit children for viewer.
+ std::vector> nodes_;
+ std::vector> diags_;
+ std::vector> links_;
+};
+
+} // namespace diagnostic_graph_aggregator
+
+#endif // COMMON__GRAPH__LOADER_HPP_
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/names.hpp b/system/diagnostic_graph_aggregator/src/common/graph/names.hpp
new file mode 100644
index 0000000000000..aa5a9c46e7b37
--- /dev/null
+++ b/system/diagnostic_graph_aggregator/src/common/graph/names.hpp
@@ -0,0 +1,42 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef COMMON__GRAPH__NAMES_HPP_
+#define COMMON__GRAPH__NAMES_HPP_
+
+namespace diagnostic_graph_aggregator::unit_name
+{
+
+constexpr char const * link = "link";
+constexpr char const * diag = "diag";
+constexpr char const * min = "or";
+constexpr char const * max = "and";
+constexpr char const * short_circuit_max = "short-circuit-and";
+constexpr char const * warn_to_ok = "warn-to-ok";
+constexpr char const * warn_to_error = "warn-to-error";
+constexpr char const * ok = "ok";
+constexpr char const * warn = "warn";
+constexpr char const * error = "error";
+constexpr char const * stale = "stale";
+
+} // namespace diagnostic_graph_aggregator::unit_name
+
+namespace diagnostic_graph_aggregator::edit_name
+{
+
+constexpr char const * remove = "remove";
+
+} // namespace diagnostic_graph_aggregator::edit_name
+
+#endif // COMMON__GRAPH__NAMES_HPP_
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/types.hpp b/system/diagnostic_graph_aggregator/src/common/graph/types.hpp
index 693094914db1d..6eae26a1f98c7 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/types.hpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/types.hpp
@@ -17,22 +17,47 @@
#include
#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include
#include
+#include
+
namespace diagnostic_graph_aggregator
{
using diagnostic_msgs::msg::DiagnosticArray;
using diagnostic_msgs::msg::DiagnosticStatus;
-using tier4_system_msgs::msg::DiagnosticGraph;
-using tier4_system_msgs::msg::DiagnosticLink;
-using tier4_system_msgs::msg::DiagnosticNode;
+using tier4_system_msgs::msg::DiagGraphStatus;
+using tier4_system_msgs::msg::DiagGraphStruct;
+using tier4_system_msgs::msg::DiagLeafStatus;
+using tier4_system_msgs::msg::DiagLeafStruct;
+using tier4_system_msgs::msg::DiagLinkStatus;
+using tier4_system_msgs::msg::DiagLinkStruct;
+using tier4_system_msgs::msg::DiagNodeStatus;
+using tier4_system_msgs::msg::DiagNodeStruct;
using DiagnosticLevel = DiagnosticStatus::_level_type;
+struct PathConfig;
+struct EditConfig;
+struct UnitConfig;
+struct LinkConfig;
+
+class TreeData;
+class UnitLink;
class BaseUnit;
+class NodeUnit;
class DiagUnit;
+class Graph;
+class UnitLoader;
} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/units.cpp b/system/diagnostic_graph_aggregator/src/common/graph/units.cpp
index 6048cae85e633..da801ae078e6c 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/units.cpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/units.cpp
@@ -14,168 +14,216 @@
#include "units.hpp"
+#include "config.hpp"
#include "error.hpp"
+#include "loader.hpp"
#include
-#include
-#include
-#include
namespace diagnostic_graph_aggregator
{
-using LinkList = std::vector>;
+void UnitLink::initialize_object(BaseUnit * parent, BaseUnit * child)
+{
+ parent_ = parent;
+ child_ = child;
+}
-void merge(LinkList & a, const LinkList & b, bool uses)
+void UnitLink::initialize_struct()
{
- for (const auto & [node, used] : b) {
- a.push_back(std::make_pair(node, used && uses));
- }
+ struct_.parent = parent_->index();
+ struct_.child = child_->index();
+ struct_.is_leaf = child_->is_leaf();
}
-auto resolve(const BaseUnit::NodeDict & dict, const std::vector & children)
+void UnitLink::initialize_status()
{
- std::vector result;
- for (const auto & child : children) {
- result.push_back(dict.configs.at(child));
- }
- return result;
+ // Do nothing.
}
-BaseUnit::BaseUnit(const std::string & path) : path_(path)
+BaseUnit::BaseUnit(const UnitLoader & unit)
{
- index_ = 0;
- level_ = DiagnosticStatus::OK;
+ index_ = unit.index();
+ parents_ = unit.parents();
}
-BaseUnit::NodeData BaseUnit::status() const
+bool BaseUnit::update()
{
- if (path_.empty()) {
- return {level_, links_};
- } else {
- return {level_, {std::make_pair(this, true)}};
+ // Update the level of this unit.
+ update_status();
+
+ // If the level does not change, it will not affect the parents.
+ const auto curr_level = level();
+ if (curr_level == prev_level_) return false;
+ prev_level_ = curr_level;
+
+ // If the level changes, the parents also need to be updated.
+ bool result = false;
+ for (const auto & link : parents_) {
+ const auto unit = link->parent();
+ result = result || unit->update();
}
+ return result;
}
-BaseUnit::NodeData BaseUnit::report() const
+NodeUnit::NodeUnit(const UnitLoader & unit) : BaseUnit(unit)
{
- return {level_, links_};
+ struct_.path = unit.path();
+ status_.level = DiagnosticStatus::STALE;
}
-void DiagUnit::init(const UnitConfig::SharedPtr & config, const NodeDict &)
+void NodeUnit::initialize_struct()
{
- name_ = config->data.take_text("diag");
- timeout_ = config->data.take("timeout", 1.0);
+ struct_.type = type();
}
-void DiagUnit::update(const rclcpp::Time & stamp)
+void NodeUnit::initialize_status()
{
- if (diagnostics_) {
- const auto updated = diagnostics_.value().first;
- const auto elapsed = (stamp - updated).seconds();
- if (timeout_ < elapsed) {
- diagnostics_ = std::nullopt;
- }
- }
+ if (child_links().size() == 0) update();
+}
- if (diagnostics_) {
- level_ = diagnostics_.value().second.level;
- } else {
- level_ = DiagnosticStatus::STALE;
- }
+LeafUnit::LeafUnit(const UnitLoader & unit) : BaseUnit(unit)
+{
+ const auto node = unit.data().required("node").text();
+ const auto name = unit.data().required("name").text();
+ const auto sep = node.empty() ? "" : ": ";
+
+ struct_.path = unit.path();
+ struct_.name = node + sep + name;
+ status_.level = DiagnosticStatus::STALE;
}
-void DiagUnit::callback(const rclcpp::Time & stamp, const DiagnosticStatus & status)
+void LeafUnit::initialize_struct()
{
- diagnostics_ = std::make_pair(stamp, status);
+ struct_.type = type();
}
-AndUnit::AndUnit(const std::string & path, bool short_circuit) : BaseUnit(path)
+void LeafUnit::initialize_status()
{
- short_circuit_ = short_circuit;
+ if (child_links().size() == 0) update();
}
-void AndUnit::init(const UnitConfig::SharedPtr & config, const NodeDict & dict)
+DiagUnit::DiagUnit(const UnitLoader & unit) : LeafUnit(unit)
{
- children_ = resolve(dict, config->children);
+ timeout_ = unit.data().optional("timeout").real(1.0);
}
-void AndUnit::update(const rclcpp::Time &)
+void DiagUnit::update_status()
{
- if (children_.empty()) {
- return;
- }
+ // Do nothing. The level is updated by on_diag and on_time.
+}
- bool uses = true;
- level_ = DiagnosticStatus::OK;
- links_ = LinkList();
+bool DiagUnit::on_diag(const rclcpp::Time & stamp, const DiagnosticStatus & status)
+{
+ last_updated_time_ = stamp;
+ status_.level = status.level;
+ status_.message = status.message;
+ status_.hardware_id = status.hardware_id;
+ status_.values = status.values;
+ return update();
+}
- for (const auto & child : children_) {
- const auto status = child->status();
- level_ = std::max(level_, status.level);
- merge(links_, status.links, uses);
- if (short_circuit_ && level_ != DiagnosticStatus::OK) {
- uses = false;
+bool DiagUnit::on_time(const rclcpp::Time & stamp)
+{
+ if (last_updated_time_) {
+ const auto updated = last_updated_time_.value();
+ const auto elapsed = (stamp - updated).seconds();
+ if (timeout_ < elapsed) {
+ last_updated_time_ = std::nullopt;
+ status_ = DiagLeafStatus();
+ status_.level = DiagnosticStatus::STALE;
}
}
- level_ = std::min(level_, DiagnosticStatus::ERROR);
+ return update();
}
-void OrUnit::init(const UnitConfig::SharedPtr & config, const NodeDict & dict)
+MaxUnit::MaxUnit(const UnitLoader & unit) : NodeUnit(unit)
{
- children_ = resolve(dict, config->children);
+ links_ = unit.children();
}
-void OrUnit::update(const rclcpp::Time &)
+void MaxUnit::update_status()
{
- if (children_.empty()) {
- return;
+ DiagnosticLevel level = DiagnosticStatus::OK;
+ for (const auto & link : links_) {
+ level = std::max(level, link->child()->level());
}
+ status_.level = std::min(level, DiagnosticStatus::ERROR);
+}
- level_ = DiagnosticStatus::ERROR;
- links_ = LinkList();
-
- for (const auto & child : children_) {
- const auto status = child->status();
- level_ = std::min(level_, status.level);
- merge(links_, status.links, true);
+void ShortCircuitMaxUnit::update_status()
+{
+ // TODO(Takagi, Isamu): update link flags.
+ DiagnosticLevel level = DiagnosticStatus::OK;
+ for (const auto & link : links_) {
+ level = std::max(level, link->child()->level());
}
- level_ = std::min(level_, DiagnosticStatus::ERROR);
+ status_.level = std::min(level, DiagnosticStatus::ERROR);
}
-RemapUnit::RemapUnit(const std::string & path, DiagnosticLevel remap_warn) : BaseUnit(path)
+MinUnit::MinUnit(const UnitLoader & unit) : NodeUnit(unit)
{
- remap_warn_ = remap_warn;
+ links_ = unit.children();
}
-void RemapUnit::init(const UnitConfig::SharedPtr & config, const NodeDict & dict)
+void MinUnit::update_status()
{
- if (config->children.size() != 1) {
- throw error("list size must be 1", config->data);
+ DiagnosticLevel level = DiagnosticStatus::OK;
+ if (!links_.empty()) {
+ level = DiagnosticStatus::STALE;
+ for (const auto & link : links_) {
+ level = std::min(level, link->child()->level());
+ }
}
- children_ = resolve(dict, config->children);
+ status_.level = std::min(level, DiagnosticStatus::ERROR);
+}
+
+RemapUnit::RemapUnit(const UnitLoader & unit) : NodeUnit(unit)
+{
+ link_ = unit.child();
+}
+
+void RemapUnit::update_status()
+{
+ const auto level = link_->child()->level();
+ status_.level = (level == level_from_) ? level_to_ : level;
+}
+
+WarnToOkUnit::WarnToOkUnit(const UnitLoader & unit) : RemapUnit(unit)
+{
+ level_from_ = DiagnosticStatus::WARN;
+ level_to_ = DiagnosticStatus::OK;
}
-void RemapUnit::update(const rclcpp::Time &)
+WarnToErrorUnit::WarnToErrorUnit(const UnitLoader & unit) : RemapUnit(unit)
{
- const auto status = children_.front()->status();
- level_ = status.level;
- links_ = status.links;
+ level_from_ = DiagnosticStatus::WARN;
+ level_to_ = DiagnosticStatus::ERROR;
+}
- if (level_ == DiagnosticStatus::WARN) level_ = remap_warn_;
+void ConstUnit::update_status()
+{
+ // Do nothing. This unit always returns the same level.
+}
+
+OkUnit::OkUnit(const UnitLoader & unit) : ConstUnit(unit)
+{
+ status_.level = DiagnosticStatus::OK;
}
-DebugUnit::DebugUnit(const std::string & path, DiagnosticLevel level) : BaseUnit(path)
+WarnUnit::WarnUnit(const UnitLoader & unit) : ConstUnit(unit)
{
- level_ = level; // overwrite
+ status_.level = DiagnosticStatus::WARN;
}
-void DebugUnit::init(const UnitConfig::SharedPtr &, const NodeDict &)
+ErrorUnit::ErrorUnit(const UnitLoader & unit) : ConstUnit(unit)
{
+ status_.level = DiagnosticStatus::ERROR;
}
-void DebugUnit::update(const rclcpp::Time &)
+StaleUnit::StaleUnit(const UnitLoader & unit) : ConstUnit(unit)
{
+ status_.level = DiagnosticStatus::STALE;
}
} // namespace diagnostic_graph_aggregator
diff --git a/system/diagnostic_graph_aggregator/src/common/graph/units.hpp b/system/diagnostic_graph_aggregator/src/common/graph/units.hpp
index dce223b30f728..f478fa9ef1a2a 100644
--- a/system/diagnostic_graph_aggregator/src/common/graph/units.hpp
+++ b/system/diagnostic_graph_aggregator/src/common/graph/units.hpp
@@ -15,120 +15,212 @@
#ifndef COMMON__GRAPH__UNITS_HPP_
#define COMMON__GRAPH__UNITS_HPP_
-#include "config.hpp"
+#include "names.hpp"
#include "types.hpp"
#include
-#include
#include
#include
-#include
-#include
#include
namespace diagnostic_graph_aggregator
{
+class UnitLink
+{
+public:
+ void initialize_object(BaseUnit * parent, BaseUnit * child);
+ void initialize_struct();
+ void initialize_status();
+ DiagLinkStruct create_struct() const { return struct_; }
+ DiagLinkStatus create_status() const { return status_; }
+ BaseUnit * parent() const { return parent_; }
+ BaseUnit * child() const { return child_; }
+
+private:
+ BaseUnit * parent_;
+ BaseUnit * child_;
+ DiagLinkStruct struct_;
+ DiagLinkStatus status_;
+};
+
class BaseUnit
{
public:
- struct NodeDict
- {
- std::unordered_map configs;
- std::unordered_map paths;
- };
- struct NodeData
- {
- DiagnosticLevel level;
- std::vector> links;
- };
- using UniquePtr = std::unique_ptr;
- using UniquePtrList = std::vector>;
-
- explicit BaseUnit(const std::string & path);
+ explicit BaseUnit(const UnitLoader & unit);
virtual ~BaseUnit() = default;
- virtual void init(const UnitConfig::SharedPtr & config, const NodeDict & dict) = 0;
- virtual void update(const rclcpp::Time & stamp) = 0;
+ virtual DiagnosticLevel level() const = 0;
+ virtual std::string path() const = 0;
virtual std::string type() const = 0;
-
- NodeData status() const;
- NodeData report() const;
- DiagnosticLevel level() const { return level_; }
-
- auto path() const { return path_; }
- auto children() const { return children_; }
-
+ virtual std::vector child_links() const = 0;
+ virtual bool is_leaf() const = 0;
size_t index() const { return index_; }
- void set_index(const size_t index) { index_ = index; }
+ size_t parent_size() const { return parents_.size(); }
protected:
- DiagnosticLevel level_;
- std::string path_;
- std::vector children_;
- std::vector> links_;
+ bool update();
private:
+ virtual void update_status() = 0;
size_t index_;
+ std::vector parents_;
+ std::optional prev_level_;
+};
+
+class NodeUnit : public BaseUnit
+{
+public:
+ explicit NodeUnit(const UnitLoader & unit);
+ void initialize_struct();
+ void initialize_status();
+ bool is_leaf() const override { return false; }
+ DiagNodeStruct create_struct() const { return struct_; }
+ DiagNodeStatus create_status() const { return status_; }
+ DiagnosticLevel level() const override { return status_.level; }
+ std::string path() const override { return struct_.path; }
+
+protected:
+ DiagNodeStruct struct_;
+ DiagNodeStatus status_;
};
-class DiagUnit : public BaseUnit
+class LeafUnit : public BaseUnit
{
public:
- using BaseUnit::BaseUnit;
- void init(const UnitConfig::SharedPtr & config, const NodeDict & dict) override;
- void update(const rclcpp::Time & stamp) override;
- std::string type() const override { return "diag"; }
+ explicit LeafUnit(const UnitLoader & unit);
+ void initialize_struct();
+ void initialize_status();
+ bool is_leaf() const override { return true; }
+ DiagLeafStruct create_struct() const { return struct_; }
+ DiagLeafStatus create_status() const { return status_; }
+ DiagnosticLevel level() const override { return status_.level; }
+ std::string name() const { return struct_.name; }
+ std::string path() const override { return struct_.path; }
- std::string name() const { return name_; }
- void callback(const rclcpp::Time & stamp, const DiagnosticStatus & status);
+protected:
+ DiagLeafStruct struct_;
+ DiagLeafStatus status_;
+};
+
+class DiagUnit : public LeafUnit
+{
+public:
+ explicit DiagUnit(const UnitLoader & unit);
+ std::string type() const override { return unit_name::diag; }
+ std::vector child_links() const override { return {}; }
+ bool on_time(const rclcpp::Time & stamp);
+ bool on_diag(const rclcpp::Time & stamp, const DiagnosticStatus & status);
private:
+ void update_status() override;
double timeout_;
- std::optional