Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI #66

Merged
merged 10 commits into from
Jun 23, 2024
Merged

UI #66

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ View the gripper in RViz:
ros2 launch robotiq_description view_gripper.launch.py
```

## Foxglove usage (user interface)

You can find the json configuration file in utils/ui/capra_ui.json. You need to load it into foxglove. On the device that you want to be connected (a.k.a. the jetson), you need to run the following command :


You can make it as a service and start at boot :

```bash
./utils/install.sh
```

Otherwise, to start use the user interface with a development laptop, you can run it by only launching the launchfile :

```bash
ros2 launch rove_launch_handler launch_handler.py
```


## Adding New Packages

To add a package for Rove, create it using the ROS2 command ([Creating Your First ROS2 Package](https://docs.ros.org/en/foxy/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html)). Name it starting with `rove_` to ensure Git tracking. For non-Rove specific packages, create a separate repository and add it to `rove.repos`.
Expand Down
29 changes: 29 additions & 0 deletions src/rove_launch_handler/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.5)
project(rove_launch_handler)

find_package(ament_cmake REQUIRED)
find_package(ament_cmake_python REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(rclpy REQUIRED)

# Convert message files to be used in python
rosidl_generate_interfaces(${PROJECT_NAME}
"srv/LaunchRequest.srv"
"srv/LaunchListRequest.srv"
)

# Install Python executables
install(PROGRAMS
scripts/launch_handler.py
DESTINATION lib/${PROJECT_NAME}
)

install(
DIRECTORY launch srv
DESTINATION share/${PROJECT_NAME}
)

ament_export_dependencies(rosidl_default_runtime)

ament_package()
25 changes: 25 additions & 0 deletions src/rove_launch_handler/launch/launch_handler.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import IncludeLaunchDescription
from launch_xml.launch_description_sources import XMLLaunchDescriptionSource
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
rosbridge_launch_file = os.path.join(
get_package_share_directory('foxglove_bridge'),
'launch',
'foxglove_bridge_launch.xml'
)

return LaunchDescription([
IncludeLaunchDescription(
XMLLaunchDescriptionSource(rosbridge_launch_file)
),
Node(
package='rove_launch_handler',
executable='launch_handler.py',
name='launch_handler',
output='screen'
)
])
25 changes: 25 additions & 0 deletions src/rove_launch_handler/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<package format="3">
<name>rove_launch_handler</name>
<version>0.0.0</version>
<description>The rove_launch_handler package</description>
<maintainer email="[email protected]">Capra</maintainer>
<license>MIT</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<build_depend>rclpy</build_depend>
<build_depend>rosidl_default_generators</build_depend>

<exec_depend>rclpy</exec_depend>
<exec_depend>rosidl_default_runtime</exec_depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

<member_of_group>rosidl_interface_packages</member_of_group>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Empty file.
99 changes: 99 additions & 0 deletions src/rove_launch_handler/scripts/launch_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env python3

import time
import rclpy
from rclpy.node import Node
import signal
import subprocess
import os
from rove_launch_handler.srv import LaunchRequest, LaunchListRequest

launched_files = {}

class LaunchFile:
def __init__(self, package, file_name, pid):
self.package = package
self.file_name = file_name
self.pid = pid

class LaunchMsg:
def __init__(self):
self.message = ""
self.is_launched = False
self.file_name = ""

def launch_file(package, file_name):
command = f"ros2 launch {package} {file_name}"

p = subprocess.Popen(command, shell=True, preexec_fn=os.setsid)

launch_msg = LaunchMsg()
# Sleep to make sure the launch command has time to fail if there's an error
time.sleep(1)
state = p.poll()
launch_msg.file_name = file_name
if state is None:
launch_msg.message = f"{file_name} was launched"
launched_files[file_name] = LaunchFile(package, file_name, p.pid)
launch_msg.is_launched = True
else:
launch_msg.message = f"{file_name} was not launched"
launch_msg.is_launched = False
return launch_msg

def kill_launch_file(file_name):
launch_msg = LaunchMsg()
launch_msg.file_name = file_name
if file_name in launched_files:
pid = launched_files[file_name].pid
try:
os.killpg(os.getpgid(pid), signal.SIGINT)
del launched_files[file_name]
launch_msg.message = f"{file_name} was killed"
launch_msg.is_launched = False
except ProcessLookupError as e:
launch_msg.message = f"Failed to kill {file_name}: {str(e)}"
launch_msg.is_launched = False
else:
launch_msg.message = f"{file_name} was not launched"
launch_msg.is_launched = False
return launch_msg

def kill_all():
for file_name in list(launched_files.keys()):
kill_launch_file(file_name)

class LaunchHandlerService(Node):
def __init__(self):
super().__init__('launch_handler_service')
self.srv_launch = self.create_service(LaunchRequest, 'launchHandler/launchFile', self.launch_callback)
self.srv_list = self.create_service(LaunchListRequest, 'launchHandler/getAllLaunchedFiles', self.get_launched_files)
self.get_logger().info("LaunchHandlerService node has been started.")

def launch_callback(self, request, response):
package = request.package
file_name = request.file_name
if file_name not in launched_files:
launch_msg = launch_file(package, file_name)
else:
launch_msg = kill_launch_file(file_name)
response.message = launch_msg.message
response.is_launched = launch_msg.is_launched
response.file_name = launch_msg.file_name
return response

def get_launched_files(self, request, response):
response.packages = [lf.package for lf in launched_files.values()]
response.files = list(launched_files.keys())
return response

def main(args=None):
rclpy.init(args=args)
node = LaunchHandlerService()
rclpy.get_default_context().on_shutdown(kill_all)
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

if __name__ == '__main__':
main()
13 changes: 13 additions & 0 deletions src/rove_launch_handler/scripts/launch_script.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Descript=Robot launch script
After=network.target

[Service]
Environment="ROS_LOG_DIR=/var/log/ros2; ROS_DOMAIN_ID=96"
ExecStart=/bin/bash -c 'source /home/simon/Workspace/capra/rove/install/setup.bash; ros2 launch rove_launch_handler launch_handler.launch.py;'
RemainAfterExit=no
Restart=on-failure
RestartSec=2s

[Install]
WantedBy=multi-user.target
3 changes: 3 additions & 0 deletions src/rove_launch_handler/srv/LaunchListRequest.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
string[] packages
string[] files
6 changes: 6 additions & 0 deletions src/rove_launch_handler/srv/LaunchRequest.srv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
string package
string file_name
---
string message
bool is_launched
string file_name
25 changes: 25 additions & 0 deletions src/rove_launch_handler/test/test_copyright.py
Original file line number Diff line number Diff line change
@@ -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'
25 changes: 25 additions & 0 deletions src/rove_launch_handler/test/test_flake8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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)
23 changes: 23 additions & 0 deletions src/rove_launch_handler/test/test_pep257.py
Original file line number Diff line number Diff line change
@@ -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'
7 changes: 7 additions & 0 deletions utils/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
sudo mkdir -p /var/log/ros2
sudo chmod 775 /var/log/ros2
sudo cp ./src/rove_launch_handler/scripts/launch_script.service /lib/systemd/system/launch_script.service
sudo systemctl daemon-reload
sudo systemctl enable launch_script.service
sudo systemctl start launch_script.service
Loading
Loading