Skip to content

Commit

Permalink
Add healthcheck + clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
rafal-gorecki committed Dec 3, 2024
1 parent 9c3307c commit a75745c
Show file tree
Hide file tree
Showing 26 changed files with 346 additions and 164 deletions.
15 changes: 5 additions & 10 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,11 @@ on:
workflow_dispatch:

jobs:
black:
name: Black
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Black
uses: psf/[email protected]
with:
options: --line-length=99
pre-commit:
name: Pre-commit
uses: ros-controls/ros2_control_ci/.github/workflows/reusable-pre-commit.yml@master
with:
ros_distro: humble

industrial_ci:
name: Industrial CI
Expand Down
57 changes: 37 additions & 20 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,49 @@ repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-merge-conflict
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-xml
- id: check-added-large-files
# mesh files has to be taken into account
args: [--maxkb=3000]
- id: check-ast
- id: check-json
exclude: ^.vscode/
- id: check-merge-conflict
- id: check-symlinks
- id: check-xml
- id: check-yaml
- id: debug-statements
- id: destroyed-symlinks
- id: detect-private-key
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: name-tests-test
files: ^.*\/test\/.*$
args: [--pytest-test-first]
- id: mixed-line-ending
- id: trailing-whitespace

- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
args: [--profile, black]

- repo: https://github.com/cheshirekow/cmake-format-precommit
rev: v0.6.13
hooks:
- id: cmake-format

- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v19.1.4
hooks:
- id: clang-format

- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
name: codespell
description: Checks for common misspellings in text files.
entry: codespell *
entry: codespell
exclude_types: [rst, svg]
language: python
types: [text]

Expand All @@ -42,14 +66,7 @@ repos:
rev: 7.1.1
hooks:
- id: flake8
args: ['--ignore=E501,W503'] # ignore too long line and line break before binary operator,
# black checks it

- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
args: [--profile, black]
args: ['--ignore=E501,W503']

- repo: local
hooks:
Expand All @@ -69,15 +86,15 @@ repos:
entry: ament_copyright
language: system

# Docs - RestructuredText hooks
- repo: https://github.com/PyCQA/doc8
rev: v1.1.2
hooks:
- id: doc8
args: [--max-line-length=100, --ignore=D001]
exclude: ^.*\/CHANGELOG\.rst/.*$

