From 352126327b23cc011b3ad164d68051c546a5c8d3 Mon Sep 17 00:00:00 2001 From: James Pace Date: Tue, 1 Sep 2026 21:48:49 -0400 Subject: [PATCH] Move from other project. --- CMakeLists.txt | 13 +++++++ config/ROUTER_CONFIG.json5 | 64 +++++++++++++++++++++++++++++++ config/SESSION_CONFIG.json5 | 72 +++++++++++++++++++++++++++++++++++ config/setup.bash | 23 +++++++++++ launch/zenoh_router.launch.py | 64 +++++++++++++++++++++++++++++++ package.xml | 15 ++++++++ 6 files changed, 251 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 config/ROUTER_CONFIG.json5 create mode 100644 config/SESSION_CONFIG.json5 create mode 100755 config/setup.bash create mode 100644 launch/zenoh_router.launch.py create mode 100644 package.xml diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..fdc448b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 4.2) +project(j7s_ros_common) + +# find dependencies +find_package(ament_cmake REQUIRED) + +# Install launch and param files. +install(DIRECTORY + launch config + DESTINATION share/${PROJECT_NAME}/ +) + +ament_package() diff --git a/config/ROUTER_CONFIG.json5 b/config/ROUTER_CONFIG.json5 new file mode 100644 index 0000000..d425fa6 --- /dev/null +++ b/config/ROUTER_CONFIG.json5 @@ -0,0 +1,64 @@ +{ + /// The node's mode (router, peer or client) + mode: "router", + + /// Which endpoints to connect to. E.g. tcp/localhost:7447. + /// This should list all the routers for this robot. + connect: { + endpoints: [ + ], + }, + + /// Which endpoints to listen on. E.g. tcp/0.0.0.0:7447. + listen: { + endpoints: [ + "tcp/0.0.0.0:7447", + ], + }, + /// Configure the scouting mechanisms and their behaviours + scouting: { + /// Disable multicast. + multicast: { + /// Whether multicast scouting is enabled or not + enabled: false, + }, + /// Enable gossip scouting. + gossip: { + /// Whether gossip scouting is enabled or not + enabled: true, + }, + }, + + /// Configuration of data messages timestamps management. + timestamping: { + /// ROS setting: PublicationCache which is required for transient_local durability + /// only works when time-stamping is enabled. + enabled: true, + }, + + /// Configure internal transport parameters + transport: { + unicast: { + compression: { + enabled: true, + }, + }, + link: { + tx: { + queue: { + congestion_control: { + drop: { + /// The maximum time in microseconds to wait for an available batch (place in queue) before dropping + /// a droppable message if still no batch is available. + wait_before_drop: 100000, + }, + }, + }, + }, + }, + /// Shared memory configuration. + shared_memory: { + enabled: false, + }, + }, +} \ No newline at end of file diff --git a/config/SESSION_CONFIG.json5 b/config/SESSION_CONFIG.json5 new file mode 100644 index 0000000..17fd660 --- /dev/null +++ b/config/SESSION_CONFIG.json5 @@ -0,0 +1,72 @@ +{ + /// The node's mode (router, peer or client) + mode: "peer", + + /// Which endpoints to connect to. E.g. tcp/localhost:7447. + /// By configuring the endpoints, it is possible to tell zenoh which router/peer to connect to at startup. + /// We only connect our local router at first. + connect: { + endpoints: [ + "tcp/localhost:7447", + ], + retry: { + period_init_ms: 100, + period_max_ms: 10000 + }, + }, + + /// Which endpoints to listen on. E.g. tcp/0.0.0.0:7447. + /// By configuring the endpoints, it is possible to tell zenoh which are the endpoints that other routers, + /// peers, or client can use to establish a zenoh session. + /// "tcp/[::]:0", "udp/0.0.0.0:0?rel=1;mixed_rel=1" + listen: { + endpoints: [ + "tcp/0.0.0.0:0", "udp/0.0.0.0:0?rel=1;mixed_rel=1" + ], + }, + + /// Configure the scouting mechanisms and their behaviours + scouting: { + /// Disable multicast. + multicast: { + enabled: false, + }, + /// Enable gossip. + gossip: { + enabled: true, + }, + }, + + /// Configuration of data messages timestamps management. + timestamping: { + /// ROS setting: PublicationCache which is required for transient_local durability + /// only works when time-stamping is enabled. + enabled: true, + }, + + /// Configure internal transport parameters + transport: { + unicast: { + compression: { + enabled: true, + }, + }, + link: { + tx: { + queue: { + congestion_control: { + drop: { + /// The maximum time in microseconds to wait for an available batch (place in queue) before dropping + /// a droppable message if still no batch is available. + wait_before_drop: 100000, + }, + }, + }, + }, + }, + /// Shared memory configuration. + shared_memory: { + enabled: false, + }, + }, +} \ No newline at end of file diff --git a/config/setup.bash b/config/setup.bash new file mode 100755 index 0000000..cd7f22c --- /dev/null +++ b/config/setup.bash @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +export RMW_IMPLEMENTATION=rmw_zenoh_cpp +export ZENOH_ROUTER_CHECK_ATTEMPTS=-1 +export ZENOH_ROUTER_CONFIG_URI=${SCRIPT_DIR}/ROUTER_CONFIG.json5 +export ZENOH_SESSION_CONFIG_URI=${SCRIPT_DIR}/SESSION_CONFIG.json5 +export ROS_STATIC_PEERS="" + +# This can be called to setup which routers to connect to. +# I.E. On my laptop I source this config. I want to connect to +# a robot with IP address 192.168.1.100 +# I can call `zenoh_router_ip 192.168.1.100` and then I will talk to its +# router. +zenoh_router_ip() { + local endpoints=() + for ip in "$@"; do + endpoints+=("'tcp/$ip:7447'") + done + local IFS=, + export ZENOH_CONFIG_OVERRIDE="connect/endpoints=[${endpoints[*]}];" +} + diff --git a/launch/zenoh_router.launch.py b/launch/zenoh_router.launch.py new file mode 100644 index 0000000..974d044 --- /dev/null +++ b/launch/zenoh_router.launch.py @@ -0,0 +1,64 @@ +import launch +import launch_ros.actions +import launch.substitutions +import launch.substitution +from launch.some_substitutions_type import SomeSubstitutionsType +import launch.conditions + +import socket + +def generate_launch_description(): + nodes = [] + nodes.append(make_router()) + return launch.LaunchDescription(nodes) + +def setup_router_env(): + # In theory you can use this to set up the environment on the router + # in a unique way. + # I tried something like wat you see below at one point, though + # it didn't pan out. + # I'm just goint to leave it as an example here. + #zenoh_runtime_settings = "( rx: (worker_threads: 2), acc: (worker_threads: 2), tx: (worker_threads: 2), app: (worker_threads: 2) )" + #env = {"ZENOH_RUNTIME": zenoh_runtime_settings} + env = None + return env + +def make_router(): + zenoh_is_rmw = launch.substitutions.EqualsSubstitution( + 'rmw_zenoh_cpp', + launch.substitutions.EnvironmentVariable('RMW_IMPLEMENTATION', default_value='') + ) + router_port_is_free = PortIsFree('7447') + + condition = launch.conditions.IfCondition( + launch.substitutions.AndSubstitution(zenoh_is_rmw, router_port_is_free) + ) + node = launch_ros.actions.Node( + package = "rmw_zenoh_cpp", + executable="rmw_zenohd", + name="zenoh_router", + condition=condition, + additional_env=setup_router_env() + ) + return node + +###### +# Custom actions/substitutions +###### + +class PortIsFree(launch.substitution.Substitution): + def __init__(self, port: SomeSubstitutionsType): + super().__init__() + self._port = port + + def perform(self, context): + port_as_string = self._port if isinstance(self._port, str) else self._port.perform(context) + port_as_int = int(port_as_string) + + if is_port_in_use_for_tcp(port_as_int): + return "False" + return "True" + +def is_port_in_use_for_tcp(port): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + return sock.connect_ex(('localhost', port)) == 0 diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..afacc71 --- /dev/null +++ b/package.xml @@ -0,0 +1,15 @@ + + + + j7s_ros_common + 0.0.0 + TODO: Package description + jimmy + TODO: License declaration + + ament_cmake + + + ament_cmake + +