├── .bazelrc ├── .github ├── CODEOWNERS └── workflows │ └── ubuntu-build.yml ├── .gitignore ├── BUILD.bazel ├── CMakeLists.txt ├── LICENSE ├── README.md ├── WORKSPACE ├── cmake └── dependencies │ └── CMakeLists.txt ├── gz-msgs9.BUILD ├── include ├── BUILD └── gz │ └── msgs │ └── extras.hh ├── python ├── BUILD ├── gz_service_info.py ├── gz_service_list.py ├── gz_topic_echo.py ├── gz_topic_info.py ├── gz_topic_list.py ├── import_check.py ├── msgs_example.py ├── pub_all_msg_types.py ├── publisher.py ├── rover_publisher.py ├── rover_subscriber.py ├── subscriber.py └── transport_example.py └── src ├── deps_check.cc ├── gz_topic_echo.cc ├── main.cc ├── msg_example.cc ├── publisher.cc ├── pybind11 └── gz │ ├── msgs │ ├── _gz_msgs_extras_pybind11.cc │ └── extras.cc │ └── transport │ └── _gz_transport_pybind11.cc ├── rover_publisher.cc ├── rover_subscriber.cc └── subscriber.cc /.bazelrc: -------------------------------------------------------------------------------- 1 | # Enable logging rc options. 2 | common --announce_rc 3 | 4 | # Enable verbose failures for testing only. 5 | build --verbose_failures 6 | 7 | # Compiler options. 8 | build --cxxopt=-std=c++17 9 | build --host_cxxopt=-std=c++17 10 | 11 | # Enable logging error output. 12 | test --test_output=errors 13 | test --test_summary=detailed 14 | 15 | # Enable use_fast_cpp_protos when using C++ implementation and py_proto_library 16 | build --define use_fast_cpp_protos=false 17 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS 2 | # 3 | # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 4 | 5 | # These owners will be the default owners for everything in 6 | # the repo. Unless a later match takes precedence. 7 | * @srmainwaring 8 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu-build.yml: -------------------------------------------------------------------------------- 1 | name: ubuntu-build 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | 8 | concurrency: 9 | group: ci-${{github.workflow}}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-22.04 15 | name: ubuntu-build 16 | steps: 17 | - name: Show env 18 | run: env 19 | 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | 23 | - name: Install Build Dependencies 24 | shell: bash 25 | run: | 26 | sudo apt update && sudo apt install --no-install-recommends -y \ 27 | cmake \ 28 | make \ 29 | wget \ 30 | lsb-release \ 31 | gnupg \ 32 | curl 33 | 34 | - name: Show Python version and platform info 35 | run: | 36 | python --version 37 | python -m platform 38 | 39 | - name: Show CMake version 40 | run: cmake --version 41 | 42 | # - name: Show protobuf compiler version 43 | # run: protoc --version 44 | 45 | - name: Install Build Tools 46 | run: | 47 | sudo sh -c 'echo "deb http://packages.ros.org/ros2/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros2-latest.list' 48 | curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - 49 | sudo apt update && sudo apt install --no-install-recommends -y \ 50 | python3-pip \ 51 | python3-vcstool \ 52 | python3-colcon-common-extensions 53 | 54 | - name: Install Gazebo Garden 55 | run: | 56 | sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg 57 | echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null 58 | sudo apt update && sudo apt install --no-install-recommends -y \ 59 | gz-garden 60 | 61 | - name: Build 62 | run: | 63 | colcon build --symlink-install --merge-install --cmake-args \ 64 | -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 65 | -DCMAKE_CXX_STANDARD=17 \ 66 | -DBUILD_TESTING=ON 67 | 68 | - name: Test 69 | run: | 70 | colcon test --merge-install 71 | colcon test-result --all --verbose 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-bin 2 | bazel-out 3 | bazel-gz-python 4 | bazel-testlogs 5 | build 6 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | #——————————————————————————————————————————————————————————————————————— 4 | # gz_py 5 | # 6 | # This is a Python library meta-target to hoist the pybind11 modules 7 | # under src/pybind11 to the root directory so that 8 | # 9 | # imports = ["src/pybind11"] 10 | # 11 | # can be used. It ensures that the 'gz' package is visible on 12 | # the PYTHONPATH and can be imported into modules without being prefixed 13 | # by the fullpackage path 'gz_python.src.pybind11' 14 | # 15 | py_library( 16 | name = "gz_py", 17 | imports = [ 18 | "src/pybind11", 19 | ], 20 | data = [ 21 | "//gz_python/src/pybind11/gz/msgs:extras.so", 22 | "//gz_python/src/pybind11/gz:transport.so", 23 | ], 24 | ) 25 | 26 | #——————————————————————————————————————————————————————————————————————— 27 | # deps_check 28 | # Quick check of dependencies on third-party packages 29 | 30 | cc_binary( 31 | name = "deps_check", 32 | srcs = ["src/deps_check.cc"], 33 | deps = [ 34 | "//gz_utils:gz_utils", 35 | "//gz_math:gz_math", 36 | "//gz_math:libgz-math7.so", 37 | "@tinyxml2", 38 | "@sqlite3", 39 | "@zmq", 40 | "@zip", 41 | "@yaml", 42 | "@curl", 43 | "@json", 44 | "@glib", 45 | "@gts", 46 | "@freeimage", 47 | # "@gl", 48 | # "@X//:Xaw", 49 | ], 50 | ) 51 | 52 | #——————————————————————————————————————————————————————————————————————— 53 | # msg_example 54 | 55 | cc_binary( 56 | name = "msg_example", 57 | srcs = [ 58 | "src/msg_example.cc", 59 | ], 60 | deps = [ 61 | "//gz_msgs:gz_msgs", 62 | "@com_google_protobuf//:protobuf", 63 | ], 64 | ) 65 | 66 | #——————————————————————————————————————————————————————————————————————— 67 | # publisher 68 | 69 | cc_binary( 70 | name = "publisher", 71 | srcs = [ 72 | "src/publisher.cc", 73 | ], 74 | deps = [ 75 | "//gz_msgs:gz_msgs", 76 | "//gz_transport:gz_transport", 77 | "@com_google_protobuf//:protobuf", 78 | ], 79 | ) 80 | 81 | #——————————————————————————————————————————————————————————————————————— 82 | # subscriber 83 | 84 | cc_binary( 85 | name = "subscriber", 86 | srcs = [ 87 | "src/subscriber.cc", 88 | ], 89 | deps = [ 90 | "//gz_msgs:gz_msgs", 91 | "//gz_transport:gz_transport", 92 | "@com_google_protobuf//:protobuf", 93 | ], 94 | ) 95 | 96 | #——————————————————————————————————————————————————————————————————————— 97 | # rover_publisher 98 | 99 | cc_binary( 100 | name = "rover_publisher", 101 | srcs = [ 102 | "src/rover_publisher.cc", 103 | ], 104 | deps = [ 105 | "//gz_math:gz_math", 106 | "//gz_msgs:gz_msgs", 107 | "//gz_transport:gz_transport", 108 | "@com_google_protobuf//:protobuf", 109 | ], 110 | ) 111 | 112 | #——————————————————————————————————————————————————————————————————————— 113 | # rover_subscriber 114 | 115 | cc_binary( 116 | name = "rover_subscriber", 117 | srcs = [ 118 | "src/rover_subscriber.cc", 119 | ], 120 | deps = [ 121 | "//gz_math:gz_math", 122 | "//gz_msgs:gz_msgs", 123 | "//gz_transport:gz_transport", 124 | "@com_google_protobuf//:protobuf", 125 | ], 126 | ) 127 | 128 | 129 | #——————————————————————————————————————————————————————————————————————— 130 | # C++ example 131 | cc_binary( 132 | name = "main", 133 | srcs = ["src/main.cc"], 134 | includes = ["include"], 135 | deps = [ 136 | "//gz_python/src/pybind11/gz/msgs:gz_msgs_extras_lib", 137 | ], 138 | ) 139 | 140 | #——————————————————————————————————————————————————————————————————————— 141 | # gz_topic_echo 142 | # 143 | cc_binary( 144 | name = "gz_topic_echo", 145 | srcs = ["src/gz_topic_echo.cc"], 146 | deps = [ 147 | "//gz_msgs:gz_msgs", 148 | "//gz_transport:gz_transport", 149 | "@com_google_protobuf//:protobuf", 150 | ], 151 | ) 152 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18) 2 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 3 | 4 | project(gz-python) 5 | 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | 9 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 10 | 11 | #============================================================================ 12 | # Options 13 | 14 | option(BUILD_TESTS "Build tests." OFF) 15 | 16 | #============================================================================ 17 | # Find Python 18 | # 19 | # macOS: $ brew install python 20 | # macOS: $ brew install pybind11 21 | # 22 | find_package(Python 3.10 EXACT COMPONENTS Interpreter Development) 23 | 24 | #============================================================================ 25 | # Find Gazebo dependencies 26 | 27 | # Default versions to Gazebo Garden 28 | set(GZ_MSGS_VER 9) 29 | set(GZ_TRANSPORT_VER 12) 30 | 31 | if("$ENV{GZ_VERSION}" STREQUAL "garden") 32 | # Garden 33 | message(STATUS "Compiling against Gazebo Garden") 34 | else() 35 | # Default to Garden 36 | message(STATUS "Compiling against Gazebo Garden") 37 | endif() 38 | 39 | find_package(gz-msgs${GZ_MSGS_VER} REQUIRED) 40 | find_package(gz-transport${GZ_TRANSPORT_VER} REQUIRED) 41 | 42 | #============================================================================ 43 | # Build dependencies 44 | 45 | set(_protobuf_repository "https://github.com/protocolbuffers/protobuf.git") 46 | set(_protobuf_version 21.5) 47 | set(_protobuf_tag v21.5) 48 | 49 | set(_pybind11_protobuf_repository "https://github.com/pybind/pybind11_protobuf.git") 50 | set(_pybind11_protobuf_version 0.0.0) 51 | set(_pybind11_protobuf_tag main) 52 | 53 | add_subdirectory(cmake/dependencies dependencies) 54 | list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_BINARY_DIR}/dependencies/install) 55 | 56 | message("pybind11_protobuf_FOUND: ${pybind11_protobuf_FOUND}") 57 | message("pybind11_protobuf_SOURCE_DIR: ${pybind11_protobuf_SOURCE_DIR}") 58 | message("pybind11_protobuf_INCLUDE_DIRS: ${pybind11_protobuf_INCLUDE_DIRS}") 59 | 60 | # TODO fix upstream... 61 | set(pybind11_protobuf_INCLUDE_DIRS ${pybind11_protobuf_SOURCE_DIR}) 62 | 63 | #============================================================================ 64 | # gz_msgs_extras_lib C++ library 65 | 66 | add_library(gz_msgs_extras_lib SHARED 67 | src/pybind11/gz/msgs/extras.cc 68 | ) 69 | 70 | target_link_libraries(gz_msgs_extras_lib 71 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 72 | ) 73 | 74 | target_include_directories(gz_msgs_extras_lib 75 | PRIVATE 76 | ${PROJECT_SOURCE_DIR}/include 77 | ) 78 | 79 | #============================================================================ 80 | # msgs extras Python extension module 81 | 82 | pybind11_add_module(extras MODULE 83 | src/pybind11/gz/msgs/_gz_msgs_extras_pybind11.cc 84 | ) 85 | 86 | target_link_libraries(extras 87 | PRIVATE 88 | gz_msgs_extras_lib 89 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 90 | protobuf::libprotobuf 91 | pybind11_native_proto_caster 92 | ) 93 | 94 | target_include_directories(extras 95 | PRIVATE 96 | ${PROJECT_SOURCE_DIR}/include 97 | ${pybind11_protobuf_INCLUDE_DIRS} 98 | ) 99 | 100 | add_dependencies(extras 101 | gz_msgs_extras_lib 102 | ) 103 | 104 | set_target_properties(extras 105 | PROPERTIES 106 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/gz/msgs" 107 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/gz/msgs" 108 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/gz/msgs" 109 | ) 110 | 111 | #============================================================================ 112 | # transport Python extension module 113 | 114 | pybind11_add_module(transport MODULE 115 | src/pybind11/gz/transport/_gz_transport_pybind11.cc 116 | ) 117 | 118 | target_link_libraries(transport 119 | PRIVATE 120 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 121 | gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} 122 | protobuf::libprotobuf 123 | pybind11_native_proto_caster 124 | ) 125 | 126 | target_include_directories(transport 127 | PRIVATE 128 | ${protobuf_INCLUDE_DIRS} 129 | ${pybind11_protobuf_INCLUDE_DIRS} 130 | ) 131 | 132 | set_target_properties(transport 133 | PROPERTIES 134 | ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/gz" 135 | LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/gz" 136 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/gz" 137 | ) 138 | 139 | #============================================================================ 140 | # main 141 | 142 | add_executable(main src/main.cc) 143 | 144 | target_link_libraries(main 145 | gz_msgs_extras_lib 146 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 147 | ) 148 | 149 | target_include_directories(main 150 | PRIVATE 151 | ${PROJECT_SOURCE_DIR}/include 152 | ) 153 | 154 | add_dependencies(main 155 | gz_msgs_extras_lib 156 | ) 157 | 158 | #============================================================================ 159 | # msg_example 160 | 161 | add_executable(msg_example src/msg_example.cc) 162 | 163 | target_link_libraries(msg_example 164 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 165 | ) 166 | 167 | #============================================================================ 168 | # publisher 169 | 170 | add_executable(publisher src/publisher.cc) 171 | 172 | target_link_libraries(publisher 173 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 174 | gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} 175 | ) 176 | 177 | #============================================================================ 178 | # subscriber 179 | 180 | add_executable(subscriber src/subscriber.cc) 181 | 182 | target_link_libraries(subscriber 183 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 184 | gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} 185 | ) 186 | 187 | #============================================================================ 188 | # rover_publisher 189 | 190 | add_executable(rover_publisher src/rover_publisher.cc) 191 | 192 | target_link_libraries(rover_publisher 193 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 194 | gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} 195 | ) 196 | 197 | #============================================================================ 198 | # rover_subscriber 199 | 200 | add_executable(rover_subscriber src/rover_subscriber.cc) 201 | 202 | target_link_libraries(rover_subscriber 203 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 204 | gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} 205 | ) 206 | 207 | #============================================================================ 208 | # gz_topic_echo 209 | 210 | add_executable(gz_topic_echo src/gz_topic_echo.cc) 211 | 212 | target_link_libraries(gz_topic_echo 213 | gz-msgs${GZ_MSGS_VER}::gz-msgs${GZ_MSGS_VER} 214 | gz-transport${GZ_TRANSPORT_VER}::gz-transport${GZ_TRANSPORT_VER} 215 | ) 216 | 217 | #============================================================================ 218 | # generate protobuf Python bindings 219 | 220 | # Get the proto files from the installed location of gz-msgs 221 | set(PROTO_PATH ${gz-msgs${GZ_MSGS_VER}_INCLUDE_DIRS}) 222 | file(GLOB PROTO_FILES ${PROTO_PATH}/gz/msgs/*.proto) 223 | 224 | #============================================================================ 225 | # Modified from gz-msgs/src/CMakeLists.txt for Python 226 | #============================================================================ 227 | # A function that calls protoc on a protobuf file 228 | # Options: 229 | # GENERATE_RUBY - generates ruby code for the message if specified 230 | # GENERATE_CPP - generates c++ code for the message if specified 231 | # GENERATE_PY - generates Python code for the message if specified 232 | # One value arguments: 233 | # PROTO_PACKAGE - Protobuf package the file belongs to (e.g. ".gz.msgs") 234 | # PROTOC_EXEC - Path to protoc 235 | # INPUT_PROTO - Path to the input .proto file 236 | # OUTPUT_CPP_DIR - Path where C++ files are saved 237 | # OUTPUT_RUBY_DIR - Path where Ruby files are saved 238 | # OUTPUT_PY_DIR - Path where Python files are saved 239 | # OUTPUT_CPP_HH_VAR - A CMake variable name containing a list that the C++ header path should be appended to 240 | # OUTPUT_CPP_CC_VAR - A CMake variable name containing a list that the C++ source path should be appended to 241 | # OUTPUT_RUBY_VAR - A CMake variable name containing a list that the ruby file should be appended to 242 | # OUTPUT_PY_VAR - A CMake variable name containing a list that the Python file should be appended to 243 | # Multi value arguments 244 | # PROTO_PATH - Passed to protoc --proto_path 245 | function(gz_msgs_protoc) 246 | set(options GENERATE_RUBY GENERATE_CPP GENERATE_PY) 247 | set(oneValueArgs PROTO_PACKAGE PROTOC_EXEC INPUT_PROTO OUTPUT_CPP_DIR OUTPUT_RUBY_DIR OUTPUT_PY_DIR OUTPUT_CPP_HH_VAR OUTPUT_CPP_CC_VAR OUTPUT_RUBY_VAR OUTPUT_PY_VAR) 248 | set(multiValueArgs PROTO_PATH) 249 | 250 | cmake_parse_arguments(gz_msgs_protoc "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 251 | 252 | get_filename_component(ABS_FIL ${gz_msgs_protoc_INPUT_PROTO} ABSOLUTE) 253 | get_filename_component(FIL_WE ${gz_msgs_protoc_INPUT_PROTO} NAME_WE) 254 | 255 | set(protoc_args) 256 | set(output_files) 257 | 258 | foreach(proto_path ${gz_msgs_protoc_PROTO_PATH}) 259 | list(APPEND protoc_args "--proto_path=${proto_path}") 260 | endforeach() 261 | 262 | set(proto_package_dir ".") 263 | if(gz_msgs_protoc_PROTO_PACKAGE) 264 | string(REPLACE "." "/" proto_package_dir ${gz_msgs_protoc_PROTO_PACKAGE}) 265 | endif() 266 | 267 | if(gz_msgs_protoc_GENERATE_CPP) 268 | set(output_header "${gz_msgs_protoc_OUTPUT_CPP_DIR}${proto_package_dir}/${FIL_WE}.pb.h") 269 | set(output_source "${gz_msgs_protoc_OUTPUT_CPP_DIR}${proto_package_dir}/${FIL_WE}.pb.cc") 270 | list(APPEND ${gz_msgs_protoc_OUTPUT_CPP_HH_VAR} ${output_header}) 271 | list(APPEND ${gz_msgs_protoc_OUTPUT_CPP_CC_VAR} ${output_source}) 272 | list(APPEND output_files ${output_header}) 273 | list(APPEND output_files ${output_source}) 274 | list(APPEND protoc_args "--plugin=protoc-gen-gzmsgs=${GZ_MSGS_GEN_EXECUTABLE}") 275 | list(APPEND protoc_args "--cpp_out=dllexport_decl=GZ__MSGS_VISIBLE:${gz_msgs_protoc_OUTPUT_CPP_DIR}") 276 | list(APPEND protoc_args "--gzmsgs_out" "${gz_msgs_protoc_OUTPUT_CPP_DIR}") 277 | set(${gz_msgs_protoc_OUTPUT_CPP_HH_VAR} ${${gz_msgs_protoc_OUTPUT_CPP_HH_VAR}} PARENT_SCOPE) 278 | set(${gz_msgs_protoc_OUTPUT_CPP_CC_VAR} ${${gz_msgs_protoc_OUTPUT_CPP_CC_VAR}} PARENT_SCOPE) 279 | endif() 280 | 281 | if(gz_msgs_protoc_GENERATE_RUBY) 282 | file(MAKE_DIRECTORY ${gz_msgs_protoc_OUTPUT_RUBY_DIR}) 283 | set(output_ruby "${gz_msgs_protoc_OUTPUT_RUBY_DIR}${proto_package_dir}/${FIL_WE}_pb.rb") 284 | list(APPEND ${gz_msgs_protoc_OUTPUT_RUBY_VAR} ${output_ruby}) 285 | list(APPEND output_files ${output_ruby}) 286 | list(APPEND protoc_args "--ruby_out=${gz_msgs_protoc_OUTPUT_RUBY_DIR}") 287 | set(${gz_msgs_protoc_OUTPUT_RUBY_VAR} ${${gz_msgs_protoc_OUTPUT_RUBY_VAR}} PARENT_SCOPE) 288 | endif() 289 | 290 | if(gz_msgs_protoc_GENERATE_PY) 291 | file(MAKE_DIRECTORY ${gz_msgs_protoc_OUTPUT_PY_DIR}) 292 | set(output_py "${gz_msgs_protoc_OUTPUT_PY_DIR}${proto_package_dir}/${FIL_WE}_pb2.py") 293 | list(APPEND ${gz_msgs_protoc_OUTPUT_PY_VAR} ${output_py}) 294 | list(APPEND output_files ${output_py}) 295 | list(APPEND protoc_args "--python_out=${gz_msgs_protoc_OUTPUT_PY_DIR}") 296 | set(${gz_msgs_protoc_OUTPUT_PY_VAR} ${${gz_msgs_protoc_OUTPUT_PY_VAR}} PARENT_SCOPE) 297 | endif() 298 | 299 | #get_cmake_property(_variableNames VARIABLES) 300 | #foreach (_variableName ${_variableNames}) 301 | # message(STATUS "${_variableName}=${${_variableName}}") 302 | #endforeach() 303 | 304 | add_custom_command( 305 | OUTPUT 306 | ${output_files} 307 | COMMAND 308 | ${gz_msgs_protoc_PROTOC_EXEC} 309 | ARGS 310 | ${protoc_args} 311 | ${ABS_FIL} 312 | DEPENDS 313 | ${ABS_FIL} 314 | # gz_msgs_gen 315 | COMMENT "Running protoc on ${gz_msgs_protoc_INPUT_PROTO}" 316 | VERBATIM) 317 | endfunction() 318 | 319 | #============================================================================ 320 | # run the code generation 321 | foreach(proto_file ${PROTO_FILES}) 322 | gz_msgs_protoc( 323 | PROTO_PACKAGE 324 | .gz.msgs 325 | # GENERATE_CPP 326 | # GENERATE_RUBY 327 | GENERATE_PY 328 | INPUT_PROTO 329 | ${proto_file} 330 | PROTOC_EXEC 331 | protobuf::protoc 332 | OUTPUT_CPP_DIR 333 | "${PROJECT_BINARY_DIR}/include" 334 | OUTPUT_RUBY_DIR 335 | "${PROJECT_BINARY_DIR}/ruby" 336 | OUTPUT_PY_DIR 337 | "${PROJECT_BINARY_DIR}/python" 338 | OUTPUT_CPP_HH_VAR 339 | gen_headers 340 | OUTPUT_CPP_CC_VAR 341 | gen_sources 342 | OUTPUT_RUBY_VAR 343 | gen_ruby_scripts 344 | OUTPUT_PY_VAR 345 | gen_py_scripts 346 | PROTO_PATH 347 | "${PROTO_PATH}") 348 | 349 | endforeach() 350 | 351 | #============================================================================ 352 | # add custom target to force .proto compilation 353 | 354 | set_source_files_properties( 355 | ${gen_headers} 356 | ${gen_sources} 357 | ${gen_ruby_scripts} 358 | ${gen_py_scripts} 359 | PROPERTIES GENERATED TRUE 360 | ) 361 | 362 | foreach(gen_py_script ${gen_py_scripts}) 363 | get_filename_component(FIL_WE ${gen_py_script} NAME_WE) 364 | add_custom_target(${FIL_WE} ALL DEPENDS ${gen_py_script}) 365 | endforeach() 366 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Software License Agreement (Apache License) 2 | 3 | Copyright 2022 Rhys Mainwaring 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gazebo Python Bindings 2 | 3 | This project provides Python bindings for [`gz-msgs`](https://github.com/gazebosim/gz-msgs) and [`gz-transport`](https://github.com/gazebosim/gz-transport). 4 | 5 | [![ubuntu-build](https://github.com/srmainwaring/gz-python/actions/workflows/ubuntu-build.yml/badge.svg)](https://github.com/srmainwaring/gz-python/actions/workflows/ubuntu-build.yml) 6 | 7 | ## Building with CMake 8 | 9 | ### Install Gazebo 10 | 11 | Follow the installation instructions for [Gazebo Garden](https://gazebosim.org/docs/garden). 12 | This project depends directly on [`gz-msgs`](https://github.com/gazebosim/gz-msgs) and [`gz-transport`](https://github.com/gazebosim/gz-transport). These may be either available as system installs or in a source install in a local workspace folder which we assume is `~/gz_ws`. 13 | 14 | ### Install `gz-python` 15 | 16 | Clone this repo into the workspace source directory: 17 | 18 | ```bash 19 | cd ~/gz_ws/src 20 | git clone https://github.com/srmainwaring/gz-python.git 21 | ``` 22 | 23 | ### Build with CMake 24 | 25 | Currently our use of [`pybind11_protobuf`](https://github.com/pybind/pybind11_protobuf) requires the protobuf compiler to generate the Python implementation of the Python protobuf bindings. This is configured with the environment variable: 26 | 27 | ```bash 28 | export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python 29 | ``` 30 | 31 | Set the environment variable determing the Gazebo version. 32 | The default is `garden`: 33 | 34 | ```bash 35 | export GAZEBO_VERSION=garden 36 | ``` 37 | 38 | Then create a build directory, configure and make: 39 | 40 | ```bash 41 | mkdir -p ~/gz_ws/src/gz-python/build 42 | cd ~/gz_ws/src/gz-python/build 43 | cmake .. 44 | make 45 | ``` 46 | 47 | ### Update the Python environment 48 | 49 | Update the PYTHONPATH to include the location of the extension modules and the 50 | generated Python protobuf bindings. 51 | 52 | ```bash 53 | cd ~/gz_ws/src/gz-python 54 | export PYTHONPATH=${PYTHONPATH}:$(pwd)/build/python 55 | ``` 56 | 57 | ## Usage 58 | 59 | ### `gz-msg` bindings 60 | 61 | The Python bindings for `gz-msgs` are the standard generated protobuf code 62 | for Python. For example `gz-msgs/proto/gz/msgs/time.proto` may be used as follows: 63 | 64 | ```python 65 | # example.py 66 | from gz.msgs.time_pb2 import Time 67 | 68 | msg = Time() 69 | msg.sec = 15 70 | msg.nsec = 21 71 | print(msg) 72 | ``` 73 | 74 | ### `gz-transport` bindings 75 | 76 | The Python bindings for `gz-transport` are contained in a module called `transport`. 77 | The object naming and usage for the most part follows the C++ interface, 78 | so the C++ Gazebo Tutorials are a good guide on how to use the library. 79 | 80 | Publish: 81 | 82 | ```python 83 | from gz.msgs.stringmsg_pb2 import StringMsg 84 | 85 | from gz.transport import AdvertiseMessageOptions 86 | from gz.transport import Node 87 | 88 | # Create a transport node 89 | node = Node() 90 | 91 | # Advertise a topic 92 | topic = "/foo" 93 | msg_type_name = StringMsg.DESCRIPTOR.full_name 94 | pub_options = AdvertiseMessageOptions() 95 | pub = node.advertise(topic, msg_type_name, pub_options) 96 | 97 | # Publish a message 98 | msg = StringMsg() 99 | msg.data = "hello" 100 | pub.publish(msg) 101 | ``` 102 | 103 | Subscribe: 104 | 105 | ```python 106 | import time 107 | import typing 108 | 109 | from gz.msgs.stringmsg_pb2 import StringMsg 110 | 111 | from gz.transport import SubscribeOptions 112 | from gz.transport import Node 113 | 114 | def cb(msg: StringMsg) -> None: 115 | print("Msg: [{}] from Python".format(msg.data)) 116 | 117 | # Create a transport node 118 | node = Node() 119 | 120 | # Subscribe to a topic by registering a callback 121 | topic = "/foo" 122 | msg_type_name = StringMsg.DESCRIPTOR.full_name 123 | sub_options = SubscribeOptions() 124 | 125 | node.subscribe(topic, cb, msg_type_name, sub_options) 126 | ``` 127 | 128 | ## Examples 129 | 130 | A number of examples in C++ and Python are provided. In the following we suppose that 131 | they are being run from the project directory `~/gz_ws/src/gz-python`. 132 | 133 | --- 134 | 135 | `src/msg_example.cc` is a copy of the [`gz-msgs` tutorial example](https://gazebosim.org/api/msgs/8.1/cppgetstarted.html): 136 | 137 | ```bash 138 | $ ./build/msg_example 139 | Point1: 140 | x: 1 141 | y: 3 142 | z: 5 143 | 144 | Point2: 145 | x: 2 146 | y: 4 147 | z: 6 148 | ``` 149 | 150 | --- 151 | 152 | `src/publisher.cc` and `src/subscriber.cc` is copied from the [`gz-transport` messages tutorial example](https://gazebosim.org/api/transport/11.0/messages.html). 153 | 154 | From terminal 1: 155 | 156 | ```bash 157 | $ ./build/publisher 158 | Publishing hello on topic [/foo] 159 | Publishing hello on topic [/foo] 160 | ... 161 | ``` 162 | 163 | From terminal 2: 164 | 165 | ```bash 166 | $ ./build/subscriber 167 | Msg: hello 168 | Msg: hello 169 | ... 170 | ``` 171 | 172 | --- 173 | 174 | `src/rover_publisher.cc` and `src/rover_subscriber.cc` comprise another publish / subscribe 175 | example that publishes the pose and twist of a rover moving in a circle with constant 176 | angular velocity. 177 | 178 | From terminal 1: 179 | 180 | ```bash 181 | $ ./build/rover_publisher 182 | Publishing pose on topic [/pose], twist on topic [/twist] 183 | Publishing pose on topic [/pose], twist on topic [/twist] 184 | ... 185 | ``` 186 | 187 | From terminal 2: 188 | 189 | ```bash 190 | $ ./build/rover_subscriber 191 | header { 192 | stamp { 193 | sec: 10 194 | nsec: 45483925 195 | } 196 | } 197 | name: "base_link" 198 | id: 20 199 | position { 200 | x: 2.7015115293406988 201 | y: 4.2073549240394827 202 | } 203 | orientation { 204 | z: 0.479425538604203 205 | w: 0.87758256189037276 206 | } 207 | header { 208 | stamp { 209 | sec: 10 210 | nsec: 45483925 211 | } 212 | } 213 | linear { 214 | x: -0.42073549240394825 215 | y: 0.27015115293406988 216 | } 217 | angular { 218 | z: 0.1 219 | } 220 | ``` 221 | 222 | --- 223 | 224 | `python/msgs_example.py` is a Python example that uses the generated Python protobuf libraries for `gz-msgs`: 225 | 226 | ```bash 227 | $ ./python/msgs_example.py 228 | Gazebo Protobuf Example 229 | 230 | proto api type: python 231 | ---------------------------------------- 232 | 233 | sec: 15 234 | nsec: 21 235 | ... 236 | ``` 237 | 238 | --- 239 | 240 | `python/publisher.py` is a Python version of the C++ `src/publisher.cc` described above. 241 | You can listen to the messages using the C++ subscriber as before. 242 | 243 | From terminal 1: 244 | 245 | ```bash 246 | $ ./python/publisher.py 247 | Advertising gz.msgs.StringMsg on topic [/foo] 248 | Publishing hello on topic [/foo] 249 | Publishing hello on topic [/foo] 250 | ... 251 | ``` 252 | 253 | From terminal 2: 254 | 255 | ```bash 256 | $ ./python/subscriber.py 257 | Subscribing to type gz.msgs.StringMsg on topic [/foo] 258 | Msg: [hello] from Python 259 | Msg: [hello] from Python 260 | ... 261 | ``` 262 | 263 | --- 264 | 265 | `python/rover_publisher.py` is a Python version of the C++ `src/rover_publisher.cc` example. 266 | 267 | From terminal 1: 268 | 269 | ```bash 270 | ./python/rover_publisher.py 271 | Advertising gz.msgs.Pose on topic [/pose] 272 | Advertising gz.msgs.Twist on topic [/twist] 273 | Publishing pose on topic [/pose], twist on topic [/twist] 274 | Publishing pose on topic [/pose], twist on topic [/twist] 275 | ... 276 | ``` 277 | 278 | From terminal 2: 279 | 280 | ```bash 281 | ./python/rover_subscriber.py 282 | Subscribing to type gz.msgs.Pose on topic [/pose] 283 | Subscribing to type gz.msgs.Twist on topic [/twist] 284 | header { 285 | stamp { 286 | sec: 2 287 | nsec: 511006000 288 | } 289 | } 290 | name: "base_link" 291 | id: 5 292 | position { 293 | x: 4.900332889206208 294 | y: 0.9933466539753061 295 | } 296 | orientation { 297 | z: 0.09983341664682815 298 | w: 0.9950041652780257 299 | } 300 | 301 | header { 302 | stamp { 303 | sec: 2 304 | nsec: 511006000 305 | } 306 | } 307 | linear { 308 | x: -0.09933466539753061 309 | y: 0.4900332889206208 310 | } 311 | angular { 312 | z: 0.1 313 | } 314 | ... 315 | ``` 316 | 317 | --- 318 | 319 | `python/gz_topic_list.py` is a Python version of the command `gz topic -l` 320 | 321 | ```bash 322 | $ ./python/gz_topic_list.py 323 | /foo 324 | ``` 325 | 326 | --- 327 | 328 | `python/gz_topic_info.py` is a Python version of the command `gz topic -i -t /foo` 329 | 330 | ```bash 331 | $ ./python/gz_topic_info.py -t /foo 332 | Publishers [Address, Message Type]: 333 | tcp://127.0.0.1:60328, gz.msgs.StringMsg 334 | ``` 335 | 336 | --- 337 | 338 | `python/gz_topic_echo.py` is a Python version of the command `gz topic -e -t /foo` 339 | 340 | ```bash 341 | $ ./python/gz_topic_echo.py -t /foo 342 | Subscribing to topic [/foo] 343 | data: "hello" 344 | 345 | data: "hello" 346 | 347 | data: "hello" 348 | ``` 349 | 350 | --- 351 | 352 | `python/gz_service_list.py` is a Python version of the command `gz service -l` 353 | 354 | ```bash 355 | $ ./python/gz_service_list.py 356 | /gazebo/resource_paths/add 357 | /gazebo/resource_paths/get 358 | /gazebo/worlds 359 | /gui/camera/view_control 360 | /gui/follow 361 | ... 362 | ``` 363 | 364 | --- 365 | 366 | `python/gz_service_info.py` is a Python version of the command `gz service -i -s /gazebo/worlds` 367 | 368 | ```bash 369 | $ ./python/gz_service_info.py -s /gazebo/worlds 370 | Service providers [Address, Request Message Type, Response Message Type]: 371 | tcp://127.0.0.1:63657, gz.msgs.Empty, gz.msgs.StringMsg_V 372 | ``` 373 | 374 | 375 | ## Building with Bazel 376 | 377 | On macOS the project may also be built with Bazel. This is experimental and depends upon 378 | a modified version of the Bazel build rules for Gazebo in the [`gz-bazel`](https://github.com/gazebosim/gz-bazel) project. 379 | 380 | 381 | ### Installing Bazel 382 | 383 | On macOS Bazel can be installed with `brew`: 384 | 385 | ```bash 386 | brew install bazel 387 | ``` 388 | 389 | The [Google protocol buffers compiler](https://github.com/protocolbuffers/protobuf) version `3.19` is also required and can be installed with: 390 | 391 | 392 | ```bash 393 | brew install protobuf 394 | ``` 395 | 396 | 397 | ### Setting up the workspace 398 | 399 | Make a directory to contain this package and all the Gazebo packages and dependencies: 400 | 401 | ```bash 402 | mkdir ~/gz/ 403 | cd ~/gz/ 404 | ``` 405 | 406 | Clone each of the packages using the `bazel.repos` file and [vcstool](https://github.com/dirk-thomas/vcstool). The forked version of [`gz-bazel`](https://github.com/srmainwaring/gz-bazel/tree/bazel-macos) contains modified build rules and repo references for macOS. 407 | 408 | ```bash 409 | wget https://raw.githubusercontent.com/srmainwaring/gz-bazel/bazel-macos/example/bazel.repos 410 | vcs import . < bazel.repos 411 | ``` 412 | 413 | Next, symlink the following files into the workspace directory: 414 | 415 | ```bash 416 | cd ~/gz 417 | ln -sf ./gz_bazel/example/WORKSPACE.example ./WORKSPACE 418 | ln -sf ./gz_bazel/example/BUILD.example ./BUILD.bazel 419 | ln -sf ./gz_bazel/example/bazelrc.example ./.bazelrc 420 | ln -sf ./gz_python/gz-msgs9.BUILD ./gz-msgs9.BUILD 421 | ``` 422 | 423 | Finally, [`pybind11_protobuf`](https://github.com/pybind/pybind11_protobuf) requires a patch to the protobuf archive which must be available in the subdirectory `~/gz/external`. It must be copied rather than symlinked: 424 | 425 | ```bash 426 | cd ~/gz 427 | mkdir external 428 | cp ./gz_python/external/com_google_protobuf_build.patch external 429 | ``` 430 | 431 | ### Build with Bazel 432 | 433 | Currently our use of `pybind11_protobuf` requires the Python implementation of the Python protobuf generator. This is configured with the environment variable: 434 | 435 | ```bash 436 | export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python 437 | ``` 438 | 439 | Then build the examples with: 440 | 441 | ```bash 442 | bazel build //gz_python:all 443 | bazel build //gz_python/python:all 444 | ``` 445 | 446 | Note: on macOS not all Gazebo projects will build with Bazel, so the command: 447 | 448 | ```bash 449 | bazel build //... 450 | ``` 451 | 452 | will result in a number of errors. 453 | 454 | ### Usage in Bazel builds 455 | 456 | The Bazel build file `gz-msgs9.BUILD` defines targets for a selection of messages. 457 | For example the targets for `proto/gz/msgs/time.proto` are: 458 | 459 | ```bash 460 | # proto_library 461 | @gz-msgs9//:time_proto 462 | 463 | # cpp_proto_library 464 | @gz-msgs9//:time_cc_proto 465 | 466 | # python_proto_library 467 | @gz-msgs9//:time_py_pb2 468 | ``` 469 | 470 | To use the message bindings for C++ targets: 471 | 472 | ```bash 473 | # BUILD.bazel 474 | 475 | cc_binary( 476 | name = "main", 477 | srcs = ["main.cc"], 478 | deps = [ 479 | "@gz-msgs9//:time_cc_proto", 480 | ], 481 | ) 482 | ``` 483 | 484 | To use the bindings for Python targets: 485 | 486 | ```bash 487 | # BUILD.bazel 488 | 489 | py_binary( 490 | name = "example", 491 | srcs = ["example.py"], 492 | data = [ 493 | "@com_google_protobuf//:proto_api", 494 | ], 495 | deps = [ 496 | "@gz-msgs9//:time_py_pb2", 497 | ], 498 | ) 499 | ``` 500 | 501 | ## Notes 502 | 503 | ### Protobuf compiler version 504 | 505 | The project depends on `protoc` version `3.19.1`. You must ensure that the version of `protoc` installed by `brew` matches (otherwise the examples will segfault). 506 | 507 | ```bash 508 | $ protoc --version 509 | libprotoc 3.19.1 510 | ``` 511 | 512 | ### Protobuf generated Python libraries 513 | 514 | The `brew` installation of `protobuf` defaults to use the C++ implementation 515 | of the generated Python protobuf library. When using this with `pybind11_protobuf` 516 | there are some cases when C++ wrapped protobuf objects returned from an extension module are not 517 | recognised as their Python equivalents. The Python implementation will work in these cases 518 | and to enable this set the following environment variable before building the project: 519 | 520 | ```bash 521 | export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python 522 | ``` 523 | 524 | ### CI Testing 525 | 526 | 527 | 528 | ### Repos used to build Gazebo on macOS with Bazel 529 | 530 | The table summarises the dependencies (repo and branch) on the Gazebo libraries. 531 | 532 | | library | repo | branch | build | test | 533 | | --- | --- | --- | --- | --- | 534 | | | | | | | 535 | |gz_python|https://github.com/srmainwaring/gz-python|main|pass|-| 536 | | | | | | | 537 | |gz-bazel|https://github.com/srmainwaring/gz-bazel|bazel-macos|pass|pass| 538 | |gz-math|https://github.com/srmainwaring/gz-math|bazel-macos/gz-math6|pass|pass| 539 | |gz-utils|https://github.com/srmainwaring/gz-utils|bazel-macos/gz-utils1|pass|pass| 540 | |gz-common|https://github.com/srmainwaring/gz-common|bazel-macos/gz-common4|pass|pass| 541 | |gz-msgs|https://github.com/srmainwaring/gz-msgs|bazel-macos/gz-msgs9|pass|pass| 542 | |sdformat|https://github.com/gazebosim/sdformat| bazel-sdf10|pass|pass| 543 | |gz-plugin|https://github.com/srmainwaring/gz-plugin|bazel-macos/gz-plugin1|pass|pass| 544 | |gz-transport|https://github.com/srmainwaring/gz-transport|bazel-macos/gz-transport12|pass|1 fail| 545 | |gz-physics|https://github.com/srmainwaring/gz-physics|bazel-macos/gz-physics5|pass|pass| 546 | |gz-tools|https://github.com/srmainwaring/gz-tools|bazel-macos/gz-tools1|partial|-| 547 | |gz-rendering|https://github.com/gazebosim/gz-rendering|bazel-rendering4|fail|fail| 548 | |gz-gui|https://github.com/gazebosim/gz-gui|bazel-gui4|fail|fail| 549 | |gz-sensors|https://github.com/gazebosim/gz-sensors|bazel-sensors4|fail|fail| 550 | |gz-gazebo|https://github.com/gazebosim/gz-gazebo|bazel-gazebo4|fail|fail| 551 | | | | | | | 552 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "gz-python") 2 | 3 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 4 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository") 5 | 6 | # `abseil-cpp` required by `pybind11_protobuf` 7 | http_archive( 8 | name = "com_google_absl", 9 | sha256 = "dcf71b9cba8dc0ca9940c4b316a0c796be8fab42b070bb6b7cab62b48f0e66c4", # SHARED_ABSL_SHA 10 | strip_prefix = "abseil-cpp-20211102.0", 11 | urls = [ 12 | "https://github.com/abseil/abseil-cpp/archive/refs/tags/20211102.0.tar.gz", 13 | ], 14 | ) 15 | 16 | # `pybind11_bazel` 17 | http_archive( 18 | name = "pybind11_bazel", 19 | strip_prefix = "pybind11_bazel-72cbbf1fbc830e487e3012862b7b720001b70672", 20 | sha256 = "516c1b3a10d87740d2b7de6f121f8e19dde2c372ecbfe59aef44cd1872c10395", 21 | urls = ["https://github.com/pybind/pybind11_bazel/archive/72cbbf1fbc830e487e3012862b7b720001b70672.tar.gz"], 22 | ) 23 | 24 | # `pybind11` 25 | http_archive( 26 | name = "pybind11", 27 | build_file = "@pybind11_bazel//:pybind11.BUILD", 28 | strip_prefix = "pybind11-2.9.0", 29 | sha256 = "057fb68dafd972bc13afb855f3b0d8cf0fa1a78ef053e815d9af79be7ff567cb", 30 | urls = ["https://github.com/pybind/pybind11/archive/refs/tags/v2.9.0.tar.gz"], 31 | ) 32 | 33 | load("@pybind11_bazel//:python_configure.bzl", "python_configure") 34 | python_configure(name = "local_config_python") 35 | 36 | # `pybind11_protobuf` 37 | git_repository( 38 | name = "pybind11_protobuf", 39 | commit = "30f02dd9ccd2fc7046c36ed913ed510fd1aa7301", # Latest (2022-01-05) 40 | remote = "https://github.com/pybind/pybind11_protobuf.git", 41 | ) 42 | 43 | # proto_library, cc_proto_library, and java_proto_library rules implicitly 44 | # depend on @com_google_protobuf for protoc and proto runtimes. 45 | # This statement defines the @com_google_protobuf repo. 46 | # 47 | # The patch is required for @pybind11_protobuf//:pybind11_protobuf:proto_cast_util 48 | # 49 | http_archive( 50 | name = "com_google_protobuf", 51 | sha256 = "87407cd28e7a9c95d9f61a098a53cf031109d451a7763e7dd1253abf8b4df422", 52 | strip_prefix = "protobuf-3.19.1", 53 | urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.19.1.tar.gz"], 54 | patches = [ 55 | "com_google_protobuf_build.patch", 56 | ], 57 | ) 58 | 59 | load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") 60 | protobuf_deps() 61 | 62 | # GRPC v1.42, for proto rules. 63 | # For a related discussion of the pro/cons of various open-source py proto rule 64 | # repositories, see b/189457935. 65 | # http_archive( 66 | # name = "com_github_grpc_grpc", 67 | # sha256 = "9f387689b7fdf6c003fd90ef55853107f89a2121792146770df5486f0199f400", 68 | # strip_prefix = "grpc-1.42.0", 69 | # urls = ["https://github.com/grpc/grpc/archive/v1.42.0.zip"], 70 | # ) 71 | 72 | # load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") 73 | # grpc_deps() 74 | 75 | # Protobuf and gRPC rules for Bazel 76 | # https://rules-proto-grpc.com/en/latest/index.html 77 | http_archive( 78 | name = "rules_proto_grpc", 79 | sha256 = "507e38c8d95c7efa4f3b1c0595a8e8f139c885cb41a76cab7e20e4e67ae87731", 80 | strip_prefix = "rules_proto_grpc-4.1.1", 81 | urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/archive/4.1.1.tar.gz"], 82 | ) 83 | 84 | load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains", "rules_proto_grpc_repos") 85 | rules_proto_grpc_toolchains() 86 | rules_proto_grpc_repos() 87 | 88 | load("@rules_proto_grpc//cpp:repositories.bzl", "cpp_repos") 89 | cpp_repos() 90 | 91 | load("@rules_proto_grpc//python:repositories.bzl", rules_proto_grpc_python_repos = "python_repos") 92 | rules_proto_grpc_python_repos() 93 | 94 | load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") 95 | rules_proto_dependencies() 96 | rules_proto_toolchains() 97 | 98 | # required when using --define use_fast_cpp_protos=true 99 | bind( 100 | name = "python_headers", 101 | actual = "@local_config_python//:python_headers", 102 | ) 103 | 104 | # `gz-msgs9` 105 | # https://stackoverflow.com/questions/50592846/pull-github-repository-using-bazel 106 | # strip the `proto` prefix so that generated protobuf bindings for gz-msgs 107 | # have the correct include path which is `gz.msgs` 108 | new_git_repository( 109 | name = "gz-msgs9", 110 | commit = "6da277056e867e8b60c0b48ff88df9b3c5a49c9a", # Latest (2022-01-01) 111 | remote = "https://github.com/gazebosim/gz-msgs.git", 112 | strip_prefix = "proto", 113 | build_file = "//:gz-msgs9.BUILD" 114 | ) 115 | 116 | # `gz-transport12` 117 | git_repository( 118 | name = "gz-transport12", 119 | commit = "185961ff1c97583c50d67c65a5b0c9bb9c64f5d5", # Latest (2022-01-01) 120 | remote = "https://github.com/gazebosim/gz-transport.git", 121 | ) 122 | -------------------------------------------------------------------------------- /cmake/dependencies/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | #============================================================================ 4 | # Declare all dependencies first 5 | 6 | find_package(Protobuf ${_protobuf_version} EXACT QUIET) 7 | if(NOT Protobuf_FOUND) 8 | set(protobuf_BUILD_TESTS OFF CACHE INTERNAL "") 9 | FetchContent_Declare( 10 | Protobuf 11 | GIT_REPOSITORY ${_protobuf_repository} 12 | GIT_TAG ${_protobuf_tag} 13 | GIT_SUBMODULES "" 14 | ) 15 | endif() 16 | 17 | find_package(pybind11_protobuf ${_pybind11_protobuf_version} EXACT QUIET) 18 | if(NOT pybind11_protobuf_FOUND) 19 | FetchContent_Declare( 20 | pybind11_protobuf 21 | GIT_REPOSITORY ${_pybind11_protobuf_repository} 22 | GIT_TAG ${_pybind11_protobuf_tag} 23 | ) 24 | endif() 25 | 26 | #============================================================================ 27 | # Make dependencies avaialble 28 | 29 | if(NOT Protobuf_FOUND) 30 | message(CHECK_START "Fetching Protobuf") 31 | list(APPEND CMAKE_MESSAGE_INDENT " ") 32 | FetchContent_MakeAvailable(Protobuf) 33 | list(POP_BACK CMAKE_MESSAGE_INDENT) 34 | message(CHECK_PASS "fetched") 35 | endif() 36 | 37 | if(NOT pybind11_protobuf_FOUND) 38 | message(CHECK_START "Fetching pybind11_protobuf") 39 | list(APPEND CMAKE_MESSAGE_INDENT " ") 40 | FetchContent_MakeAvailable(pybind11_protobuf) 41 | list(POP_BACK CMAKE_MESSAGE_INDENT) 42 | message(CHECK_PASS "fetched") 43 | endif() 44 | 45 | -------------------------------------------------------------------------------- /gz-msgs9.BUILD: -------------------------------------------------------------------------------- 1 | #——————————————————————————————————————————————————————————————————————— 2 | # BUILD file for gz-msgs9 3 | # 4 | # Using https://rules-proto-grpc.com 5 | # 6 | # C++ 7 | # https://rules-proto-grpc.com/en/latest/example.html 8 | # 9 | # Python 10 | # https://rules-proto-grpc.com/en/latest/lang/python.html 11 | # 12 | # Notes: python_proto_library 13 | # - Use output_mode = "NO_PREFIX" to use only the protobuf 14 | # import path gz.msgs. The default PREFIX will place 15 | # each Python protobuf library under an extra folder with 16 | # the target name. 17 | # 18 | # TODO(srmainwaring): 19 | # Create a macro to generate the libraries for each .proto 20 | # Main challenge will be to extract the deps 21 | # 22 | 23 | load("@rules_proto_grpc//cpp:defs.bzl", "cpp_proto_library") 24 | load("@rules_proto_grpc//python:defs.bzl", "python_proto_library") 25 | 26 | package(default_visibility = ["//visibility:public"]) 27 | 28 | #——————————————————————————————————————————————————————————————————————— 29 | # proto_files 30 | 31 | # load("@gz-msgs9//:protobuf.bzl", 32 | # "get_include_directory", 33 | # "proto_path_to_generated_filename", 34 | # ) 35 | 36 | # load("@//:rules.bzl", "strip_proto_extension") 37 | 38 | # get all .proto files 39 | # proto_srcs = glob([ 40 | # "proto/gz/msgs/*.proto", 41 | # ]) 42 | 43 | # proto_srcs = [ 44 | # "proto/gz/msgs/any.proto", 45 | # ] 46 | 47 | # deps = get_include_directory(proto_srcs[0]) 48 | 49 | # test rule name generation 50 | # [genrule( 51 | # name = proto_path_to_generated_filename(src, "{}_proto2"), 52 | # srcs = [src], 53 | # outs = [], 54 | # cmd = "", 55 | # ) for src in proto_srcs] 56 | 57 | #——————————————————————————————————————————————————————————————————————— 58 | # any.proto 59 | proto_library( 60 | name = "any_proto", 61 | srcs = ["proto/gz/msgs/any.proto"], 62 | deps = [ 63 | ":header_proto", 64 | ":color_proto", 65 | ":pose_proto", 66 | ":quaternion_proto", 67 | ":time_proto", 68 | ":vector3d_proto", 69 | ], 70 | strip_import_prefix = "proto", 71 | ) 72 | 73 | cpp_proto_library( 74 | name = "any_cc_proto", 75 | protos = [":any_proto"], 76 | deps = [ 77 | ":header_cc_proto", 78 | ":color_cc_proto", 79 | ":pose_cc_proto", 80 | ":quaternion_cc_proto", 81 | ":time_cc_proto", 82 | ":vector3d_cc_proto", 83 | ], 84 | ) 85 | 86 | python_proto_library( 87 | name = "any_py_pb2", 88 | protos = [":any_proto"], 89 | deps = [ 90 | ":header_py_pb2", 91 | ":color_py_pb2", 92 | ":pose_py_pb2", 93 | ":quaternion_py_pb2", 94 | ":time_py_pb2", 95 | ":vector3d_py_pb2", 96 | ], 97 | output_mode = "NO_PREFIX", 98 | ) 99 | 100 | 101 | #——————————————————————————————————————————————————————————————————————— 102 | # color.proto 103 | proto_library( 104 | name = "color_proto", 105 | srcs = ["proto/gz/msgs/color.proto"], 106 | deps = [ 107 | ":header_proto", 108 | ], 109 | strip_import_prefix = "proto", 110 | ) 111 | 112 | cpp_proto_library( 113 | name = "color_cc_proto", 114 | protos = [":color_proto"], 115 | deps = [ 116 | ":header_cc_proto", 117 | ], 118 | ) 119 | 120 | python_proto_library( 121 | name = "color_py_pb2", 122 | protos = [":color_proto"], 123 | deps = [ 124 | ":header_py_pb2", 125 | ], 126 | output_mode = "NO_PREFIX", 127 | ) 128 | 129 | #——————————————————————————————————————————————————————————————————————— 130 | # cmd_vel2d.proto 131 | proto_library( 132 | name = "cmd_vel2d_proto", 133 | srcs = ["proto/gz/msgs/cmd_vel2d.proto"], 134 | deps = [ 135 | ":header_proto", 136 | ], 137 | strip_import_prefix = "proto", 138 | ) 139 | 140 | cpp_proto_library( 141 | name = "cmd_vel2d_cc_proto", 142 | protos = [":cmd_vel2d_proto"], 143 | deps = [ 144 | ":header_cc_proto", 145 | ], 146 | ) 147 | 148 | python_proto_library( 149 | name = "cmd_vel2d_py_pb2", 150 | protos = [":cmd_vel2d_proto"], 151 | deps = [ 152 | ":header_py_pb2", 153 | ], 154 | output_mode = "NO_PREFIX", 155 | ) 156 | 157 | #——————————————————————————————————————————————————————————————————————— 158 | # double.proto 159 | proto_library( 160 | name = "double_proto", 161 | srcs = ["proto/gz/msgs/double.proto"], 162 | deps = [ 163 | ":header_proto", 164 | ], 165 | strip_import_prefix = "proto", 166 | ) 167 | 168 | cpp_proto_library( 169 | name = "double_cc_proto", 170 | protos = [":double_proto"], 171 | deps = [ 172 | ":header_cc_proto", 173 | ], 174 | ) 175 | 176 | python_proto_library( 177 | name = "double_py_pb2", 178 | protos = [":double_proto"], 179 | deps = [ 180 | ":header_py_pb2", 181 | ], 182 | output_mode = "NO_PREFIX", 183 | ) 184 | 185 | #——————————————————————————————————————————————————————————————————————— 186 | # double_v.proto 187 | proto_library( 188 | name = "double_v_proto", 189 | srcs = ["proto/gz/msgs/double_v.proto"], 190 | deps = [ 191 | ":double_proto", 192 | ":header_proto", 193 | ], 194 | strip_import_prefix = "proto", 195 | ) 196 | 197 | cpp_proto_library( 198 | name = "double_v_cc_proto", 199 | protos = [":double_v_proto"], 200 | deps = [ 201 | ":double_cc_proto", 202 | ":header_cc_proto", 203 | ], 204 | ) 205 | 206 | python_proto_library( 207 | name = "double_v_py_pb2", 208 | protos = [ 209 | ":double_v_proto", 210 | ], 211 | deps = [ 212 | ":double_py_pb2", 213 | ":header_py_pb2", 214 | ], 215 | output_mode = "NO_PREFIX", 216 | ) 217 | 218 | #——————————————————————————————————————————————————————————————————————— 219 | # float.proto 220 | proto_library( 221 | name = "float_proto", 222 | srcs = ["proto/gz/msgs/float.proto"], 223 | deps = [ 224 | ":header_proto", 225 | ], 226 | strip_import_prefix = "proto", 227 | ) 228 | 229 | cpp_proto_library( 230 | name = "float_cc_proto", 231 | protos = [":float_proto"], 232 | deps = [ 233 | ":header_cc_proto", 234 | ], 235 | ) 236 | 237 | python_proto_library( 238 | name = "float_py_pb2", 239 | protos = [":float_proto"], 240 | deps = [ 241 | ":header_py_pb2", 242 | ], 243 | output_mode = "NO_PREFIX", 244 | ) 245 | 246 | #——————————————————————————————————————————————————————————————————————— 247 | # float_v.proto 248 | proto_library( 249 | name = "float_v_proto", 250 | srcs = ["proto/gz/msgs/float_v.proto"], 251 | deps = [ 252 | ":float_proto", 253 | ":header_proto", 254 | ], 255 | strip_import_prefix = "proto", 256 | ) 257 | 258 | cpp_proto_library( 259 | name = "float_v_cc_proto", 260 | protos = [":float_v_proto"], 261 | deps = [ 262 | ":float_cc_proto", 263 | ":header_cc_proto", 264 | ], 265 | ) 266 | 267 | python_proto_library( 268 | name = "float_v_py_pb2", 269 | protos = [":float_v_proto"], 270 | deps = [ 271 | ":float_py_pb2", 272 | ":header_py_pb2", 273 | ], 274 | output_mode = "NO_PREFIX", 275 | ) 276 | 277 | #——————————————————————————————————————————————————————————————————————— 278 | # header.proto 279 | proto_library( 280 | name = "header_proto", 281 | srcs = ["proto/gz/msgs/header.proto"], 282 | deps = [":time_proto"], 283 | strip_import_prefix = "proto", 284 | ) 285 | 286 | cpp_proto_library( 287 | name = "header_cc_proto", 288 | protos = [":header_proto"], 289 | deps = [ 290 | ":time_cc_proto", 291 | ], 292 | ) 293 | 294 | python_proto_library( 295 | name = "header_py_pb2", 296 | protos = [":header_proto"], 297 | deps = [ 298 | ":time_py_pb2", 299 | ], 300 | output_mode = "NO_PREFIX", 301 | ) 302 | 303 | #——————————————————————————————————————————————————————————————————————— 304 | # param.proto 305 | proto_library( 306 | name = "param_proto", 307 | srcs = ["proto/gz/msgs/param.proto"], 308 | deps = [ 309 | ":any_proto", 310 | ":header_proto", 311 | ], 312 | strip_import_prefix = "proto", 313 | ) 314 | 315 | cpp_proto_library( 316 | name = "param_cc_proto", 317 | protos = [":param_proto"], 318 | deps = [ 319 | ":any_cc_proto", 320 | ":header_cc_proto", 321 | ], 322 | ) 323 | 324 | python_proto_library( 325 | name = "param_py_pb2", 326 | protos = [":param_proto"], 327 | deps = [ 328 | ":any_py_pb2", 329 | ":header_py_pb2", 330 | ], 331 | output_mode = "NO_PREFIX", 332 | ) 333 | 334 | #——————————————————————————————————————————————————————————————————————— 335 | # pid.proto 336 | proto_library( 337 | name = "pid_proto", 338 | srcs = ["proto/gz/msgs/pid.proto"], 339 | deps = [ 340 | ":double_proto", 341 | ":header_proto", 342 | ], 343 | strip_import_prefix = "proto", 344 | ) 345 | 346 | cpp_proto_library( 347 | name = "pid_cc_proto", 348 | protos = [":pid_proto"], 349 | deps = [ 350 | ":double_cc_proto", 351 | ":header_cc_proto", 352 | ], 353 | ) 354 | 355 | python_proto_library( 356 | name = "pid_py_pb2", 357 | protos = [":pid_proto"], 358 | deps = [ 359 | ":double_py_pb2", 360 | ":header_py_pb2", 361 | ], 362 | output_mode = "NO_PREFIX", 363 | ) 364 | 365 | #——————————————————————————————————————————————————————————————————————— 366 | # pose.proto 367 | proto_library( 368 | name = "pose_proto", 369 | srcs = ["proto/gz/msgs/pose.proto"], 370 | deps = [ 371 | ":header_proto", 372 | ":quaternion_proto", 373 | ":vector3d_proto", 374 | ], 375 | strip_import_prefix = "proto", 376 | ) 377 | 378 | cpp_proto_library( 379 | name = "pose_cc_proto", 380 | protos = [":pose_proto"], 381 | deps = [ 382 | ":header_cc_proto", 383 | ":quaternion_cc_proto", 384 | ":vector3d_cc_proto", 385 | ], 386 | ) 387 | 388 | python_proto_library( 389 | name = "pose_py_pb2", 390 | protos = [":pose_proto"], 391 | deps = [ 392 | ":header_py_pb2", 393 | ":quaternion_py_pb2", 394 | ":vector3d_py_pb2", 395 | ], 396 | output_mode = "NO_PREFIX", 397 | ) 398 | 399 | #——————————————————————————————————————————————————————————————————————— 400 | # pose_v.proto 401 | proto_library( 402 | name = "pose_v_proto", 403 | srcs = ["proto/gz/msgs/pose_v.proto"], 404 | deps = [ 405 | ":header_proto", 406 | ":pose_proto", 407 | ], 408 | strip_import_prefix = "proto", 409 | ) 410 | 411 | cpp_proto_library( 412 | name = "pose_v_cc_proto", 413 | protos = [":pose_v_proto"], 414 | deps = [ 415 | ":header_cc_proto", 416 | ":pose_cc_proto", 417 | ], 418 | ) 419 | 420 | python_proto_library( 421 | name = "pose_v_py_pb2", 422 | protos = [":pose_v_proto"], 423 | deps = [ 424 | ":header_py_pb2", 425 | ":pose_py_pb2", 426 | ], 427 | output_mode = "NO_PREFIX", 428 | ) 429 | 430 | #——————————————————————————————————————————————————————————————————————— 431 | # publish.proto 432 | proto_library( 433 | name = "publish_proto", 434 | srcs = ["proto/gz/msgs/publish.proto"], 435 | deps = [ 436 | ":header_proto", 437 | ], 438 | strip_import_prefix = "proto", 439 | ) 440 | 441 | cpp_proto_library( 442 | name = "publish_cc_proto", 443 | protos = [":publish_proto"], 444 | deps = [ 445 | ":header_cc_proto", 446 | ], 447 | ) 448 | 449 | python_proto_library( 450 | name = "publish_py_pb2", 451 | protos = [":publish_proto"], 452 | deps = [ 453 | ":header_py_pb2", 454 | ], 455 | output_mode = "NO_PREFIX", 456 | ) 457 | 458 | #——————————————————————————————————————————————————————————————————————— 459 | # publishers.proto 460 | proto_library( 461 | name = "publishers_proto", 462 | srcs = ["proto/gz/msgs/publishers.proto"], 463 | deps = [ 464 | ":header_proto", 465 | ":publish_proto", 466 | ], 467 | strip_import_prefix = "proto", 468 | ) 469 | 470 | cpp_proto_library( 471 | name = "publishers_cc_proto", 472 | protos = [":publishers_proto"], 473 | deps = [ 474 | ":header_cc_proto", 475 | ":publish_cc_proto", 476 | ], 477 | ) 478 | 479 | python_proto_library( 480 | name = "publishers_py_pb2", 481 | protos = [":publishers_proto"], 482 | deps = [ 483 | ":header_py_pb2", 484 | ":publish_py_pb2", 485 | ], 486 | output_mode = "NO_PREFIX", 487 | ) 488 | 489 | #——————————————————————————————————————————————————————————————————————— 490 | # quaternion.proto 491 | proto_library( 492 | name = "quaternion_proto", 493 | srcs = ["proto/gz/msgs/quaternion.proto"], 494 | deps = [ 495 | ":header_proto", 496 | ], 497 | strip_import_prefix = "proto", 498 | ) 499 | 500 | cpp_proto_library( 501 | name = "quaternion_cc_proto", 502 | protos = [":quaternion_proto"], 503 | deps = [ 504 | ":header_cc_proto", 505 | ], 506 | ) 507 | 508 | python_proto_library( 509 | name = "quaternion_py_pb2", 510 | protos = [":quaternion_proto"], 511 | deps = [ 512 | ":header_py_pb2", 513 | ], 514 | output_mode = "NO_PREFIX", 515 | ) 516 | 517 | #——————————————————————————————————————————————————————————————————————— 518 | # stringmsg.proto 519 | proto_library( 520 | name = "stringmsg_proto", 521 | srcs = ["proto/gz/msgs/stringmsg.proto"], 522 | deps = [ 523 | ":header_proto", 524 | ], 525 | strip_import_prefix = "proto", 526 | ) 527 | 528 | cpp_proto_library( 529 | name = "stringmsg_cc_proto", 530 | protos = [":stringmsg_proto"], 531 | deps = [ 532 | ":header_cc_proto", 533 | ], 534 | ) 535 | 536 | python_proto_library( 537 | name = "stringmsg_py_pb2", 538 | protos = [":stringmsg_proto"], 539 | deps = [ 540 | ":header_py_pb2", 541 | ], 542 | output_mode = "NO_PREFIX", 543 | ) 544 | 545 | #——————————————————————————————————————————————————————————————————————— 546 | # subscribe.proto 547 | proto_library( 548 | name = "subscribe_proto", 549 | srcs = ["proto/gz/msgs/subscribe.proto"], 550 | deps = [ 551 | ":header_proto" 552 | ], 553 | strip_import_prefix = "proto", 554 | ) 555 | 556 | cpp_proto_library( 557 | name = "subscribe_cc_proto", 558 | protos = [":subscribe_proto"], 559 | deps = [ 560 | ":header_cc_proto" 561 | ], 562 | ) 563 | 564 | python_proto_library( 565 | name = "subscribe_py_pb2", 566 | protos = [":subscribe_proto"], 567 | deps = [ 568 | ":header_py_pb2", 569 | ], 570 | output_mode = "NO_PREFIX", 571 | ) 572 | 573 | #——————————————————————————————————————————————————————————————————————— 574 | # time.proto 575 | proto_library( 576 | name = "time_proto", 577 | srcs = ["proto/gz/msgs/time.proto"], 578 | strip_import_prefix = "proto", 579 | ) 580 | 581 | cpp_proto_library( 582 | name = "time_cc_proto", 583 | protos = [":time_proto"], 584 | ) 585 | 586 | python_proto_library( 587 | name = "time_py_pb2", 588 | protos = [":time_proto"], 589 | output_mode = "NO_PREFIX", 590 | ) 591 | 592 | #——————————————————————————————————————————————————————————————————————— 593 | # topic_info.proto 594 | proto_library( 595 | name = "topic_info_proto", 596 | srcs = ["proto/gz/msgs/topic_info.proto"], 597 | deps = [ 598 | ":header_proto", 599 | ":publish_proto", 600 | ":subscribe_proto", 601 | ], 602 | strip_import_prefix = "proto", 603 | ) 604 | 605 | cpp_proto_library( 606 | name = "topic_info_cc_proto", 607 | protos = [":topic_info_proto"], 608 | deps = [ 609 | ":header_cc_proto", 610 | ":publish_cc_proto", 611 | ":subscribe_cc_proto", 612 | ], 613 | ) 614 | 615 | python_proto_library( 616 | name = "topic_info_py_pb2", 617 | protos = [":topic_info_proto"], 618 | deps = [ 619 | ":header_py_pb2", 620 | ":publish_py_pb2", 621 | ":subscribe_py_pb2", 622 | ], 623 | output_mode = "NO_PREFIX", 624 | ) 625 | 626 | #——————————————————————————————————————————————————————————————————————— 627 | # twist.proto 628 | proto_library( 629 | name = "twist_proto", 630 | srcs = ["proto/gz/msgs/twist.proto"], 631 | deps = [ 632 | ":header_proto", 633 | ":vector3d_proto", 634 | ], 635 | strip_import_prefix = "proto", 636 | ) 637 | 638 | cpp_proto_library( 639 | name = "twist_cc_proto", 640 | protos = [":twist_proto"], 641 | deps = [ 642 | ":header_cc_proto", 643 | ":vector3d_cc_proto", 644 | ], 645 | ) 646 | 647 | python_proto_library( 648 | name = "twist_py_pb2", 649 | protos = [":twist_proto"], 650 | deps = [ 651 | ":header_py_pb2", 652 | ":vector3d_py_pb2", 653 | ], 654 | output_mode = "NO_PREFIX", 655 | ) 656 | 657 | #——————————————————————————————————————————————————————————————————————— 658 | # vector3d.proto 659 | proto_library( 660 | name = "vector3d_proto", 661 | srcs = ["proto/gz/msgs/vector3d.proto"], 662 | deps = [ 663 | ":header_proto", 664 | ], 665 | strip_import_prefix = "proto", 666 | ) 667 | 668 | cpp_proto_library( 669 | name = "vector3d_cc_proto", 670 | protos = [":vector3d_proto"], 671 | deps = [ 672 | ":header_cc_proto", 673 | ], 674 | ) 675 | 676 | python_proto_library( 677 | name = "vector3d_py_pb2", 678 | protos = [":vector3d_proto"], 679 | deps = [ 680 | ":header_py_pb2", 681 | ], 682 | output_mode = "NO_PREFIX", 683 | ) 684 | 685 | #——————————————————————————————————————————————————————————————————————— 686 | # wrench.proto 687 | proto_library( 688 | name = "wrench_proto", 689 | srcs = ["proto/gz/msgs/wrench.proto"], 690 | deps = [ 691 | ":header_proto", 692 | ":vector3d_proto", 693 | ], 694 | strip_import_prefix = "proto", 695 | ) 696 | 697 | cpp_proto_library( 698 | name = "wrench_cc_proto", 699 | protos = [":wrench_proto"], 700 | deps = [ 701 | ":header_cc_proto", 702 | ":vector3d_cc_proto", 703 | ], 704 | ) 705 | 706 | python_proto_library( 707 | name = "wrench_py_pb2", 708 | protos = [":wrench_proto"], 709 | deps = [ 710 | ":header_py_pb2", 711 | ":vector3d_py_pb2", 712 | ], 713 | output_mode = "NO_PREFIX", 714 | ) 715 | -------------------------------------------------------------------------------- /include/BUILD: -------------------------------------------------------------------------------- 1 | #——————————————————————————————————————————————————————————————————————— 2 | # gz_msgs_extras_hh 3 | # 4 | cc_library( 5 | name = "gz_msgs_extras_hdrs", 6 | hdrs = ["gz/msgs/extras.hh"], 7 | includes = ["."], 8 | deps = [ 9 | "@gz-msgs9//:time_cc_proto", 10 | "@gz-msgs9//:topic_info_cc_proto", 11 | "@gz-msgs9//:wrench_cc_proto", 12 | ], 13 | visibility = ["//visibility:public"], 14 | ) 15 | -------------------------------------------------------------------------------- /include/gz/msgs/extras.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #ifndef GZ_PYTHON_MSGS_HH 19 | #define GZ_PYTHON_MSGS_HH 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace gz 26 | { 27 | namespace msgs 28 | { 29 | namespace extras 30 | { 31 | 32 | Time MakeTime(); 33 | 34 | void TakeTime(const Time& msg); 35 | 36 | void TakeTopicInfo(const TopicInfo& msg); 37 | 38 | void TakeWrench(const Wrench& msg); 39 | } // extras 40 | } // msgs 41 | } // gz 42 | 43 | #endif // GZ_PYTHON_MSGS_HH 44 | -------------------------------------------------------------------------------- /python/BUILD: -------------------------------------------------------------------------------- 1 | #——————————————————————————————————————————————————————————————————————— 2 | # Python examples 3 | # 4 | # 5 | # Notes: 6 | # 7 | # In py_binary the flag: 8 | # 9 | # legacy_create_init = False 10 | # 11 | # Because there are two `gz` packages. One coming from @gz-msgs9 12 | # that contains the generated protbuf Python bindings, and the other 13 | # from the pybind11 extension modules. The default behaviour is for Bazel 14 | # to generate empty __init__.py files in all subdirectories under the 15 | # .runfiles directory in bazel-bin. In that case only the 16 | # first occurrence of `gz` on the sys.path will be loaded and an 17 | # attempt to import the other package results in module not found errors. 18 | # 19 | # See the function documentation for `py_binary`: 20 | # Bazel Python Rules: https://docs.bazel.build/versions/main/be/python.html 21 | # 22 | # 23 | #——————————————————————————————————————————————————————————————————————— 24 | 25 | package(default_visibility = ["//visibility:public"]) 26 | 27 | #——————————————————————————————————————————————————————————————————————— 28 | # gz_msgs_py_deps: list of all the generated Python protobuf libraries 29 | # 30 | gz_msgs_py_deps = [ 31 | "@gz-msgs9//:any_py_pb2", 32 | "@gz-msgs9//:color_py_pb2", 33 | "@gz-msgs9//:cmd_vel2d_py_pb2", 34 | "@gz-msgs9//:double_py_pb2", 35 | "@gz-msgs9//:double_v_py_pb2", 36 | "@gz-msgs9//:float_py_pb2", 37 | "@gz-msgs9//:float_v_py_pb2", 38 | "@gz-msgs9//:header_py_pb2", 39 | "@gz-msgs9//:param_py_pb2", 40 | "@gz-msgs9//:pid_py_pb2", 41 | "@gz-msgs9//:pose_py_pb2", 42 | "@gz-msgs9//:pose_v_py_pb2", 43 | "@gz-msgs9//:publish_py_pb2", 44 | "@gz-msgs9//:publishers_py_pb2", 45 | "@gz-msgs9//:quaternion_py_pb2", 46 | "@gz-msgs9//:stringmsg_py_pb2", 47 | "@gz-msgs9//:subscribe_py_pb2", 48 | "@gz-msgs9//:time_py_pb2", 49 | "@gz-msgs9//:topic_info_py_pb2", 50 | "@gz-msgs9//:twist_py_pb2", 51 | "@gz-msgs9//:vector3d_py_pb2", 52 | "@gz-msgs9//:wrench_py_pb2", 53 | ] 54 | 55 | #——————————————————————————————————————————————————————————————————————— 56 | # import_check 57 | # 58 | py_binary( 59 | name = "import_check", 60 | srcs = ["import_check.py"], 61 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 62 | legacy_create_init = False, 63 | ) 64 | 65 | #——————————————————————————————————————————————————————————————————————— 66 | # msgs_example 67 | # 68 | py_binary( 69 | name = "msgs_example", 70 | srcs = ["msgs_example.py"], 71 | deps = gz_msgs_py_deps, 72 | legacy_create_init = False, 73 | ) 74 | 75 | #——————————————————————————————————————————————————————————————————————— 76 | # transport_example 77 | # 78 | py_binary( 79 | name = "transport_example", 80 | srcs = ["transport_example.py"], 81 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 82 | legacy_create_init = False, 83 | ) 84 | 85 | #——————————————————————————————————————————————————————————————————————— 86 | # publisher 87 | # 88 | # Example: publishes to topic /foo [gz.msgs.StringMsg] 89 | # 90 | py_binary( 91 | name = "publisher", 92 | srcs = ["publisher.py"], 93 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 94 | legacy_create_init = False, 95 | ) 96 | 97 | #——————————————————————————————————————————————————————————————————————— 98 | # subscriber 99 | # 100 | # Example: subscribes to topic /foo [gz.msgs.StringMsg] 101 | # 102 | py_binary( 103 | name = "subscriber", 104 | srcs = ["subscriber.py"], 105 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 106 | legacy_create_init = False, 107 | ) 108 | 109 | #——————————————————————————————————————————————————————————————————————— 110 | # rover_subscriber 111 | # 112 | # Example: publishes to topics /pose [gz.msgs.Pose] 113 | # and /twist [gz.msgs.Twist] 114 | # 115 | py_binary( 116 | name = "rover_publisher", 117 | srcs = ["rover_publisher.py"], 118 | data = [ 119 | "@com_google_protobuf//:proto_api", 120 | ], 121 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 122 | legacy_create_init = False, 123 | ) 124 | 125 | #——————————————————————————————————————————————————————————————————————— 126 | # rover_subscriber 127 | # 128 | # Example: subscribes to topics /pose [gz.msgs.Pose] 129 | # and /twist [gz.msgs.Twist] 130 | # 131 | py_binary( 132 | name = "rover_subscriber", 133 | srcs = ["rover_subscriber.py"], 134 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 135 | legacy_create_init = False, 136 | ) 137 | 138 | #——————————————————————————————————————————————————————————————————————— 139 | # pub_all_msg_types 140 | # 141 | py_binary( 142 | name = "pub_all_msg_types", 143 | srcs = ["pub_all_msg_types.py"], 144 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 145 | legacy_create_init = False, 146 | ) 147 | 148 | #——————————————————————————————————————————————————————————————————————— 149 | # gz_topic_list 150 | # 151 | # Replicates command: gz topic -l 152 | # 153 | py_binary( 154 | name = "gz_topic_list", 155 | srcs = ["gz_topic_list.py"], 156 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 157 | legacy_create_init = False, 158 | ) 159 | 160 | #——————————————————————————————————————————————————————————————————————— 161 | # gz_topic_info 162 | # 163 | # Replicates command: gz topic -i -t /topic 164 | # 165 | py_binary( 166 | name = "gz_topic_info", 167 | srcs = ["gz_topic_info.py"], 168 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 169 | legacy_create_init = False, 170 | ) 171 | 172 | #——————————————————————————————————————————————————————————————————————— 173 | # gz_topic_echo 174 | # 175 | # Note: this can only echo messages included in the deps = [] 176 | # 177 | py_binary( 178 | name = "gz_topic_echo", 179 | srcs = ["gz_topic_echo.py"], 180 | deps = gz_msgs_py_deps + ["//gz_python:gz_py"], 181 | legacy_create_init = False, 182 | ) 183 | 184 | -------------------------------------------------------------------------------- /python/gz_service_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ''' 18 | Replicate the gz_tools command: 19 | 20 | $ gz service -i -s /service 21 | ''' 22 | 23 | import argparse 24 | 25 | from gz.transport import Node 26 | 27 | def main(): 28 | # process command line 29 | parser = argparse.ArgumentParser(description="Get info about a service.") 30 | parser.add_argument("-s", "--service", 31 | metavar="service", required=True, help="Name of a service") 32 | args = parser.parse_args() 33 | 34 | # service is required 35 | service = args.service 36 | 37 | # create a transport node 38 | node = Node() 39 | 40 | # get list of service info 41 | service_info_list = node.service_info(service) 42 | 43 | # display address and message types 44 | print("Service providers [Address, Request Message Type, Response Message Type]:") 45 | for service_info in service_info_list: 46 | print(" {}, {}, {}".format( 47 | service_info.addr, 48 | service_info.req_type_name, 49 | service_info.rep_type_name)) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | 55 | -------------------------------------------------------------------------------- /python/gz_service_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ''' 18 | Replicate the gz_tools command: 19 | 20 | $ gz service -l 21 | ''' 22 | 23 | from gz.transport import Node 24 | 25 | def main(): 26 | # create a transport node 27 | node = Node() 28 | 29 | # get list of services 30 | service_list = node.service_list() 31 | for service in service_list: 32 | print(service) 33 | 34 | if __name__ == "__main__": 35 | main() 36 | 37 | -------------------------------------------------------------------------------- /python/gz_topic_echo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ''' 18 | Replicate the gz_tools command: 19 | 20 | $ gz topic -i -e /topic 21 | ''' 22 | 23 | import argparse 24 | import time 25 | 26 | from gz.transport import SubscribeOptions 27 | from gz.transport import Node 28 | 29 | # callback - prints the raw message 30 | def cb(msg): 31 | print(msg) 32 | 33 | def main(): 34 | # process command line 35 | parser = argparse.ArgumentParser(description="Output data to screen.") 36 | parser.add_argument("-t", "--topic", 37 | metavar="topic", required=True, help="Name of a topic") 38 | args = parser.parse_args() 39 | 40 | # topic is required 41 | topic = args.topic 42 | 43 | # create a transport node 44 | node = Node() 45 | 46 | msg_type_name = "google.protobuf.Message" 47 | sub_options = SubscribeOptions() 48 | 49 | # subscribe to a topic by registering a callback 50 | if node.subscribe(topic, cb, msg_type_name, sub_options): 51 | print("Subscribing to topic [{}]".format(topic)) 52 | else: 53 | print("Error subscribing to topic [{}]".format(topic)) 54 | return 55 | 56 | # wait for shutdown 57 | try: 58 | while True: 59 | time.sleep(0.001) 60 | except KeyboardInterrupt: 61 | pass 62 | 63 | if __name__ == "__main__": 64 | main() 65 | 66 | -------------------------------------------------------------------------------- /python/gz_topic_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ''' 18 | Replicate the gz_tools command: 19 | 20 | $ gz topic -i -t /topic 21 | ''' 22 | 23 | import argparse 24 | 25 | from gz.transport import Node 26 | 27 | def main(): 28 | # process command line 29 | parser = argparse.ArgumentParser(description="Get info about a topic.") 30 | parser.add_argument("-t", "--topic", 31 | metavar="topic", required=True, help="Name of a topic") 32 | args = parser.parse_args() 33 | 34 | # topic is required 35 | topic = args.topic 36 | 37 | # create a transport node 38 | node = Node() 39 | 40 | # get list of topic info 41 | topic_info_list = node.topic_info(topic) 42 | 43 | # display address and message type 44 | print("Publishers [Address, Message Type]:") 45 | for topic_info in topic_info_list: 46 | print(" {}, {}".format(topic_info.addr, topic_info.msg_type_name)) 47 | 48 | 49 | if __name__ == "__main__": 50 | main() 51 | 52 | -------------------------------------------------------------------------------- /python/gz_topic_list.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ''' 18 | Replicate the gz_tools command: 19 | 20 | $ gz topic -l 21 | ''' 22 | 23 | from gz.transport import Node 24 | 25 | def main(): 26 | # create a transport node 27 | node = Node() 28 | 29 | # get list of topics 30 | topic_list = node.topic_list() 31 | for topic in topic_list: 32 | print(topic) 33 | 34 | if __name__ == "__main__": 35 | main() 36 | 37 | -------------------------------------------------------------------------------- /python/import_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import sys 18 | import time 19 | 20 | from gz.msgs.any_pb2 import Any 21 | from gz.msgs.header_pb2 import Header 22 | from gz.msgs.stringmsg_pb2 import StringMsg 23 | from gz.msgs.time_pb2 import Time 24 | 25 | from gz.msgs.extras import make_time 26 | from gz.msgs.extras import take_time 27 | from gz.msgs.extras import take_topic_info 28 | from gz.msgs.extras import take_wrench 29 | 30 | from gz.transport import AdvertiseMessageOptions 31 | from gz.transport import Node 32 | 33 | 34 | def main(): 35 | # for item in sys.path: 36 | # print(item) 37 | 38 | # msgs 39 | msg = Any() 40 | msg = Header() 41 | msg = StringMsg() 42 | 43 | # msgs functions 44 | msg = make_time() 45 | 46 | # transport types 47 | opts = AdvertiseMessageOptions() 48 | node = Node() 49 | pub = Node.Publisher() 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /python/msgs_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from gz.msgs.header_pb2 import Header 18 | from gz.msgs.double_pb2 import Double 19 | from gz.msgs.double_v_pb2 import Double_V 20 | from gz.msgs.float_pb2 import Float 21 | from gz.msgs.float_v_pb2 import Float_V 22 | from gz.msgs.cmd_vel2d_pb2 import CmdVel2D 23 | from gz.msgs.pid_pb2 import PID 24 | from gz.msgs.pose_pb2 import Pose 25 | from gz.msgs.pose_v_pb2 import Pose_V 26 | from gz.msgs.quaternion_pb2 import Quaternion 27 | from gz.msgs.time_pb2 import Time 28 | from gz.msgs.twist_pb2 import Twist 29 | from gz.msgs.vector3d_pb2 import Vector3d 30 | from gz.msgs.wrench_pb2 import Wrench 31 | 32 | from gz.msgs.publish_pb2 import Publish 33 | from gz.msgs.publishers_pb2 import Publishers 34 | from gz.msgs.subscribe_pb2 import Subscribe 35 | from gz.msgs.topic_info_pb2 import TopicInfo 36 | 37 | from google.protobuf.internal import api_implementation 38 | 39 | def main(): 40 | print("Gazebo Protobuf Example\n") 41 | 42 | #---------------------------------------------- 43 | # google.protobuf implementation 44 | print("proto api type: {}".format(api_implementation.Type())) 45 | 46 | print("----------------------------------------") 47 | time_msg = Time() 48 | time_msg.sec = 15 49 | time_msg.nsec = 21 50 | print(type(time_msg)) 51 | print(time_msg) 52 | 53 | print("----------------------------------------") 54 | header_msg = Header() 55 | header_msg.stamp.CopyFrom(time_msg) 56 | print(type(header_msg)) 57 | print(header_msg) 58 | 59 | print("----------------------------------------") 60 | double_msg = Double() 61 | double_msg.header.CopyFrom(header_msg) 62 | double_msg.data = 21.3 63 | print(type(double_msg)) 64 | print(double_msg) 65 | 66 | print("----------------------------------------") 67 | double_v_msg = Double_V() 68 | double_v_msg.data.append(21.4) 69 | double_v_msg.data.append(12.8) 70 | double_v_msg.data.append(15.6) 71 | double_v_msg.data.append(10.1) 72 | print(type(double_v_msg)) 73 | print(double_v_msg) 74 | 75 | print("----------------------------------------") 76 | float_msg = Float() 77 | float_msg.header.CopyFrom(header_msg) 78 | float_msg.data = 21.3 79 | print(type(float_msg)) 80 | print(float_msg) 81 | 82 | print("----------------------------------------") 83 | float_v_msg = Float_V() 84 | float_v_msg.header.CopyFrom(header_msg) 85 | float_v_msg.data.append(21.4) 86 | float_v_msg.data.append(12.8) 87 | float_v_msg.data.append(15.6) 88 | float_v_msg.data.append(10.1) 89 | print(type(float_v_msg)) 90 | print(float_v_msg) 91 | 92 | print("----------------------------------------") 93 | vector3d_msg = Vector3d() 94 | vector3d_msg.header.CopyFrom(header_msg) 95 | vector3d_msg.x = 21.3 96 | vector3d_msg.y = 12.7 97 | vector3d_msg.z = 15.2 98 | print(type(vector3d_msg)) 99 | print(vector3d_msg) 100 | 101 | print("----------------------------------------") 102 | quat_msg = Quaternion() 103 | quat_msg.header.CopyFrom(header_msg) 104 | quat_msg.x = 0.0 105 | quat_msg.y = 0.0 106 | quat_msg.z = 0.0 107 | quat_msg.w = 1.0 108 | print(type(quat_msg)) 109 | print(quat_msg) 110 | 111 | print("----------------------------------------") 112 | pose_msg = Pose() 113 | pose_msg.header.CopyFrom(header_msg) 114 | pose_msg.name = "base_link" 115 | pose_msg.id = 0 116 | pose_msg.position.CopyFrom(vector3d_msg) 117 | pose_msg.orientation.CopyFrom(quat_msg) 118 | print(type(pose_msg)) 119 | print(pose_msg) 120 | 121 | print("----------------------------------------") 122 | pose_v_msg = Pose_V() 123 | pose_v_msg.header.CopyFrom(header_msg) 124 | 125 | pose_msg.name = "base_link" 126 | pose_msg.id = 0 127 | pose_v_msg.pose.append(pose_msg) 128 | 129 | pose_msg.name = "left_motor_link" 130 | pose_msg.id = 1 131 | pose_v_msg.pose.append(pose_msg) 132 | 133 | pose_msg.name = "right_motor_link" 134 | pose_msg.id = 2 135 | pose_v_msg.pose.append(pose_msg) 136 | 137 | print(type(pose_v_msg)) 138 | print(pose_v_msg) 139 | 140 | print("----------------------------------------") 141 | cmd_vel_msg = CmdVel2D() 142 | cmd_vel_msg.header.CopyFrom(header_msg) 143 | cmd_vel_msg.velocity = 8.25 144 | cmd_vel_msg.theta = 1.517 145 | print(type(cmd_vel_msg)) 146 | print(cmd_vel_msg) 147 | 148 | print("----------------------------------------") 149 | pid_msg = PID() 150 | pid_msg.header.CopyFrom(header_msg) 151 | double_msg.data = 1.25 152 | pid_msg.target_optional.CopyFrom(double_msg) 153 | double_msg.data = 0.5 154 | pid_msg.p_gain_optional.CopyFrom(double_msg) 155 | double_msg.data = 0.05 156 | pid_msg.i_gain_optional.CopyFrom(double_msg) 157 | double_msg.data = 0.005 158 | pid_msg.d_gain_optional.CopyFrom(double_msg) 159 | double_msg.data = 1.0 160 | pid_msg.i_max_optional.CopyFrom(double_msg) 161 | double_msg.data = -1.0 162 | pid_msg.i_min_optional.CopyFrom(double_msg) 163 | double_msg.data = 10.0 164 | pid_msg.limit_optional.CopyFrom(double_msg) 165 | print(type(pid_msg)) 166 | print(pid_msg) 167 | 168 | print("----------------------------------------") 169 | twist_msg = Twist() 170 | twist_msg.header.CopyFrom(header_msg) 171 | twist_msg.linear.CopyFrom(vector3d_msg) 172 | twist_msg.angular.CopyFrom(vector3d_msg) 173 | print(type(twist_msg)) 174 | print(twist_msg) 175 | 176 | print("----------------------------------------") 177 | wrench_msg = Wrench() 178 | wrench_msg.header.CopyFrom(header_msg) 179 | wrench_msg.force.CopyFrom(vector3d_msg) 180 | wrench_msg.torque.CopyFrom(vector3d_msg) 181 | wrench_msg.force_offset.CopyFrom(vector3d_msg) 182 | print(type(wrench_msg)) 183 | print(wrench_msg) 184 | 185 | print("----------------------------------------") 186 | pub_msg = Publish() 187 | pub_msg.header.CopyFrom(header_msg) 188 | pub_msg.topic = "/force_torque" 189 | pub_msg.msg_type = "gz.msgs.Wrench" 190 | pub_msg.host = "127.0.0.1" 191 | pub_msg.port = 11311 192 | print(type(pub_msg)) 193 | print(pub_msg) 194 | 195 | print("----------------------------------------") 196 | sub_msg = Subscribe() 197 | sub_msg.header.CopyFrom(header_msg) 198 | sub_msg.topic = "/force_torque" 199 | sub_msg.msg_type = "gz.msgs.Wrench" 200 | sub_msg.host = "127.0.0.1" 201 | sub_msg.port = 11311 202 | sub_msg.latching = True 203 | print(type(sub_msg)) 204 | print(sub_msg) 205 | 206 | print("----------------------------------------") 207 | topic_msg = TopicInfo() 208 | topic_msg.header.CopyFrom(header_msg) 209 | topic_msg.msg_type = "gz.msgs.Wrench" 210 | topic_msg.publisher.append(pub_msg) 211 | topic_msg.subscriber.append(sub_msg) 212 | print(type(topic_msg)) 213 | print(topic_msg) 214 | 215 | 216 | if __name__ == "__main__": 217 | main() 218 | -------------------------------------------------------------------------------- /python/pub_all_msg_types.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import time 18 | 19 | from gz.msgs.cmd_vel2d_pb2 import CmdVel2D 20 | from gz.msgs.double_pb2 import Double 21 | from gz.msgs.double_v_pb2 import Double_V 22 | from gz.msgs.float_pb2 import Float 23 | from gz.msgs.float_v_pb2 import Float_V 24 | from gz.msgs.header_pb2 import Header 25 | from gz.msgs.pid_pb2 import PID 26 | from gz.msgs.pose_pb2 import Pose 27 | from gz.msgs.pose_v_pb2 import Pose_V 28 | from gz.msgs.publish_pb2 import Publish 29 | from gz.msgs.publishers_pb2 import Publishers 30 | from gz.msgs.quaternion_pb2 import Quaternion 31 | from gz.msgs.subscribe_pb2 import Subscribe 32 | from gz.msgs.topic_info_pb2 import TopicInfo 33 | from gz.msgs.time_pb2 import Time 34 | from gz.msgs.twist_pb2 import Twist 35 | from gz.msgs.vector3d_pb2 import Vector3d 36 | from gz.msgs.wrench_pb2 import Wrench 37 | 38 | from gz.transport import AdvertiseMessageOptions 39 | from gz.transport import Node 40 | 41 | def main(): 42 | # Create a transport node and advertise options 43 | node = Node() 44 | opts = AdvertiseMessageOptions() 45 | 46 | # list of publishers 47 | publishers = [] 48 | 49 | time_msg = Time() 50 | time_msg.sec = 15 51 | time_msg.nsec = 21 52 | publishers.append({ 53 | "msg": time_msg, 54 | "pub": node.advertise( 55 | "/time", Time.DESCRIPTOR.full_name, opts) 56 | }) 57 | 58 | header_msg = Header() 59 | header_msg.stamp.CopyFrom(time_msg) 60 | publishers.append({ 61 | "msg": header_msg, 62 | "pub": node.advertise( 63 | "/header", Header.DESCRIPTOR.full_name, opts) 64 | }) 65 | 66 | double_msg = Double() 67 | double_msg.header.CopyFrom(header_msg) 68 | double_msg.data = 21.3 69 | publishers.append({ 70 | "msg": double_msg, 71 | "pub": node.advertise( 72 | "/double", Double.DESCRIPTOR.full_name, opts) 73 | }) 74 | 75 | double_v_msg = Double_V() 76 | double_v_msg.data.append(21.4) 77 | double_v_msg.data.append(12.8) 78 | double_v_msg.data.append(15.6) 79 | double_v_msg.data.append(10.1) 80 | publishers.append({ 81 | "msg": double_v_msg, 82 | "pub": node.advertise( 83 | "/double_v", Double_V.DESCRIPTOR.full_name, opts) 84 | }) 85 | 86 | float_msg = Float() 87 | float_msg.header.CopyFrom(header_msg) 88 | float_msg.data = 21.3 89 | publishers.append({ 90 | "msg": float_msg, 91 | "pub": node.advertise( 92 | "/float", Float.DESCRIPTOR.full_name, opts) 93 | }) 94 | 95 | float_v_msg = Float_V() 96 | float_v_msg.header.CopyFrom(header_msg) 97 | float_v_msg.data.append(21.4) 98 | float_v_msg.data.append(12.8) 99 | float_v_msg.data.append(15.6) 100 | float_v_msg.data.append(10.1) 101 | publishers.append({ 102 | "msg": float_v_msg, 103 | "pub": node.advertise( 104 | "/float_v", Float_V.DESCRIPTOR.full_name, opts) 105 | }) 106 | 107 | vector3d_msg = Vector3d() 108 | vector3d_msg.header.CopyFrom(header_msg) 109 | vector3d_msg.x = 21.3 110 | vector3d_msg.y = 12.7 111 | vector3d_msg.z = 15.2 112 | publishers.append({ 113 | "msg": vector3d_msg, 114 | "pub": node.advertise( 115 | "/vector3d", Vector3d.DESCRIPTOR.full_name, opts) 116 | }) 117 | 118 | quat_msg = Quaternion() 119 | quat_msg.header.CopyFrom(header_msg) 120 | quat_msg.x = 0.0 121 | quat_msg.y = 0.0 122 | quat_msg.z = 0.0 123 | quat_msg.w = 1.0 124 | publishers.append({ 125 | "msg": quat_msg, 126 | "pub": node.advertise( 127 | "/quat", Quaternion.DESCRIPTOR.full_name, opts) 128 | }) 129 | 130 | pose_msg = Pose() 131 | pose_msg.header.CopyFrom(header_msg) 132 | pose_msg.name = "base_link" 133 | pose_msg.id = 0 134 | pose_msg.position.CopyFrom(vector3d_msg) 135 | pose_msg.orientation.CopyFrom(quat_msg) 136 | 137 | pose_v_msg = Pose_V() 138 | pose_v_msg.header.CopyFrom(header_msg) 139 | 140 | pose_msg.name = "base_link" 141 | pose_msg.id = 0 142 | pose_v_msg.pose.append(pose_msg) 143 | 144 | pose_msg.name = "left_motor_link" 145 | pose_msg.id = 1 146 | pose_v_msg.pose.append(pose_msg) 147 | 148 | pose_msg.name = "right_motor_link" 149 | pose_msg.id = 2 150 | pose_v_msg.pose.append(pose_msg) 151 | publishers.append({ 152 | "msg": pose_msg, 153 | "pub": node.advertise( 154 | "/pose", Pose.DESCRIPTOR.full_name, opts) 155 | }) 156 | 157 | cmd_vel_msg = CmdVel2D() 158 | cmd_vel_msg.header.CopyFrom(header_msg) 159 | cmd_vel_msg.velocity = 8.25 160 | cmd_vel_msg.theta = 1.517 161 | publishers.append({ 162 | "msg": cmd_vel_msg, 163 | "pub": node.advertise( 164 | "/cmd_vel", CmdVel2D.DESCRIPTOR.full_name, opts) 165 | }) 166 | 167 | pid_msg = PID() 168 | pid_msg.header.CopyFrom(header_msg) 169 | double_msg.data = 1.25 170 | pid_msg.target_optional.CopyFrom(double_msg) 171 | double_msg.data = 0.5 172 | pid_msg.p_gain_optional.CopyFrom(double_msg) 173 | double_msg.data = 0.05 174 | pid_msg.i_gain_optional.CopyFrom(double_msg) 175 | double_msg.data = 0.005 176 | pid_msg.d_gain_optional.CopyFrom(double_msg) 177 | double_msg.data = 1.0 178 | pid_msg.i_max_optional.CopyFrom(double_msg) 179 | double_msg.data = -1.0 180 | pid_msg.i_min_optional.CopyFrom(double_msg) 181 | double_msg.data = 10.0 182 | pid_msg.limit_optional.CopyFrom(double_msg) 183 | publishers.append({ 184 | "msg": pid_msg, 185 | "pub": node.advertise( 186 | "/pid", PID.DESCRIPTOR.full_name, opts) 187 | }) 188 | 189 | twist_msg = Twist() 190 | twist_msg.header.CopyFrom(header_msg) 191 | twist_msg.linear.CopyFrom(vector3d_msg) 192 | twist_msg.angular.CopyFrom(vector3d_msg) 193 | publishers.append({ 194 | "msg": twist_msg, 195 | "pub": node.advertise( 196 | "/twist", Twist.DESCRIPTOR.full_name, opts) 197 | }) 198 | 199 | wrench_msg = Wrench() 200 | wrench_msg.header.CopyFrom(header_msg) 201 | wrench_msg.force.CopyFrom(vector3d_msg) 202 | wrench_msg.torque.CopyFrom(vector3d_msg) 203 | wrench_msg.force_offset.CopyFrom(vector3d_msg) 204 | publishers.append({ 205 | "msg": wrench_msg, 206 | "pub": node.advertise( 207 | "/wrench", Wrench.DESCRIPTOR.full_name, opts) 208 | }) 209 | 210 | pub_msg = Publish() 211 | pub_msg.header.CopyFrom(header_msg) 212 | pub_msg.topic = "/force_torque" 213 | pub_msg.msg_type = "gz.msgs.Wrench" 214 | pub_msg.host = "127.0.0.1" 215 | pub_msg.port = 11311 216 | publishers.append({ 217 | "msg": pub_msg, 218 | "pub": node.advertise( 219 | "/publish", Publish.DESCRIPTOR.full_name, opts) 220 | }) 221 | 222 | sub_msg = Subscribe() 223 | sub_msg.header.CopyFrom(header_msg) 224 | sub_msg.topic = "/force_torque" 225 | sub_msg.msg_type = "gz.msgs.Wrench" 226 | sub_msg.host = "127.0.0.1" 227 | sub_msg.port = 11311 228 | sub_msg.latching = True 229 | publishers.append({ 230 | "msg": sub_msg, 231 | "pub": node.advertise( 232 | "/subscribe", Subscribe.DESCRIPTOR.full_name, opts) 233 | }) 234 | 235 | topic_msg = TopicInfo() 236 | topic_msg.header.CopyFrom(header_msg) 237 | topic_msg.msg_type = "gz.msgs.Wrench" 238 | topic_msg.publisher.append(pub_msg) 239 | topic_msg.subscriber.append(sub_msg) 240 | publishers.append({ 241 | "msg": topic_msg, 242 | "pub": node.advertise( 243 | "/topic_info", TopicInfo.DESCRIPTOR.full_name, opts) 244 | }) 245 | 246 | # publish each of the items at 1Hz 247 | try: 248 | while True: 249 | for item in publishers: 250 | msg = item["msg"] 251 | pub = item["pub"] 252 | pub.publish(msg) 253 | 254 | time.sleep(1.0) 255 | 256 | except KeyboardInterrupt: 257 | pass 258 | 259 | 260 | if __name__ == "__main__": 261 | main() 262 | -------------------------------------------------------------------------------- /python/publisher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import time 18 | 19 | from gz.msgs.stringmsg_pb2 import StringMsg 20 | 21 | from gz.transport import AdvertiseMessageOptions 22 | from gz.transport import Node 23 | 24 | def main(): 25 | # Create a transport node and advertise a topic 26 | node = Node() 27 | topic = "/foo" 28 | msg_type_name = StringMsg.DESCRIPTOR.full_name 29 | pub_options = AdvertiseMessageOptions() 30 | pub = node.advertise(topic, msg_type_name, pub_options) 31 | if pub.valid(): 32 | print("Advertising {} on topic [{}]".format(msg_type_name, topic)) 33 | else: 34 | print("Error advertising topic [{}]".format(topic)) 35 | 36 | # Prepare the message 37 | msg = StringMsg() 38 | msg.data = "hello" 39 | 40 | try: 41 | while True: 42 | if not pub.publish(msg): 43 | break 44 | 45 | print("Publishing hello on topic [{}]".format(topic)) 46 | time.sleep(1.0) 47 | 48 | except KeyboardInterrupt: 49 | pass 50 | 51 | if __name__ == "__main__": 52 | main() 53 | 54 | -------------------------------------------------------------------------------- /python/rover_publisher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from scipy.spatial.transform import Rotation as Rotation 18 | import math 19 | import time 20 | 21 | from gz.msgs.header_pb2 import Header 22 | from gz.msgs.pose_pb2 import Pose 23 | from gz.msgs.quaternion_pb2 import Quaternion 24 | from gz.msgs.time_pb2 import Time 25 | from gz.msgs.twist_pb2 import Twist 26 | from gz.msgs.vector3d_pb2 import Vector3d 27 | 28 | from gz.transport import AdvertiseMessageOptions 29 | from gz.transport import Node 30 | 31 | def main(): 32 | # Create a transport node and advertise a topic 33 | node = Node() 34 | pub_options = AdvertiseMessageOptions() 35 | 36 | pose_topic = "/pose" 37 | pose_msg_type_name = Pose.DESCRIPTOR.full_name 38 | 39 | pose_pub = node.advertise( 40 | pose_topic, pose_msg_type_name, pub_options) 41 | if pose_pub.valid(): 42 | print("Advertising {} on topic [{}]".format( 43 | pose_msg_type_name, pose_topic)) 44 | else: 45 | print("Error advertising topic [{}]".format(pose_topic)) 46 | 47 | twist_topic = "/twist" 48 | twist_msg_type_name = Twist.DESCRIPTOR.full_name 49 | twist_pub = node.advertise( 50 | twist_topic, twist_msg_type_name, pub_options) 51 | if twist_pub.valid(): 52 | print("Advertising {} on topic [{}]".format( 53 | twist_msg_type_name, twist_topic)) 54 | else: 55 | print("Error advertising topic [{}]".format(twist_topic)) 56 | 57 | # rover moving in a circle of radius with constant velocity 58 | radius = 5.0 59 | ang_vel = 0.1 60 | 61 | # publish messages at 2Hz 62 | start = time.time_ns() 63 | count = 0 64 | try: 65 | while True: 66 | # update time 67 | now = time.time_ns() 68 | time_ns = now - start 69 | time_s = int(time_ns/1.0E9) 70 | time_ns = int(time_ns % 1000000000) 71 | id = count 72 | count += 1 73 | 74 | # update position, orientation and velocity 75 | wz = ang_vel 76 | theta = wz * time_s 77 | c = math.cos(theta) 78 | s = math.sin(theta) 79 | x = radius * c 80 | y = radius * s 81 | vx = -1.0 * radius * wz * s 82 | vy = radius * wz * c 83 | rot = Rotation.from_euler("xyz", [0.0, 0.0, theta]) 84 | quat = rot.as_quat() 85 | 86 | # # Prepare the messages. 87 | time_msg = Time() 88 | time_msg.sec = time_s 89 | time_msg.nsec = time_ns 90 | 91 | header = Header() 92 | header.stamp.CopyFrom(time_msg) 93 | 94 | position = Vector3d() 95 | position.x = x 96 | position.y = y 97 | position.z = 0.0 98 | 99 | orientation = Quaternion() 100 | orientation.x = quat[0] 101 | orientation.y = quat[1] 102 | orientation.z = quat[2] 103 | orientation.w = quat[3] 104 | 105 | pose = Pose() 106 | pose.name = "base_link" 107 | pose.id = id 108 | pose.header.CopyFrom(header) 109 | pose.position.CopyFrom(position) 110 | pose.orientation.CopyFrom(orientation) 111 | 112 | lin_vel_msg = Vector3d() 113 | lin_vel_msg.x = vx 114 | lin_vel_msg.y = vy 115 | lin_vel_msg.z = 0.0 116 | 117 | ang_vel_msg = Vector3d() 118 | ang_vel_msg.x = 0.0 119 | ang_vel_msg.y = 0.0 120 | ang_vel_msg.z = wz 121 | 122 | twist = Twist() 123 | twist.header.CopyFrom(header) 124 | twist.linear.CopyFrom(lin_vel_msg) 125 | twist.angular.CopyFrom(ang_vel_msg) 126 | 127 | if not pose_pub.publish(pose): 128 | break 129 | 130 | if not twist_pub.publish(twist): 131 | break 132 | 133 | print("Publishing pose on topic [{}], twist on topic [{}]".format( 134 | pose_topic, twist_topic)) 135 | 136 | time.sleep(0.5) 137 | 138 | except KeyboardInterrupt: 139 | pass 140 | 141 | 142 | if __name__ == "__main__": 143 | main() 144 | 145 | -------------------------------------------------------------------------------- /python/rover_subscriber.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import time 18 | import typing 19 | 20 | from gz.msgs.pose_pb2 import Pose 21 | from gz.msgs.quaternion_pb2 import Quaternion 22 | from gz.msgs.twist_pb2 import Twist 23 | from gz.msgs.vector3d_pb2 import Vector3d 24 | 25 | from gz.transport import Node 26 | from gz.transport import SubscribeOptions 27 | 28 | 29 | def pose_cb(msg: Pose) -> None: 30 | print("{}".format(msg)) 31 | 32 | def twist_cb(msg: Twist) -> None: 33 | print("{}".format(msg)) 34 | 35 | def main(): 36 | # create a transport node 37 | node = Node() 38 | sub_options = SubscribeOptions() 39 | 40 | # subscribe to pose 41 | pose_topic = "/pose" 42 | pose_msg_type_name = Pose.DESCRIPTOR.full_name 43 | if node.subscribe(pose_topic, pose_cb, pose_msg_type_name, sub_options): 44 | print("Subscribing to type {} on topic [{}]".format( 45 | pose_msg_type_name, pose_topic)) 46 | else: 47 | print("Error subscribing to topic [{}]".format(pose_topic)) 48 | return 49 | 50 | # subscribe to twist 51 | twist_topic = "/twist" 52 | twist_msg_type_name = Twist.DESCRIPTOR.full_name 53 | if node.subscribe(twist_topic, twist_cb, twist_msg_type_name, sub_options): 54 | print("Subscribing to type {} on topic [{}]".format( 55 | twist_msg_type_name, twist_topic)) 56 | else: 57 | print("Error subscribing to topic [{}]".format(twist_topic)) 58 | return 59 | 60 | # wait for shutdown 61 | try: 62 | while True: 63 | time.sleep(0.001) 64 | except KeyboardInterrupt: 65 | pass 66 | 67 | if __name__ == "__main__": 68 | main() 69 | 70 | -------------------------------------------------------------------------------- /python/subscriber.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import time 18 | import typing 19 | 20 | from gz.msgs.stringmsg_pb2 import StringMsg 21 | 22 | from gz.transport import SubscribeOptions 23 | from gz.transport import Node 24 | 25 | def cb(msg: StringMsg) -> None: 26 | print("Msg: [{}] from Python".format(msg.data)) 27 | 28 | def main(): 29 | # create a transport node 30 | node = Node() 31 | topic = "/foo" 32 | msg_type_name = StringMsg.DESCRIPTOR.full_name 33 | sub_options = SubscribeOptions() 34 | 35 | # subscribe to a topic by registering a callback 36 | if node.subscribe(topic, cb, msg_type_name, sub_options): 37 | print("Subscribing to type {} on topic [{}]".format( 38 | msg_type_name, topic)) 39 | else: 40 | print("Error subscribing to topic [{}]".format(topic)) 41 | return 42 | 43 | # wait for shutdown 44 | try: 45 | while True: 46 | time.sleep(0.001) 47 | except KeyboardInterrupt: 48 | pass 49 | 50 | if __name__ == "__main__": 51 | main() 52 | 53 | -------------------------------------------------------------------------------- /python/transport_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2022 Rhys Mainwaring 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License") 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http:#www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from gz.msgs.header_pb2 import Header 18 | from gz.msgs.time_pb2 import Time 19 | from gz.msgs.quaternion_pb2 import Quaternion 20 | from gz.msgs.pose_pb2 import Pose 21 | from gz.msgs.twist_pb2 import Twist 22 | from gz.msgs.vector3d_pb2 import Vector3d 23 | from gz.msgs.wrench_pb2 import Wrench 24 | 25 | from gz.transport import AdvertiseMessageOptions 26 | from gz.transport import SubscribeOptions 27 | from gz.transport import Node 28 | 29 | from google.protobuf.internal import api_implementation 30 | 31 | def main(): 32 | print("Gazebo Transport Example\n") 33 | 34 | #---------------------------------------------- 35 | # google.protobuf implementation 36 | print("proto api type: {}".format(api_implementation.Type())) 37 | 38 | #---------------------------------------------- 39 | # create transport node 40 | node = Node() 41 | 42 | #---------------------------------------------- 43 | # get a publisher and check valid 44 | pub = Node.Publisher() 45 | print("publisher valid: {}".format(pub.valid())) 46 | 47 | # create and publish different message types 48 | print("----------------------------------------") 49 | time_msg = Time() 50 | time_msg.sec = 15 51 | time_msg.nsec = 21 52 | pub.publish(time_msg) 53 | 54 | print("----------------------------------------") 55 | header_msg = Header() 56 | header_msg.stamp.CopyFrom(time_msg) 57 | pub.publish(header_msg) 58 | 59 | print("----------------------------------------") 60 | vector3d_msg = Vector3d() 61 | vector3d_msg.header.CopyFrom(header_msg) 62 | vector3d_msg.x = 21.3 63 | vector3d_msg.y = 12.7 64 | vector3d_msg.z = 15.2 65 | pub.publish(vector3d_msg) 66 | 67 | print("----------------------------------------") 68 | quat_msg = Quaternion() 69 | quat_msg.header.CopyFrom(header_msg) 70 | quat_msg.x = 0.0 71 | quat_msg.y = 0.0 72 | quat_msg.z = 0.0 73 | quat_msg.w = 1.0 74 | pub.publish(quat_msg) 75 | 76 | print("----------------------------------------") 77 | pose_msg = Pose() 78 | pose_msg.header.CopyFrom(header_msg) 79 | pose_msg.name = "base_link" 80 | pose_msg.id = 0 81 | pose_msg.position.CopyFrom(vector3d_msg) 82 | pose_msg.orientation.CopyFrom(quat_msg) 83 | pub.publish(pose_msg) 84 | 85 | print("----------------------------------------") 86 | twist_msg = Twist() 87 | twist_msg.header.CopyFrom(header_msg) 88 | twist_msg.linear.CopyFrom(vector3d_msg) 89 | twist_msg.angular.CopyFrom(vector3d_msg) 90 | pub.publish(twist_msg) 91 | 92 | print("----------------------------------------") 93 | wrench_msg = Wrench() 94 | wrench_msg.header.CopyFrom(header_msg) 95 | wrench_msg.force.CopyFrom(vector3d_msg) 96 | wrench_msg.torque.CopyFrom(vector3d_msg) 97 | wrench_msg.force_offset.CopyFrom(vector3d_msg) 98 | pub.publish(wrench_msg) 99 | 100 | print("----------------------------------------") 101 | # AdvertiseMessageOptions defaults 102 | pub_options = AdvertiseMessageOptions() 103 | print("AdvertiseMessageOptions") 104 | print("throttled: {}".format(pub_options.throttled)) 105 | print("msgs_per_sec: {}".format(pub_options.msgs_per_sec)) 106 | # apply throttling 107 | pub_options.msgs_per_sec = 10 108 | print("throttled: {}".format(pub_options.throttled)) 109 | print("msgs_per_sec: {}".format(pub_options.msgs_per_sec)) 110 | 111 | topic = "/motor_1_joint/force_torque" 112 | msg_type_name = Wrench.DESCRIPTOR.full_name 113 | pub = node.advertise(topic, msg_type_name, pub_options) 114 | pub.publish(wrench_msg) 115 | 116 | print("----------------------------------------") 117 | def on_force_torque(msg): 118 | print(msg) 119 | 120 | sub_options = SubscribeOptions() 121 | print("SubscribeOptions") 122 | print("throttled: {}".format(sub_options.throttled)) 123 | print("msgs_per_sec: {}".format(sub_options.msgs_per_sec)) 124 | # sub = node.subscribe(topic, on_force_torque, sub_options) 125 | 126 | if __name__ == "__main__": 127 | main() 128 | 129 | -------------------------------------------------------------------------------- /src/deps_check.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // @yaml 4 | #include 5 | #include 6 | 7 | // @zip 8 | #include 9 | #include 10 | 11 | // @zmq 12 | #include 13 | #include 14 | 15 | // @gl 16 | // #include 17 | 18 | // @glib 19 | #include 20 | 21 | // @gts 22 | #include 23 | 24 | // @freeimage 25 | #include 26 | 27 | int main(int argc, const char *argv[]) 28 | { 29 | std::cout << "@gz_python//:deps_check" << std::endl; 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/gz_topic_echo.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | void cb(const google::protobuf::Message &_msg) 27 | { 28 | std::cout << _msg.DebugString() << std::endl; 29 | } 30 | 31 | void print_usage() 32 | { 33 | std::cout << "Must supply topic using '-t'" << std::endl; 34 | } 35 | 36 | int main(int argc, char **argv) 37 | { 38 | // parse command line 39 | std::string topic; 40 | 41 | if (argc < 2) 42 | { 43 | print_usage(); 44 | return -1; 45 | } 46 | int opt = 0; 47 | while ((opt = getopt(argc, argv, "t:")) != -1) 48 | { 49 | switch (opt) 50 | { 51 | case 't': 52 | topic = optarg; 53 | break; 54 | default: 55 | print_usage(); 56 | return -1; 57 | } 58 | } 59 | 60 | // create node 61 | gz::transport::Node node; 62 | 63 | // subscribe to a topic by registering a callback. 64 | if (!node.Subscribe(topic, cb)) 65 | { 66 | std::cerr << "Error subscribing to topic [" << topic << "]" << std::endl; 67 | return -1; 68 | } 69 | 70 | gz::transport::waitForShutdown(); 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include "gz/msgs/extras.hh" 24 | 25 | int main(int argc, const char* argv[]) 26 | { 27 | // Verify the version of the library we linked against is 28 | // compatible with the version used to generate the headers. 29 | GOOGLE_PROTOBUF_VERIFY_VERSION; 30 | 31 | std::cout << "Gazebo MsgsExtras Example" << std::endl; 32 | 33 | // example: gz/msgs/time.proto 34 | { 35 | gz::msgs::Time msg; 36 | msg.set_sec(11); 37 | msg.set_nsec(25); 38 | std::cout << msg.DebugString(); 39 | } 40 | 41 | // example: msgs_tools 42 | { 43 | auto msg = gz::msgs::extras::MakeTime(); 44 | gz::msgs::extras::TakeTime(msg); 45 | } 46 | 47 | // Shutdown 48 | google::protobuf::ShutdownProtobufLibrary(); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /src/msg_example.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Open Source Robotics Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | 20 | #include 21 | 22 | using namespace gz; 23 | 24 | int main(int argc, const char *argv[]) 25 | { 26 | // https://gazebosim.org/api/msgs/8.1/cppgetstarted.html 27 | { 28 | gz::msgs::Vector3d point1; 29 | point1.set_x(1); 30 | point1.set_y(3); 31 | point1.set_z(5); 32 | gz::msgs::Vector3d point2; 33 | point2.set_x(2); 34 | point2.set_y(4); 35 | point2.set_z(6); 36 | std::cout << "Point1:\n" << point1.DebugString() << std::endl; 37 | std::cout << "Point2:\n" << point2.DebugString() << std::endl; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/publisher.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Open Source Robotics Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | static std::atomic g_terminatePub(false); 28 | 29 | void signal_handler(int _signal) 30 | { 31 | if (_signal == SIGINT || _signal == SIGTERM) 32 | g_terminatePub = true; 33 | } 34 | 35 | int main(int argc, char **argv) 36 | { 37 | // Install a signal handler for SIGINT and SIGTERM. 38 | std::signal(SIGINT, signal_handler); 39 | std::signal(SIGTERM, signal_handler); 40 | 41 | // Create a transport node and advertise a topic. 42 | gz::transport::Node node; 43 | std::string topic = "/foo"; 44 | auto pub = node.Advertise(topic); 45 | if (!pub) 46 | { 47 | std::cerr << "Error advertising topic [" << topic << "]" << std::endl; 48 | return -1; 49 | } 50 | 51 | // Prepare the message. 52 | gz::msgs::StringMsg msg; 53 | msg.set_data("hello"); 54 | 55 | // Publish messages at 1Hz. 56 | while (!g_terminatePub) 57 | { 58 | if (!pub.Publish(msg)) 59 | { 60 | break; 61 | } 62 | std::cout << "Publishing hello on topic [" << topic << "]" << std::endl; 63 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 64 | } 65 | return 0; 66 | } -------------------------------------------------------------------------------- /src/pybind11/gz/msgs/_gz_msgs_extras_pybind11.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include "gz/msgs/extras.hh" 19 | 20 | #include 21 | 22 | #include "pybind11_protobuf/native_proto_caster.h" 23 | 24 | namespace py = pybind11; 25 | 26 | PYBIND11_MODULE(extras, m) 27 | { 28 | using namespace gz; 29 | using namespace msgs; 30 | using namespace extras; 31 | 32 | pybind11_protobuf::ImportNativeProtoCasters(); 33 | 34 | m.doc() = "Gazebo Msgs Extras Python Library."; 35 | 36 | m.def("make_time", &MakeTime); 37 | m.def("take_time", &TakeTime, pybind11::arg("msg")); 38 | m.def("take_topic_info", &TakeTopicInfo, pybind11::arg("msg")); 39 | m.def("take_wrench", &TakeWrench, pybind11::arg("msg")); 40 | } 41 | -------------------------------------------------------------------------------- /src/pybind11/gz/msgs/extras.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include "gz/msgs/extras.hh" 19 | 20 | namespace gz 21 | { 22 | namespace msgs 23 | { 24 | namespace extras 25 | { 26 | Time MakeTime() 27 | { 28 | Time msg; 29 | msg.set_sec(10); 30 | msg.set_nsec(20); 31 | return msg; 32 | } 33 | 34 | void TakeTime(const Time& msg) 35 | { 36 | std::cout << msg.DebugString(); 37 | } 38 | 39 | void TakeTopicInfo(const TopicInfo& msg) 40 | { 41 | std::cout << msg.DebugString(); 42 | } 43 | 44 | void TakeWrench(const Wrench& msg) 45 | { 46 | std::cout << msg.DebugString(); 47 | } 48 | } // namespace extras 49 | } // namespace msgs 50 | } // namespace gz 51 | -------------------------------------------------------------------------------- /src/pybind11/gz/transport/_gz_transport_pybind11.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "pybind11_protobuf/native_proto_caster.h" 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | namespace py = pybind11; 33 | 34 | namespace gz 35 | { 36 | namespace transport 37 | { 38 | namespace python 39 | { 40 | /// \brief Define pybind11 bindings for gz::transport objects 41 | void define_transport_node(py::module_ module) 42 | { 43 | using namespace gz; 44 | using namespace transport; 45 | 46 | pybind11_protobuf::ImportNativeProtoCasters(); 47 | 48 | py::enum_(module, "Scope_t", 49 | "Defines the different options for the scope of a topic/service") 50 | .value("PROCESS", Scope_t::PROCESS, 51 | "Topic/service only available to subscribers in the" 52 | " same process as the publisher") 53 | .value("HOST", Scope_t::HOST, 54 | "Topic/service only available to subscribers in the" 55 | " same machine as the publisher") 56 | .value("ALL", Scope_t::ALL, 57 | "Topic/service available to any subscriber (default scope)") 58 | ; 59 | 60 | py::class_( 61 | module, "AdvertiseOptions", 62 | "A class for customizing the publication options for" 63 | " a topic or service advertised") 64 | .def(py::init<>()) 65 | .def_property("scope", 66 | &AdvertiseOptions::Scope, 67 | &AdvertiseOptions::SetScope, 68 | "The scope used in this topic/service") 69 | ; 70 | 71 | py::class_( 72 | module, "AdvertiseMessageOptions", 73 | "A class for customizing the publication options for a topic") 74 | .def(py::init<>()) 75 | .def_property_readonly("throttled", 76 | &AdvertiseMessageOptions::Throttled, 77 | "Whether the publication has been throttled") 78 | .def_property("msgs_per_sec", 79 | &AdvertiseMessageOptions::MsgsPerSec, 80 | &AdvertiseMessageOptions::SetMsgsPerSec, 81 | "The maximum number of messages per second to be published") 82 | ; 83 | 84 | py::class_( 85 | module, "AdvertiseServiceOptions", 86 | "A class for customizing the publication options for a service") 87 | .def(py::init<>()) 88 | ; 89 | 90 | py::class_( 91 | module, "SubscribeOptions", 92 | "A class to provide different options for a subscription") 93 | .def(py::init<>()) 94 | .def_property_readonly("throttled", 95 | &SubscribeOptions::Throttled, 96 | "Whether the subscription has been throttled") 97 | .def_property("msgs_per_sec", 98 | &SubscribeOptions::MsgsPerSec, 99 | &SubscribeOptions::SetMsgsPerSec, 100 | "Set the maximum number of messages per second received per topic") 101 | ; 102 | 103 | py::class_( 104 | module, "Publisher", 105 | "This class stores all the information about a publisher." 106 | " It stores the topic name that publishes, addresses," 107 | " UUIDs, scope, etc.") 108 | .def(py::init<>()) 109 | .def_property("topic", 110 | &Publisher::Topic, 111 | &Publisher::SetTopic, 112 | "The topic published by this publisher") 113 | .def_property("addr", 114 | &Publisher::Addr, 115 | &Publisher::SetAddr, 116 | "Get the ZeroMQ address of the publisher") 117 | .def_property("puuid", 118 | &Publisher::PUuid, 119 | &Publisher::SetPUuid, 120 | "The process UUID of the publisher") 121 | .def_property("nuuid", 122 | &Publisher::NUuid, 123 | &Publisher::SetNUuid, 124 | "Get the node UUID of the publisher") 125 | // virtual 126 | .def_property("options", 127 | &Publisher::Options, 128 | &Publisher::SetOptions, 129 | "Get the advertised options") 130 | // virtual 131 | .def("discovery", []( 132 | Publisher &_pub) 133 | { 134 | gz::msgs::Discovery msg; 135 | _pub.FillDiscovery(msg); 136 | return msg; 137 | }, 138 | "Populate a discovery message") 139 | ; 140 | 141 | py::class_( 142 | module, "MessagePublisher", 143 | "This class stores all the information about a message publisher") 144 | .def(py::init<>()) 145 | .def_property("ctrl", 146 | &MessagePublisher::Ctrl, 147 | &MessagePublisher::SetCtrl, 148 | "The ZeroMQ control address. This address is used by the" 149 | " subscribers to notify the publisher about the new subscription") 150 | .def_property("msg_type_name", 151 | &MessagePublisher::MsgTypeName, 152 | &MessagePublisher::SetMsgTypeName, 153 | "Get the message type advertised by this publisher") 154 | // virtual 155 | .def_property("options", 156 | &MessagePublisher::Options, 157 | &MessagePublisher::SetOptions, 158 | "The advertised options") 159 | // virtual 160 | .def("discovery", []( 161 | MessagePublisher &_pub) 162 | { 163 | gz::msgs::Discovery msg; 164 | _pub.FillDiscovery(msg); 165 | return msg; 166 | }, 167 | "Populate a discovery message") 168 | ; 169 | 170 | py::class_( 171 | module, "ServicePublisher", 172 | "This class stores all the information about a service publisher") 173 | .def(py::init<>()) 174 | .def_property("socket_id", 175 | &ServicePublisher::SocketId, 176 | &ServicePublisher::SetSocketId, 177 | "The ZeroMQ socket ID for this publisher") 178 | .def_property("req_type_name", 179 | &ServicePublisher::ReqTypeName, 180 | &ServicePublisher::SetReqTypeName, 181 | "The name of the request's protobuf message advertised") 182 | .def_property("rep_type_name", 183 | &ServicePublisher::RepTypeName, 184 | &ServicePublisher::SetRepTypeName, 185 | "The name of the response's protobuf message advertised") 186 | // virtual 187 | .def_property("options", 188 | &ServicePublisher::Options, 189 | &ServicePublisher::SetOptions, 190 | "The advertised options") 191 | // virtual 192 | .def("discovery", []( 193 | ServicePublisher &_pub) 194 | { 195 | gz::msgs::Discovery msg; 196 | _pub.FillDiscovery(msg); 197 | return msg; 198 | }, 199 | "Populate a discovery message") 200 | ; 201 | 202 | auto node = py::class_(module, "Node", 203 | "A class that allows a client to communicate with other peers." 204 | " There are two main communication modes: pub/sub messages" 205 | " and service calls") 206 | .def(py::init<>()) 207 | .def("advertise", static_cast< 208 | Node::Publisher (Node::*) ( 209 | const std::string &, 210 | const std::string &, 211 | const AdvertiseMessageOptions & 212 | )>(&Node::Advertise), 213 | pybind11::arg("topic"), 214 | pybind11::arg("msg_type_name"), 215 | pybind11::arg("options"), 216 | "Advertise a new topic. If a topic is currently advertised," 217 | " you cannot advertise it a second time (regardless of its type)") 218 | .def("advertised_topics", &Node::AdvertisedTopics, 219 | "Get the list of topics advertised by this node") 220 | .def("subscribe", []( 221 | Node &_node, 222 | const std::string &_topic, 223 | std::function &_callback, 224 | const std::string &_msg_type_name, 225 | const SubscribeOptions &_opts) 226 | { 227 | return _node.Subscribe(_topic, _callback, _opts); 228 | }, 229 | pybind11::arg("topic"), 230 | pybind11::arg("callback"), 231 | pybind11::arg("msg_type_name"), 232 | pybind11::arg("options"), 233 | "Subscribe to a topic registering a callback") 234 | .def("subscribed_topics", &Node::SubscribedTopics, 235 | "Get the list of topics subscribed by this node") 236 | .def("unsubscribe", &Node::Unsubscribe, 237 | pybind11::arg("topic"), 238 | "Unsubscribe from a topic") 239 | .def("topic_list", []( 240 | Node &_node) 241 | { 242 | std::vector topics; 243 | _node.TopicList(topics); 244 | return topics; 245 | }, 246 | "Get the list of topics currently advertised in the network") 247 | .def("topic_info", []( 248 | Node &_node, 249 | const std::string &_topic) 250 | { 251 | std::vector publishers; 252 | _node.TopicInfo(_topic, publishers); 253 | return publishers; 254 | }, 255 | pybind11::arg("topic"), 256 | "Get the information about a topic") 257 | .def("advertised_services", &Node::AdvertisedServices, 258 | "Get the list of services advertised by this node") 259 | // send a service request using the blocking interface 260 | .def("request", []( 261 | Node &_node, 262 | const std::string &_service, 263 | const google::protobuf::Message &_request, 264 | const unsigned int &_timeout, 265 | const std::string &_repType) 266 | { 267 | // see ign-transport/src/cmd/ign.cc L227-240 268 | auto rep = gz::msgs::Factory::New(_repType); 269 | if (!rep) 270 | { 271 | std::cerr << "Unable to create response of type[" 272 | << _repType << "].\n"; 273 | return std::make_tuple(false, false); 274 | } 275 | 276 | bool result{false}; 277 | bool executed = _node.Request( 278 | _service, _request, _timeout, *rep, result); 279 | return std::make_tuple(executed, result); 280 | }, 281 | pybind11::arg("service"), 282 | pybind11::arg("request"), 283 | pybind11::arg("timeout"), 284 | pybind11::arg("rep_type_name"), 285 | "Request a new service without input parameter using" 286 | " a blocking call") 287 | .def("service_list", []( 288 | Node &_node) 289 | { 290 | std::vector services; 291 | _node.ServiceList(services); 292 | return services; 293 | }, 294 | "Get the list of topics currently advertised in the network") 295 | .def("service_info", []( 296 | Node &_node, 297 | const std::string &_service) 298 | { 299 | std::vector publishers; 300 | _node.ServiceInfo(_service, publishers); 301 | return publishers; 302 | }, 303 | pybind11::arg("service"), 304 | "Get the information about a service") 305 | ; 306 | 307 | // register Node::Publisher as a subclass of Node 308 | py::class_(node, "Publisher", 309 | "A class that is used to store information about an" 310 | " advertised publisher.") 311 | .def(py::init<>()) 312 | .def("valid", &gz::transport::Node::Publisher::Valid, 313 | "Return true if valid information, such as a non-empty" 314 | " topic name, is present.") 315 | .def("publish", &gz::transport::Node::Publisher::Publish, 316 | pybind11::arg("msg"), 317 | "Publish a message") 318 | .def("throttled_update_ready", 319 | &gz::transport::Node::Publisher::ThrottledUpdateReady, 320 | "") 321 | .def("has_connections", 322 | &gz::transport::Node::Publisher::HasConnections, 323 | "Return true if this publisher has subscribers") 324 | ; 325 | } 326 | } 327 | } // python 328 | } // transport 329 | 330 | /// \brief Define the gz_transport module 331 | PYBIND11_MODULE(transport, m) { 332 | pybind11_protobuf::ImportNativeProtoCasters(); 333 | 334 | m.doc() = "Gazebo Transport Python Library."; 335 | 336 | gz::transport::python::define_transport_node(m); 337 | 338 | m.def("version", []() -> std::string { 339 | return GZ_TRANSPORT_VERSION_FULL; 340 | }); 341 | 342 | } // gz 343 | -------------------------------------------------------------------------------- /src/rover_publisher.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static std::atomic g_terminatePub(false); 29 | 30 | void signal_handler(int _signal) 31 | { 32 | if (_signal == SIGINT || _signal == SIGTERM) 33 | g_terminatePub = true; 34 | } 35 | 36 | int main(int argc, char **argv) 37 | { 38 | // Install a signal handler for SIGINT and SIGTERM. 39 | std::signal(SIGINT, signal_handler); 40 | std::signal(SIGTERM, signal_handler); 41 | 42 | // Create a transport node and advertise a topic. 43 | gz::transport::Node node; 44 | 45 | std::string pose_topic = "/pose"; 46 | auto pose_pub = node.Advertise(pose_topic); 47 | if (!pose_pub) 48 | { 49 | std::cerr << "Error advertising topic [" << pose_topic << "]\n"; 50 | return -1; 51 | } 52 | 53 | std::string twist_topic = "/twist"; 54 | auto twist_pub = node.Advertise(twist_topic); 55 | if (!twist_pub) 56 | { 57 | std::cerr << "Error advertising topic [" << twist_topic << "]\n"; 58 | return -1; 59 | } 60 | 61 | // rover moving in a circle of radius with constant velocity 62 | double radius = 5.0; 63 | double ang_vel = 0.1; 64 | 65 | const auto start = std::chrono::steady_clock::now(); 66 | 67 | // Publish messages at 2Hz. 68 | uint32_t count = 0; 69 | while (!g_terminatePub) 70 | { 71 | // update time 72 | const auto now = std::chrono::steady_clock::now(); 73 | auto time_s = std::chrono::duration_cast(now - start).count(); 74 | auto time_ns = std::chrono::duration_cast(now - start).count(); 75 | time_ns = time_ns % 1000000000; 76 | uint32_t id = count++; 77 | 78 | // update position, orientation and velocity 79 | double wz = ang_vel; 80 | double theta = wz * time_s; 81 | double c = std::cos(theta); 82 | double s = std::sin(theta); 83 | double x = radius * c; 84 | double y = radius * s; 85 | double vx = -1.0 * radius * wz * s; 86 | double vy = radius * wz * c; 87 | gz::math::Quaternion quat(0.0, 0.0, theta); 88 | 89 | // Prepare the message. 90 | gz::msgs::Time time; 91 | time.set_sec(time_s); 92 | time.set_nsec(time_ns); 93 | 94 | gz::msgs::Header header; 95 | *header.mutable_stamp() = time; 96 | 97 | gz::msgs::Vector3d position; 98 | position.set_x(x); 99 | position.set_y(y); 100 | position.set_z(0.0); 101 | 102 | gz::msgs::Quaternion orientation; 103 | orientation.set_x(quat.X()); 104 | orientation.set_y(quat.Y()); 105 | orientation.set_z(quat.Z()); 106 | orientation.set_w(quat.W()); 107 | 108 | gz::msgs::Pose pose; 109 | pose.set_name("base_link"); 110 | pose.set_id(id); 111 | *pose.mutable_header() = header; 112 | *pose.mutable_position() = position; 113 | *pose.mutable_orientation() = orientation; 114 | 115 | gz::msgs::Vector3d lin_vel_msg; 116 | lin_vel_msg.set_x(vx); 117 | lin_vel_msg.set_y(vy); 118 | lin_vel_msg.set_z(0.0); 119 | 120 | gz::msgs::Vector3d ang_vel_msg; 121 | ang_vel_msg.set_x(0.0); 122 | ang_vel_msg.set_y(0.0); 123 | ang_vel_msg.set_z(wz); 124 | 125 | gz::msgs::Twist twist; 126 | *twist.mutable_header() = header; 127 | *twist.mutable_linear() = lin_vel_msg; 128 | *twist.mutable_angular() = ang_vel_msg; 129 | 130 | if (!pose_pub.Publish(pose)) 131 | { 132 | break; 133 | } 134 | if (!twist_pub.Publish(twist)) 135 | { 136 | break; 137 | } 138 | std::cout << "Publishing pose on topic [" << pose_topic 139 | << "], twist on topic [" << twist_topic << "]\n"; 140 | 141 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 142 | } 143 | return 0; 144 | } -------------------------------------------------------------------------------- /src/rover_subscriber.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Rhys Mainwaring 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | void pose_cb(const gz::msgs::Pose &_msg) 24 | { 25 | std::cout << _msg.DebugString(); 26 | } 27 | 28 | void twist_cb(const gz::msgs::Twist &_msg) 29 | { 30 | std::cout << _msg.DebugString(); 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | gz::transport::Node node; 36 | 37 | // subscribe to pose 38 | std::string pose_topic = "/pose"; 39 | if (!node.Subscribe(pose_topic, pose_cb)) 40 | { 41 | std::cerr << "Error subscribing to topic [" 42 | << pose_topic << "]" << std::endl; 43 | return -1; 44 | } 45 | 46 | // subscribe to twist 47 | std::string twist_topic = "/twist"; 48 | if (!node.Subscribe(twist_topic, twist_cb)) 49 | { 50 | std::cerr << "Error subscribing to topic [" 51 | << twist_topic << "]" << std::endl; 52 | return -1; 53 | } 54 | 55 | // spin. 56 | gz::transport::waitForShutdown(); 57 | return 0; 58 | } -------------------------------------------------------------------------------- /src/subscriber.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Open Source Robotics Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | void cb(const gz::msgs::StringMsg &_msg) 24 | { 25 | std::cout << "Msg: " << _msg.data() << std::endl; 26 | } 27 | 28 | int main(int argc, char **argv) 29 | { 30 | gz::transport::Node node; 31 | std::string topic = "/foo"; 32 | 33 | // Subscribe to a topic by registering a callback. 34 | if (!node.Subscribe(topic, cb)) 35 | { 36 | std::cerr << "Error subscribing to topic [" << topic << "]" << std::endl; 37 | return -1; 38 | } 39 | 40 | // Zzzzzz. 41 | gz::transport::waitForShutdown(); 42 | return 0; 43 | } --------------------------------------------------------------------------------