diff --git a/.github/workflows/industrial_ci.yaml b/.github/workflows/industrial_ci.yaml index 29bb70c..35c94d6 100644 --- a/.github/workflows/industrial_ci.yaml +++ b/.github/workflows/industrial_ci.yaml @@ -49,6 +49,9 @@ jobs: cp -r src/ros2_controllers/diff_drive_controller src/ cp -r src/ros2_controllers/imu_sensor_broadcaster src/ rm -rf src/ros2_controllers + # Package micro_ros_msgs does not have industrial ci and tests does not pass. + # For more information see https://github.com/micro-ROS/micro_ros_msgs/issues/7 + sed '/if(BUILD_TESTING)/,/endif()/d' src/micro_ros_msgs/CMakeLists.txt -i - uses: ros-industrial/industrial_ci@master env: diff --git a/.gitignore b/.gitignore index 84aaaf8..4d56f66 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ rosbot_hardware_interfaces/ ros_components_description/ rosbot_controllers/ husarion/husarion_office_gz +micro-ROS-Agent/ +micro_ros_msgs/ +industrial_ci/ ros2_controllers/ diff_drive_controller/ imu_sensor_broadcaster/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d24e258..325294f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -76,6 +76,3 @@ repos: rev: 1.0.0 hooks: - id: xmllint - args: - - --schema - - http://download.ros.org/schema/package_format3.xsd diff --git a/.wordlist.txt b/.wordlist.txt index 61bb2de..0a7ac43 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -70,6 +70,21 @@ utils tf yaml odometry +pyftdi +usbutils +CustomUdpTransport +UDPv +SHM +UDPv +laggy +CBUS +cbus +ftdi +RST +url +subprocess +Ftdi +Microros namespaces namespace delihus diff --git a/README.md b/README.md index 413e593..7fda75c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Metapackage that contains dependencies to other repositories. It is also used to ### `rosbot_xl_bringup` -Package that contains launch, which starts all base functionalities. Also configs for `robot_localization` and `laser_filters` are defined there. +Package that contains launch, which starts all base functionalities with the microros agent. Also configs for `robot_localization` and `laser_filters` are defined there. ### `rosbot_xl_description` @@ -32,6 +32,10 @@ Launch files for Ignition Gazebo working with ROS2 control. ROS2 hardware controller for ROSbot XL. Inputs and outputs data from ROS2 control and forwards it via ROS topic to be read by microros. +### `rosbot_xl_utils` + +This package contains the stable firmware version with the flash script. + ## ROS API Available in [ROS_API.md](./ROS_API.md) @@ -67,6 +71,9 @@ source /opt/ros/$ROS_DISTRO/setup.bash vcs import src < src/rosbot_xl/rosbot_xl_hardware.repos +# Remove tests from micro_ros_msgs +sed '/if(BUILD_TESTING)/,/endif()/d' src/micro_ros_msgs/CMakeLists.txt -i + rm -r src/rosbot_xl_gazebo # Copy only diff_drive_controller and imu_sensor_broadcaster, waits for features from ros2-control @@ -74,6 +81,9 @@ cp -r src/ros2_controllers/diff_drive_controller src/ cp -r src/ros2_controllers/imu_sensor_broadcaster src/ rm -rf src/ros2_controllers +# stm32flash is not in the ros index and should be installed manually +sudo apt install stm32flash + rosdep init rosdep update --rosdistro $ROS_DISTRO rosdep install -i --from-path src --rosdistro $ROS_DISTRO -y @@ -84,9 +94,19 @@ colcon build > Before starting the software on the robot please make sure that you're using the latest firmware and run the `micro-ROS` agent as described in the [Usage on hardware](#usage-on-hardware) step. 2. **Running** + +Flash firmware. +```bash +# Get admin permissions to flash firmware +sudo su +source install/setup.bash +ros2 run rosbot_xl_utils flash_firmware +exit +``` + ``` source install/setup.bash -ros2 launch rosbot_xl_bringup bringup.launch.py +ros2 launch rosbot_xl_bringup combined.launch.py ``` ### Build and run Gazebo simulation @@ -100,6 +120,9 @@ source /opt/ros/$ROS_DISTRO/setup.bash vcs import src < src/rosbot_xl/rosbot_xl_hardware.repos vcs import src < src/rosbot_xl/rosbot_xl_simulation.repos +# Remove tests from micro_ros_msgs +sed '/if(BUILD_TESTING)/,/endif()/d' src/micro_ros_msgs/CMakeLists.txt -i + # Copy only diff_drive_controller and imu_sensor_broadcaster, waits for features from ros2-control cp -r src/ros2_controllers/diff_drive_controller src/ cp -r src/ros2_controllers/imu_sensor_broadcaster src/ diff --git a/rosbot_xl/rosbot_xl_hardware.repos b/rosbot_xl/rosbot_xl_hardware.repos index 7803e1a..e7f687a 100644 --- a/rosbot_xl/rosbot_xl_hardware.repos +++ b/rosbot_xl/rosbot_xl_hardware.repos @@ -7,6 +7,14 @@ repositories: type: git url: https://github.com/husarion/ros_components_description.git version: ros2 + micro_ros_msgs: + type: git + url: https://github.com/micro-ROS/micro_ros_msgs.git + version: humble + micro-ROS-Agent: + type: git + url: https://github.com/micro-ROS/micro-ROS-Agent.git + version: humble rosbot_controllers: type: git url: https://github.com/husarion/rosbot_controllers diff --git a/rosbot_xl_bringup/config/microros_localhost_only.xml b/rosbot_xl_bringup/config/microros_localhost_only.xml new file mode 100644 index 0000000..a0c4948 --- /dev/null +++ b/rosbot_xl_bringup/config/microros_localhost_only.xml @@ -0,0 +1,26 @@ + + + + + + + CustomUdpTransport + UDPv4 + +
127.0.0.1
+
+
+
+ + + + + CustomUdpTransport + + + false + + +
+
diff --git a/rosbot_xl_bringup/launch/combined.launch.py b/rosbot_xl_bringup/launch/combined.launch.py new file mode 100644 index 0000000..597e0a6 --- /dev/null +++ b/rosbot_xl_bringup/launch/combined.launch.py @@ -0,0 +1,166 @@ +# Copyright 2024 Husarion +# +# 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. + +from launch import LaunchDescription +from launch_ros.substitutions import FindPackageShare +from launch.actions import ( + IncludeLaunchDescription, + DeclareLaunchArgument, + LogInfo, + SetEnvironmentVariable, + OpaqueFunction, +) +from launch.launch_description_sources import PythonLaunchDescriptionSource +from launch.substitutions import ThisLaunchFileDir, LaunchConfiguration +from launch_ros.actions import Node +import os + + +def generate_microros_agent_node(context, *args, **kwargs): + env_setup_actions = [] + + ros_domain_id = os.environ.get("ROS_DOMAIN_ID") + if ros_domain_id: + env_setup_actions.append( + SetEnvironmentVariable(name="XRCE_DOMAIN_ID_OVERRIDE", value=ros_domain_id) + ) + + port = LaunchConfiguration("port").perform(context) + + localhost_only_fastrtps_profiles_file = LaunchConfiguration( + "localhost_only_fastrtps_profiles_file" + ).perform(context) + + if os.environ.get("ROS_LOCALHOST_ONLY") == "1": + env_setup_actions.extend( + [ + LogInfo( + msg=[ + "ROS_LOCALHOST_ONLY set to 1. Using FASTRTPS_DEFAULT_PROFILES_FILE=", + localhost_only_fastrtps_profiles_file, + ".", + ] + ), + SetEnvironmentVariable(name="RMW_IMPLEMENTATION", value="rmw_fastrtps_cpp"), + SetEnvironmentVariable( + name="FASTRTPS_DEFAULT_PROFILES_FILE", + value=localhost_only_fastrtps_profiles_file, + ), + ] + ) + + microros_agent_node = Node( + package="micro_ros_agent", + executable="micro_ros_agent", + arguments=["udp4", "--port", port], + output="screen", + ) + + return env_setup_actions + [microros_agent_node] + + +def generate_launch_description(): + declare_port_arg = DeclareLaunchArgument( + "port", + default_value="8888", + description="UDP4 port for micro-ROS agent", + ) + + mecanum = LaunchConfiguration("mecanum") + declare_mecanum_arg = DeclareLaunchArgument( + "mecanum", + default_value="False", + description=( + "Whether to use mecanum drive controller (otherwise diff drive controller is used)" + ), + ) + + camera_model = LaunchConfiguration("camera_model") + declare_camera_model_arg = DeclareLaunchArgument( + "camera_model", + default_value="None", + description="Add camera model to the robot URDF", + choices=[ + "None", + "intel_realsense_d435", + "orbbec_astra", + "stereolabs_zed", + "stereolabs_zedm", + "stereolabs_zed2", + "stereolabs_zed2i", + "stereolabs_zedx", + "stereolabs_zedxm", + ], + ) + + lidar_model = LaunchConfiguration("lidar_model") + declare_lidar_model_arg = DeclareLaunchArgument( + "lidar_model", + default_value="slamtec_rplidar_s1", + description="Add LiDAR model to the robot URDF", + choices=[ + "None", + "slamtec_rplidar_a2", + "slamtec_rplidar_a3", + "slamtec_rplidar_s1", + "slamtec_rplidar_s2", + "slamtec_rplidar_s3", + "velodyne_puck", + ], + ) + + include_camera_mount = LaunchConfiguration("include_camera_mount") + declare_include_camera_mount_arg = DeclareLaunchArgument( + "include_camera_mount", + default_value="False", + description="Whether to include camera mount to the robot URDF", + ) + + # Locate the rosbot_bringup package + package_dir = FindPackageShare("rosbot_xl_bringup").find("rosbot_xl_bringup") + + # Construct the path to the XML file within the package + fastrtps_profiles_file = os.path.join(package_dir, "config", "microros_localhost_only.xml") + + declare_localhost_only_fastrtps_profiles_file_arg = DeclareLaunchArgument( + "localhost_only_fastrtps_profiles_file", + default_value=fastrtps_profiles_file, + description=( + "Path to the Fast RTPS default profiles file for Micro-ROS agent for localhost only" + " setup" + ), + ) + + bringup_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource([ThisLaunchFileDir(), "/bringup.launch.py"]), + launch_arguments={ + "mecanum": mecanum, + "lidar_model": lidar_model, + "camera_model": camera_model, + "include_camera_mount": include_camera_mount, + }.items(), + ) + + return LaunchDescription( + [ + declare_port_arg, + declare_localhost_only_fastrtps_profiles_file_arg, + declare_mecanum_arg, + declare_camera_model_arg, + declare_lidar_model_arg, + declare_include_camera_mount_arg, + OpaqueFunction(function=generate_microros_agent_node), + bringup_launch, + ] + ) diff --git a/rosbot_xl_bringup/package.xml b/rosbot_xl_bringup/package.xml index 1a155d7..c6ac2a5 100644 --- a/rosbot_xl_bringup/package.xml +++ b/rosbot_xl_bringup/package.xml @@ -27,6 +27,10 @@ laser_filters robot_localization + micro_ros_agent + + + git rclpy python3-pytest diff --git a/rosbot_xl_bringup/setup.py b/rosbot_xl_bringup/setup.py index 1e6d8d3..8533fd3 100644 --- a/rosbot_xl_bringup/setup.py +++ b/rosbot_xl_bringup/setup.py @@ -27,6 +27,7 @@ ("share/" + package_name, ["package.xml"]), (os.path.join("share", package_name, "launch"), glob("launch/*.launch.py")), (os.path.join("share", package_name, "config"), glob("config/*.yaml")), + (os.path.join("share", package_name, "config"), glob("config/*.xml")), ], install_requires=["setuptools"], zip_safe=True, diff --git a/rosbot_xl_utils/firmware/firmware-v1.4.0.bin b/rosbot_xl_utils/firmware/firmware-v1.4.0.bin new file mode 100644 index 0000000..5a0d450 Binary files /dev/null and b/rosbot_xl_utils/firmware/firmware-v1.4.0.bin differ diff --git a/rosbot_xl_utils/package.xml b/rosbot_xl_utils/package.xml new file mode 100644 index 0000000..38c57d4 --- /dev/null +++ b/rosbot_xl_utils/package.xml @@ -0,0 +1,33 @@ + + + + rosbot_xl_utils + 0.8.12 + Utilities for ROSbot XL + + Husarion + + Apache License 2.0 + + https://husarion.com/ + https://github.com/husarion/rosbot_xl_ros + https://github.com/husarion/rosbot_xl_ros/issues + + Jakub Delicat + + python3-sh + python-periphery-pip + python3-pyftdi-pip + usbutils + python3-serial + python3-requests + + ament_copyright + ament_flake8 + ament_pep257 + python3-pytest + + + ament_python + + diff --git a/rosbot_xl_utils/resource/rosbot_xl_utils b/rosbot_xl_utils/resource/rosbot_xl_utils new file mode 100644 index 0000000..e69de29 diff --git a/rosbot_xl_utils/rosbot_xl_utils/__init__.py b/rosbot_xl_utils/rosbot_xl_utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rosbot_xl_utils/rosbot_xl_utils/flash-firmware.py b/rosbot_xl_utils/rosbot_xl_utils/flash-firmware.py new file mode 100644 index 0000000..a13e366 --- /dev/null +++ b/rosbot_xl_utils/rosbot_xl_utils/flash-firmware.py @@ -0,0 +1,99 @@ +#!/usr/bin/python3 + +# 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 sh +import time +import sys +import argparse +from pyftdi.ftdi import Ftdi + +# CBUS0 - BOOT0 +# CBUS1 - RST + + +class FirmwareFlasher: + def __init__(self, binary_file, port): + self.device = "ftdi://ftdi:ft-x:/1" + self.ftdi = Ftdi() + + self.binary_file = binary_file + self.max_approach_no = 5 + self.port = port + + def enter_bootloader_mode(self): + self.ftdi.open_from_url(url=self.device) + self.ftdi.set_cbus_direction(0b11, 0b11) # set CBUS0 and CBUS1 to output + time.sleep(0.1) + self.ftdi.set_cbus_gpio(0b11) # set CBUS0 to 1 and RST to 1 + time.sleep(0.1) + self.ftdi.set_cbus_gpio(0b01) # set CBUS0 to 1 and RST to 0 + time.sleep(0.1) + # self.ftdi.set_cbus_direction(0b11,0b00) # set CBUS0 and CBUS1 to input + time.sleep(0.1) + self.ftdi.close() + + def exit_bootloader_mode(self): + self.ftdi.open_from_url(url=self.device) + self.ftdi.set_cbus_direction(0b11, 0b11) # set CBUS0 and CBUS1 to output + time.sleep(0.1) + self.ftdi.set_cbus_gpio(0b10) # set CBUS0 to 1 and RST to 1 + time.sleep(0.1) + self.ftdi.set_cbus_gpio(0b00) # set CBUS0 to 1 and RST to 0 + time.sleep(0.1) + # self.ftdi.set_cbus_direction(0b11,0b00) # set CBUS0 and CBUS1 to input + time.sleep(0.1) + self.ftdi.close() + + def flash_firmware(self): + self.enter_bootloader_mode() + sh.usbreset("0403:6015") + # workaround: using pyftdi causes laggy serial port. + # This line is like unplug/plug for USB port + sh.stm32flash(self.port, "-v", w=self.binary_file, b="115200", _out=sys.stdout) + self.exit_bootloader_mode() + sh.usbreset("0403:6015") + + +def main(): + parser = argparse.ArgumentParser( + description="Flashing the firmware on STM32 microcontroller in ROSbot XL" + ) + + parser.add_argument( + "-f", + "--file", + nargs="?", + default="/firmware.bin", + help="Path to a firmware file. Default: /firmware.bin", + ) + parser.add_argument( + "-p", + "--port", + nargs="?", + default="/dev/ttyUSB0", + help="Path to serial connection. Default: /dev/ttyUSB0", + ) + + binary_file = parser.parse_args().file + port = parser.parse_args().port + + flasher = FirmwareFlasher(binary_file, port) + flasher.flash_firmware() + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/rosbot_xl_utils/rosbot_xl_utils/flash_firmware.py b/rosbot_xl_utils/rosbot_xl_utils/flash_firmware.py new file mode 100644 index 0000000..ab736fc --- /dev/null +++ b/rosbot_xl_utils/rosbot_xl_utils/flash_firmware.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +# 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 subprocess +import os +import sys +import argparse +import signal +import glob +import requests +import ament_index_python.packages + +# Global variable to hold the subprocess reference +subproc = None +firmware_version = "v1.4.0" + + +def signal_handler(sig, frame): + global subproc + if subproc: + print("Terminating the flashing process...") + subproc.terminate() + sys.exit(0) + + +def download_firmware(firmware_url, firmware_path): + response = requests.get(firmware_url, allow_redirects=True) + if response.status_code == 200: + with open(firmware_path, "wb") as f: + f.write(response.content) + print("Firmware downloaded successfully.") + else: + raise Exception(f"Failed to download firmware: HTTP {response.status_code}") + + +def find_firmware_file(): + # Find the install directory of 'rosbot_utils' package + package_install_directory = ament_index_python.packages.get_package_share_directory( + "rosbot_xl_utils" + ) + + # Construct the path to the firmware directory + firmware_dir = os.path.join(package_install_directory, "firmware") + firmware_files = glob.glob(os.path.join(firmware_dir, f"firmware-{firmware_version}.bin")) + + if not firmware_files: + firmware_url = ( + "https://github.com/husarion/rosbot_xl_firmware/releases/" + f"download/{firmware_version}/firmware.bin" + ) + firmware_path = os.path.join(firmware_dir, f"firmware-{firmware_version}.bin") + print("Downloading firmware...") + download_firmware(firmware_url, firmware_path) + return firmware_path + + return firmware_files[0] # return the first found firmware file + + +def main(args=None): + global subproc + + # Setting up the signal handler + signal.signal(signal.SIGINT, signal_handler) + + parser = argparse.ArgumentParser(description="Flash Firmware ROS 2 Node") + parser.add_argument( + "-p", + "--port", + default="/dev/ttyUSB0", + help="Specify the USB port (default: /dev/ttyUSB0)", + ) + parser.add_argument("--file", help="Specify the firmware file") + + args = parser.parse_args(args) + + # Determine the firmware file to use + firmware_file = args.file if args.file else find_firmware_file() + + try: + script_name = "flash-firmware.py" + + script_path = os.path.join(os.path.dirname(__file__), script_name) + additional_args = ["-p", args.port, "--file", firmware_file] + + # Print the flashing details + print(f"Flashing {firmware_file} over {args.port}") + + # # Starting the subprocess + subproc = subprocess.Popen([sys.executable, script_path] + additional_args) + return_code = subproc.wait() # Wait for the subprocess to finish and get the return code + + if return_code != 0: + print(f"Firmware flashing failed with return code {return_code}") + else: + print("Firmware flashing completed successfully.") + except subprocess.CalledProcessError as e: + print(f"Error during firmware flashing: {e}") + + +if __name__ == "__main__": + main() diff --git a/rosbot_xl_utils/setup.cfg b/rosbot_xl_utils/setup.cfg new file mode 100644 index 0000000..e883952 --- /dev/null +++ b/rosbot_xl_utils/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script_dir=$base/lib/rosbot_xl_utils +[install] +install_scripts=$base/lib/rosbot_xl_utils diff --git a/rosbot_xl_utils/setup.py b/rosbot_xl_utils/setup.py new file mode 100644 index 0000000..19226e3 --- /dev/null +++ b/rosbot_xl_utils/setup.py @@ -0,0 +1,40 @@ +# 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 +from glob import glob +from setuptools import find_packages, setup + +package_name = "rosbot_xl_utils" + +setup( + name=package_name, + version="0.0.0", + packages=find_packages(exclude=["test"]), + data_files=[ + ("share/ament_index/resource_index/packages", ["resource/" + package_name]), + ("share/" + package_name, ["package.xml"]), + (os.path.join("share", package_name, "firmware"), glob("firmware/*.bin")), + ], + install_requires=["setuptools"], + zip_safe=True, + maintainer="Husarion", + maintainer_email="support@husarion.com", + description="Utilities for ROSbot XL", + license="Apache License 2.0", + tests_require=["pytest"], + entry_points={ + "console_scripts": ["flash_firmware = rosbot_xl_utils.flash_firmware:main"], + }, +) diff --git a/rosbot_xl_utils/test/test_copyright.py b/rosbot_xl_utils/test/test_copyright.py new file mode 100644 index 0000000..95f0381 --- /dev/null +++ b/rosbot_xl_utils/test/test_copyright.py @@ -0,0 +1,25 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# 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. + +from ament_copyright.main import main +import pytest + + +# Remove the `skip` decorator once the source file(s) have a copyright header +@pytest.mark.skip(reason="No copyright header has been placed in the generated source file.") +@pytest.mark.copyright +@pytest.mark.linter +def test_copyright(): + rc = main(argv=[".", "test"]) + assert rc == 0, "Found errors" diff --git a/rosbot_xl_utils/test/test_flake8.py b/rosbot_xl_utils/test/test_flake8.py new file mode 100644 index 0000000..49c1644 --- /dev/null +++ b/rosbot_xl_utils/test/test_flake8.py @@ -0,0 +1,23 @@ +# Copyright 2017 Open Source Robotics Foundation, Inc. +# +# 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. + +from ament_flake8.main import main_with_errors +import pytest + + +@pytest.mark.flake8 +@pytest.mark.linter +def test_flake8(): + rc, errors = main_with_errors(argv=[]) + assert rc == 0, "Found %d code style errors / warnings:\n" % len(errors) + "\n".join(errors) diff --git a/rosbot_xl_utils/test/test_pep257.py b/rosbot_xl_utils/test/test_pep257.py new file mode 100644 index 0000000..a2c3deb --- /dev/null +++ b/rosbot_xl_utils/test/test_pep257.py @@ -0,0 +1,23 @@ +# Copyright 2015 Open Source Robotics Foundation, Inc. +# +# 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. + +from ament_pep257.main import main +import pytest + + +@pytest.mark.linter +@pytest.mark.pep257 +def test_pep257(): + rc = main(argv=[".", "test"]) + assert rc == 0, "Found code style errors / warnings"