- repo: https://github.com/comkieffer/pre-commit-xmllint.git
rev: 1.0.0
- repo: https://github.com/tier4/pre-commit-hooks-ros
rev: v0.10.0
hooks:
- id: xmllint
- id: prettier-package-xml
- id: sort-package-xml
2 changes: 1 addition & 1 deletion docker/Dockerfile.simulation
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ RUN apt-get update && apt-get install -y \
ros-dev-tools && \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
rm -rf /var/lib/apt/lists/*
61 changes: 30 additions & 31 deletions rosbot_bringup/launch/bringup.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,54 +20,53 @@
LaunchConfiguration,
PathJoinSubstitution,
)
from launch_ros.actions import Node, SetParameter
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare


def generate_launch_description():
namespace = LaunchConfiguration("namespace")
use_sim = LaunchConfiguration("use_sim", default="False")

declare_healthcheck_arg = DeclareLaunchArgument(
"healthcheck",
default_value="True",
description="Check if all node are up and ready, if not emit shutdown signal.",
choices=["True", "true", "False", "false"],
)

declare_namespace_arg = DeclareLaunchArgument(
"namespace",
default_value=EnvironmentVariable("ROBOT_NAMESPACE", default_value=""),
description="Namespace for all topics and tfs",
)

use_sim = LaunchConfiguration("use_sim")
declare_use_sim_arg = DeclareLaunchArgument(
"use_sim",
default_value="False",
description="Whether simulation is used",
)

rosbot_controller = FindPackageShare("rosbot_controller")
rosbot_bringup = FindPackageShare("rosbot_bringup")

mecanum = LaunchConfiguration("mecanum")
declare_mecanum_arg = DeclareLaunchArgument(
"mecanum",
default_value="False",
description=(
"Whether to use mecanum drive controller (otherwise diff drive controller is used)"
),
)
rosbot_controller = FindPackageShare("rosbot_controller")

controller_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution(
[
rosbot_controller,
"launch",
"controller.launch.py",
]
)
PathJoinSubstitution([rosbot_controller, "launch", "controller.launch.py"])
),
launch_arguments={
"use_sim": use_sim,
"mecanum": mecanum,
"namespace": namespace,
"use_sim": use_sim,
}.items(),
)

healthcheck_launch = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
PathJoinSubstitution([rosbot_bringup, "launch", "healthcheck.launch.py"])
)
)

# microros_launch = IncludeLaunchDescription(
# PythonLaunchDescriptionSource(
# PathJoinSubstitution([rosbot_bringup, "launch", "microros.launch.py"])
# ),
# condition=UnlessCondition([use_sim]),
# )

ekf_config = PathJoinSubstitution([rosbot_bringup, "config", "ekf.yaml"])

robot_localization_node = Node(
Expand Down Expand Up @@ -97,13 +96,13 @@ def generate_launch_description():
)

actions = [
declare_healthcheck_arg,
declare_namespace_arg,
declare_mecanum_arg,
declare_use_sim_arg,
SetParameter(name="use_sim_time", value=use_sim),
controller_launch,
robot_localization_node,
healthcheck_launch,
# microros_launch,
laser_filter_node,
robot_localization_node,
]

return LaunchDescription(actions)
98 changes: 98 additions & 0 deletions rosbot_bringup/launch/healthcheck.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2020 ros2_control Development Team
# Copyright 2024 Husarion sp. z o.o.
#
# 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 os
import subprocess

from launch import LaunchDescription
from launch.actions import EmitEvent, OpaqueFunction, TimerAction
from launch.events import Shutdown
from launch.substitutions import LaunchConfiguration


def check_controller_status(context):
healthcheck = LaunchConfiguration("healthcheck").perform(context)
if healthcheck.lower() == "false":
with open("/var/tmp/rosbot_status.txt", "w") as status_file:
status_file.write("healthy")
return
else:
with open("/var/tmp/rosbot_status.txt", "w") as status_file:
status_file.write("unhealthy")

use_sim = LaunchConfiguration("use_sim", default="False").perform(context)
nodes_to_check = [
"/controller_manager",
"/ekf_filter_node",
"/imu_broadcaster",
"/joint_state_broadcaster",
"/laser_scan_box_filter",
"/robot_state_publisher",
"/rosbot_base_controller",
"/scan_to_scan_filter_chain",
]
if use_sim.lower() == "true":
additional_nodes = [
"/gz_ros2_control",
"/ros_gz_bridge",
]
else:
additional_nodes = [
"/imu_sensor_node",
# "/rosbot_ros2_firmware", not visible via USB port
"/rosbot_system_node",
]
nodes_to_check.extend(additional_nodes)

namespace = LaunchConfiguration("namespace", default="").perform(context)
ns = "/" + namespace if namespace else ""
nodes_to_check = [ns + node for node in nodes_to_check]

def get_missing_nodes():
env = os.environ.copy()
env["ROS_LOCALHOST_ONLY"] = "1"

result = subprocess.run(
["ros2", "node", "list"],
stdout=subprocess.PIPE,
text=True,
env=env,
)
active_nodes = result.stdout.splitlines()
return [node for node in nodes_to_check if node not in active_nodes]

missing_nodes = get_missing_nodes()
green_color = "\033[92m"
red_color = "\033[91m"
reset_color = "\033[0m"

if missing_nodes:
print(
f"{red_color}Error: some nodes are missing: {missing_nodes}. Emitting shutdown...{reset_color}"
)
return [EmitEvent(event=Shutdown())]
else:
print(f"{green_color}All systems are up and running!{reset_color}")
with open("/var/tmp/rosbot_status.txt", "w") as status_file:
status_file.write("healthy")


def generate_launch_description():
check_controller = TimerAction(
period=15.0,
actions=[OpaqueFunction(function=check_controller_status)],
)

return LaunchDescription([check_controller])
Loading

0 comments on commit a75745c

Please sign in to comment.