├── .gitignore ├── .vscode ├── PythonImportHelper-v2-Completion.json ├── c_cpp_properties.json └── settings.json ├── README.md ├── requirements.txt └── src ├── test_msg ├── CMakeLists.txt ├── msg │ └── Test.msg └── package.xml └── test_plot ├── CMakeLists.txt ├── launch └── test.launch.py ├── package.xml └── scripts ├── test_plot.py ├── test_plot1.py └── test_pub.py /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | install/ 3 | log/ 4 | venv/ -------------------------------------------------------------------------------- /.vscode/PythonImportHelper-v2-Completion.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "os", 4 | "kind": 6, 5 | "isExtraImport": true, 6 | "importPath": "os", 7 | "description": "os", 8 | "detail": "os", 9 | "documentation": {} 10 | }, 11 | { 12 | "label": "getenv", 13 | "importPath": "os", 14 | "description": "os", 15 | "isExtraImport": true, 16 | "detail": "os", 17 | "documentation": {} 18 | }, 19 | { 20 | "label": "getenv", 21 | "importPath": "os", 22 | "description": "os", 23 | "isExtraImport": true, 24 | "detail": "os", 25 | "documentation": {} 26 | }, 27 | { 28 | "label": "builtins", 29 | "kind": 6, 30 | "isExtraImport": true, 31 | "importPath": "builtins", 32 | "description": "builtins", 33 | "detail": "builtins", 34 | "documentation": {} 35 | }, 36 | { 37 | "label": "rosidl_parser.definition", 38 | "kind": 6, 39 | "isExtraImport": true, 40 | "importPath": "rosidl_parser.definition", 41 | "description": "rosidl_parser.definition", 42 | "detail": "rosidl_parser.definition", 43 | "documentation": {} 44 | }, 45 | { 46 | "label": "find_packages", 47 | "importPath": "setuptools", 48 | "description": "setuptools", 49 | "isExtraImport": true, 50 | "detail": "setuptools", 51 | "documentation": {} 52 | }, 53 | { 54 | "label": "setup", 55 | "importPath": "setuptools", 56 | "description": "setuptools", 57 | "isExtraImport": true, 58 | "detail": "setuptools", 59 | "documentation": {} 60 | }, 61 | { 62 | "label": "threading", 63 | "kind": 6, 64 | "isExtraImport": true, 65 | "importPath": "threading", 66 | "description": "threading", 67 | "detail": "threading", 68 | "documentation": {} 69 | }, 70 | { 71 | "label": "typing", 72 | "kind": 6, 73 | "isExtraImport": true, 74 | "importPath": "typing", 75 | "description": "typing", 76 | "detail": "typing", 77 | "documentation": {} 78 | }, 79 | { 80 | "label": "matplotlib.pyplot", 81 | "kind": 6, 82 | "isExtraImport": true, 83 | "importPath": "matplotlib.pyplot", 84 | "description": "matplotlib.pyplot", 85 | "detail": "matplotlib.pyplot", 86 | "documentation": {} 87 | }, 88 | { 89 | "label": "matplotlib.animation", 90 | "kind": 6, 91 | "isExtraImport": true, 92 | "importPath": "matplotlib.animation", 93 | "description": "matplotlib.animation", 94 | "detail": "matplotlib.animation", 95 | "documentation": {} 96 | }, 97 | { 98 | "label": "numpy", 99 | "kind": 6, 100 | "isExtraImport": true, 101 | "importPath": "numpy", 102 | "description": "numpy", 103 | "detail": "numpy", 104 | "documentation": {} 105 | }, 106 | { 107 | "label": "numpy.typing", 108 | "kind": 6, 109 | "isExtraImport": true, 110 | "importPath": "numpy.typing", 111 | "description": "numpy.typing", 112 | "detail": "numpy.typing", 113 | "documentation": {} 114 | }, 115 | { 116 | "label": "rclpy", 117 | "kind": 6, 118 | "isExtraImport": true, 119 | "importPath": "rclpy", 120 | "description": "rclpy", 121 | "detail": "rclpy", 122 | "documentation": {} 123 | }, 124 | { 125 | "label": "Test", 126 | "importPath": "test_msg.msg", 127 | "description": "test_msg.msg", 128 | "isExtraImport": true, 129 | "detail": "test_msg.msg", 130 | "documentation": {} 131 | }, 132 | { 133 | "label": "Test", 134 | "importPath": "test_msg.msg", 135 | "description": "test_msg.msg", 136 | "isExtraImport": true, 137 | "detail": "test_msg.msg", 138 | "documentation": {} 139 | }, 140 | { 141 | "label": "Test", 142 | "importPath": "test_msg.msg", 143 | "description": "test_msg.msg", 144 | "isExtraImport": true, 145 | "detail": "test_msg.msg", 146 | "documentation": {} 147 | }, 148 | { 149 | "label": "Test", 150 | "importPath": "test_msg.msg", 151 | "description": "test_msg.msg", 152 | "isExtraImport": true, 153 | "detail": "test_msg.msg", 154 | "documentation": {} 155 | }, 156 | { 157 | "label": "Test", 158 | "importPath": "test_msg.msg", 159 | "description": "test_msg.msg", 160 | "isExtraImport": true, 161 | "detail": "test_msg.msg", 162 | "documentation": {} 163 | }, 164 | { 165 | "label": "Test", 166 | "importPath": "test_msg.msg", 167 | "description": "test_msg.msg", 168 | "isExtraImport": true, 169 | "detail": "test_msg.msg", 170 | "documentation": {} 171 | }, 172 | { 173 | "label": "Subscription", 174 | "importPath": "rclpy.subscription", 175 | "description": "rclpy.subscription", 176 | "isExtraImport": true, 177 | "detail": "rclpy.subscription", 178 | "documentation": {} 179 | }, 180 | { 181 | "label": "Subscription", 182 | "importPath": "rclpy.subscription", 183 | "description": "rclpy.subscription", 184 | "isExtraImport": true, 185 | "detail": "rclpy.subscription", 186 | "documentation": {} 187 | }, 188 | { 189 | "label": "Subscription", 190 | "importPath": "rclpy.subscription", 191 | "description": "rclpy.subscription", 192 | "isExtraImport": true, 193 | "detail": "rclpy.subscription", 194 | "documentation": {} 195 | }, 196 | { 197 | "label": "Subscription", 198 | "importPath": "rclpy.subscription", 199 | "description": "rclpy.subscription", 200 | "isExtraImport": true, 201 | "detail": "rclpy.subscription", 202 | "documentation": {} 203 | }, 204 | { 205 | "label": "Node", 206 | "importPath": "rclpy.node", 207 | "description": "rclpy.node", 208 | "isExtraImport": true, 209 | "detail": "rclpy.node", 210 | "documentation": {} 211 | }, 212 | { 213 | "label": "Node", 214 | "importPath": "rclpy.node", 215 | "description": "rclpy.node", 216 | "isExtraImport": true, 217 | "detail": "rclpy.node", 218 | "documentation": {} 219 | }, 220 | { 221 | "label": "Node", 222 | "importPath": "rclpy.node", 223 | "description": "rclpy.node", 224 | "isExtraImport": true, 225 | "detail": "rclpy.node", 226 | "documentation": {} 227 | }, 228 | { 229 | "label": "Node", 230 | "importPath": "rclpy.node", 231 | "description": "rclpy.node", 232 | "isExtraImport": true, 233 | "detail": "rclpy.node", 234 | "documentation": {} 235 | }, 236 | { 237 | "label": "Node", 238 | "importPath": "rclpy.node", 239 | "description": "rclpy.node", 240 | "isExtraImport": true, 241 | "detail": "rclpy.node", 242 | "documentation": {} 243 | }, 244 | { 245 | "label": "Node", 246 | "importPath": "rclpy.node", 247 | "description": "rclpy.node", 248 | "isExtraImport": true, 249 | "detail": "rclpy.node", 250 | "documentation": {} 251 | }, 252 | { 253 | "label": "time", 254 | "kind": 6, 255 | "isExtraImport": true, 256 | "importPath": "time", 257 | "description": "time", 258 | "detail": "time", 259 | "documentation": {} 260 | }, 261 | { 262 | "label": "rclpy.callback_groups", 263 | "kind": 6, 264 | "isExtraImport": true, 265 | "importPath": "rclpy.callback_groups", 266 | "description": "rclpy.callback_groups", 267 | "detail": "rclpy.callback_groups", 268 | "documentation": {} 269 | }, 270 | { 271 | "label": "Client", 272 | "importPath": "rclpy.client", 273 | "description": "rclpy.client", 274 | "isExtraImport": true, 275 | "detail": "rclpy.client", 276 | "documentation": {} 277 | }, 278 | { 279 | "label": "Client", 280 | "importPath": "rclpy.client", 281 | "description": "rclpy.client", 282 | "isExtraImport": true, 283 | "detail": "rclpy.client", 284 | "documentation": {} 285 | }, 286 | { 287 | "label": "launch.actions", 288 | "kind": 6, 289 | "isExtraImport": true, 290 | "importPath": "launch.actions", 291 | "description": "launch.actions", 292 | "detail": "launch.actions", 293 | "documentation": {} 294 | }, 295 | { 296 | "label": "get_package_share_directory", 297 | "importPath": "ament_index_python.packages", 298 | "description": "ament_index_python.packages", 299 | "isExtraImport": true, 300 | "detail": "ament_index_python.packages", 301 | "documentation": {} 302 | }, 303 | { 304 | "label": "get_package_share_directory", 305 | "importPath": "ament_index_python.packages", 306 | "description": "ament_index_python.packages", 307 | "isExtraImport": true, 308 | "detail": "ament_index_python.packages", 309 | "documentation": {} 310 | }, 311 | { 312 | "label": "LaunchDescription", 313 | "importPath": "launch", 314 | "description": "launch", 315 | "isExtraImport": true, 316 | "detail": "launch", 317 | "documentation": {} 318 | }, 319 | { 320 | "label": "LaunchDescription", 321 | "importPath": "launch", 322 | "description": "launch", 323 | "isExtraImport": true, 324 | "detail": "launch", 325 | "documentation": {} 326 | }, 327 | { 328 | "label": "Node", 329 | "importPath": "launch_ros.actions", 330 | "description": "launch_ros.actions", 331 | "isExtraImport": true, 332 | "detail": "launch_ros.actions", 333 | "documentation": {} 334 | }, 335 | { 336 | "label": "Node", 337 | "importPath": "launch_ros.actions", 338 | "description": "launch_ros.actions", 339 | "isExtraImport": true, 340 | "detail": "launch_ros.actions", 341 | "documentation": {} 342 | }, 343 | { 344 | "label": "argparse", 345 | "kind": 6, 346 | "isExtraImport": true, 347 | "importPath": "argparse", 348 | "description": "argparse", 349 | "detail": "argparse", 350 | "documentation": {} 351 | }, 352 | { 353 | "label": "OrderedDict", 354 | "importPath": "collections", 355 | "description": "collections", 356 | "isExtraImport": true, 357 | "detail": "collections", 358 | "documentation": {} 359 | }, 360 | { 361 | "label": "OrderedDict", 362 | "importPath": "collections", 363 | "description": "collections", 364 | "isExtraImport": true, 365 | "detail": "collections", 366 | "documentation": {} 367 | }, 368 | { 369 | "label": "Path", 370 | "importPath": "pathlib", 371 | "description": "pathlib", 372 | "isExtraImport": true, 373 | "detail": "pathlib", 374 | "documentation": {} 375 | }, 376 | { 377 | "label": "Path", 378 | "importPath": "pathlib", 379 | "description": "pathlib", 380 | "isExtraImport": true, 381 | "detail": "pathlib", 382 | "documentation": {} 383 | }, 384 | { 385 | "label": "sys", 386 | "kind": 6, 387 | "isExtraImport": true, 388 | "importPath": "sys", 389 | "description": "sys", 390 | "detail": "sys", 391 | "documentation": {} 392 | }, 393 | { 394 | "label": "Metaclass_Test", 395 | "kind": 6, 396 | "importPath": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 397 | "description": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 398 | "peekOfCode": "class Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None\n _TYPE_SUPPORT = None\n __constants = {\n }\n @classmethod", 399 | "detail": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 400 | "documentation": {} 401 | }, 402 | { 403 | "label": "Test", 404 | "kind": 6, 405 | "importPath": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 406 | "description": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 407 | "peekOfCode": "class Test(metaclass=Metaclass_Test):\n \"\"\"Message class 'Test'.\"\"\"\n __slots__ = [\n '_num',\n '_check_fields',\n ]\n _fields_and_field_types = {\n 'num': 'int64',\n }\n # This attribute is used to store an rosidl_parser.definition variable", 408 | "detail": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 409 | "documentation": {} 410 | }, 411 | { 412 | "label": "ros_python_check_fields", 413 | "kind": 5, 414 | "importPath": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 415 | "description": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 416 | "peekOfCode": "ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')\n# Import statements for member types\nimport builtins # noqa: E402, I100\nimport rosidl_parser.definition # noqa: E402, I100\nclass Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None", 417 | "detail": "build.test_msg.ament_cmake_python.test_msg.test_msg.msg._test", 418 | "documentation": {} 419 | }, 420 | { 421 | "label": "Metaclass_Test", 422 | "kind": 6, 423 | "importPath": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 424 | "description": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 425 | "peekOfCode": "class Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None\n _TYPE_SUPPORT = None\n __constants = {\n }\n @classmethod", 426 | "detail": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 427 | "documentation": {} 428 | }, 429 | { 430 | "label": "Test", 431 | "kind": 6, 432 | "importPath": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 433 | "description": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 434 | "peekOfCode": "class Test(metaclass=Metaclass_Test):\n \"\"\"Message class 'Test'.\"\"\"\n __slots__ = [\n '_num',\n '_check_fields',\n ]\n _fields_and_field_types = {\n 'num': 'int64',\n }\n # This attribute is used to store an rosidl_parser.definition variable", 435 | "detail": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 436 | "documentation": {} 437 | }, 438 | { 439 | "label": "ros_python_check_fields", 440 | "kind": 5, 441 | "importPath": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 442 | "description": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 443 | "peekOfCode": "ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')\n# Import statements for member types\nimport builtins # noqa: E402, I100\nimport rosidl_parser.definition # noqa: E402, I100\nclass Metaclass_Test(type):\n \"\"\"Metaclass of message 'Test'.\"\"\"\n _CREATE_ROS_MESSAGE = None\n _CONVERT_FROM_PY = None\n _CONVERT_TO_PY = None\n _DESTROY_ROS_MESSAGE = None", 444 | "detail": "build.test_msg.rosidl_generator_py.test_msg.msg._test", 445 | "documentation": {} 446 | }, 447 | { 448 | "label": "Example_Node", 449 | "kind": 6, 450 | "importPath": "install.test_plot.lib.test_plot.test_plot", 451 | "description": "install.test_plot.lib.test_plot.test_plot", 452 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"", 453 | "detail": "install.test_plot.lib.test_plot.test_plot", 454 | "documentation": {} 455 | }, 456 | { 457 | "label": "main", 458 | "kind": 2, 459 | "importPath": "install.test_plot.lib.test_plot.test_plot", 460 | "description": "install.test_plot.lib.test_plot.test_plot", 461 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()", 462 | "detail": "install.test_plot.lib.test_plot.test_plot", 463 | "documentation": {} 464 | }, 465 | { 466 | "label": "Example_Node", 467 | "kind": 6, 468 | "importPath": "install.test_plot.lib.test_plot.test_plot1", 469 | "description": "install.test_plot.lib.test_plot.test_plot1", 470 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"", 471 | "detail": "install.test_plot.lib.test_plot.test_plot1", 472 | "documentation": {} 473 | }, 474 | { 475 | "label": "main", 476 | "kind": 2, 477 | "importPath": "install.test_plot.lib.test_plot.test_plot1", 478 | "description": "install.test_plot.lib.test_plot.test_plot1", 479 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()", 480 | "detail": "install.test_plot.lib.test_plot.test_plot1", 481 | "documentation": {} 482 | }, 483 | { 484 | "label": "TestPub", 485 | "kind": 6, 486 | "importPath": "install.test_plot.lib.test_plot.test_pub", 487 | "description": "install.test_plot.lib.test_plot.test_pub", 488 | "peekOfCode": "class TestPub(Node):\n def __init__(self) -> None:\n super().__init__(\"test_pub\")\n self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()\n self.pub = self.create_publisher(Test, \"test\", 10, callback_group=self.cbg)\n self.counter = 4\n time.sleep(5)\n self.timer = self.create_timer(1, self.loop_pub)\n def loop_pub(self) -> None:\n self.counter += 1", 489 | "detail": "install.test_plot.lib.test_plot.test_pub", 490 | "documentation": {} 491 | }, 492 | { 493 | "label": "main", 494 | "kind": 2, 495 | "importPath": "install.test_plot.lib.test_plot.test_pub", 496 | "description": "install.test_plot.lib.test_plot.test_pub", 497 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = TestPub()\n rclpy.spin(node)\n rclpy.shutdown()\nif __name__ == \"__main__\":\n main()", 498 | "detail": "install.test_plot.lib.test_plot.test_pub", 499 | "documentation": {} 500 | }, 501 | { 502 | "label": "generate_launch_description", 503 | "kind": 2, 504 | "importPath": "install.test_plot.share.test_plot.launch.test.launch", 505 | "description": "install.test_plot.share.test_plot.launch.test.launch", 506 | "peekOfCode": "def generate_launch_description():\n test_plotter = Node(\n package=\"test_plot\",\n executable=\"test_plot1.py\",\n name=\"test_plotter\",\n output=\"screen\",\n emulate_tty=True,\n )\n test_pub = Node(\n package=\"test_plot\",", 507 | "detail": "install.test_plot.share.test_plot.launch.test.launch", 508 | "documentation": {} 509 | }, 510 | { 511 | "label": "main", 512 | "kind": 2, 513 | "importPath": "install._local_setup_util_ps1", 514 | "description": "install._local_setup_util_ps1", 515 | "peekOfCode": "def main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',\n help='The additional file extension to be considered')", 516 | "detail": "install._local_setup_util_ps1", 517 | "documentation": {} 518 | }, 519 | { 520 | "label": "get_packages", 521 | "kind": 2, 522 | "importPath": "install._local_setup_util_ps1", 523 | "description": "install._local_setup_util_ps1", 524 | "peekOfCode": "def get_packages(prefix_path, merged_install):\n \"\"\"\n Find packages based on colcon-specific files created during installation.\n :param Path prefix_path: The install prefix path of all packages\n :param bool merged_install: The flag if the packages are all installed\n directly in the prefix or if each package is installed in a subdirectory\n named after the package\n :returns: A mapping from the package name to the set of runtime\n dependencies\n :rtype: dict", 525 | "detail": "install._local_setup_util_ps1", 526 | "documentation": {} 527 | }, 528 | { 529 | "label": "add_package_runtime_dependencies", 530 | "kind": 2, 531 | "importPath": "install._local_setup_util_ps1", 532 | "description": "install._local_setup_util_ps1", 533 | "peekOfCode": "def add_package_runtime_dependencies(path, packages):\n \"\"\"\n Check the path and if it exists extract the packages runtime dependencies.\n :param Path path: The resource file containing the runtime dependencies\n :param dict packages: A mapping from package names to the sets of runtime\n dependencies to add to\n \"\"\"\n content = path.read_text()\n dependencies = set(content.split(os.pathsep) if content else [])\n packages[path.name] = dependencies", 534 | "detail": "install._local_setup_util_ps1", 535 | "documentation": {} 536 | }, 537 | { 538 | "label": "order_packages", 539 | "kind": 2, 540 | "importPath": "install._local_setup_util_ps1", 541 | "description": "install._local_setup_util_ps1", 542 | "peekOfCode": "def order_packages(packages):\n \"\"\"\n Order packages topologically.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies\n :returns: The package names\n :rtype: list\n \"\"\"\n # select packages with no dependencies in alphabetical order\n to_be_ordered = list(packages.keys())", 543 | "detail": "install._local_setup_util_ps1", 544 | "documentation": {} 545 | }, 546 | { 547 | "label": "reduce_cycle_set", 548 | "kind": 2, 549 | "importPath": "install._local_setup_util_ps1", 550 | "description": "install._local_setup_util_ps1", 551 | "peekOfCode": "def reduce_cycle_set(packages):\n \"\"\"\n Reduce the set of packages to the ones part of the circular dependency.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies which is modified in place\n \"\"\"\n last_depended = None\n while len(packages) > 0:\n # get all remaining dependencies\n depended = set()", 552 | "detail": "install._local_setup_util_ps1", 553 | "documentation": {} 554 | }, 555 | { 556 | "label": "get_commands", 557 | "kind": 2, 558 | "importPath": "install._local_setup_util_ps1", 559 | "description": "install._local_setup_util_ps1", 560 | "peekOfCode": "def get_commands(pkg_name, prefix, primary_extension, additional_extension):\n commands = []\n package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv')\n if os.path.exists(package_dsv_path):\n commands += process_dsv_file(\n package_dsv_path, prefix, primary_extension, additional_extension)\n return commands\ndef process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):", 561 | "detail": "install._local_setup_util_ps1", 562 | "documentation": {} 563 | }, 564 | { 565 | "label": "process_dsv_file", 566 | "kind": 2, 567 | "importPath": "install._local_setup_util_ps1", 568 | "description": "install._local_setup_util_ps1", 569 | "peekOfCode": "def process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):\n commands = []\n if _include_comments():\n commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path}))\n with open(dsv_path, 'r') as h:\n content = h.read()\n lines = content.splitlines()\n basenames = OrderedDict()", 570 | "detail": "install._local_setup_util_ps1", 571 | "documentation": {} 572 | }, 573 | { 574 | "label": "handle_dsv_types_except_source", 575 | "kind": 2, 576 | "importPath": "install._local_setup_util_ps1", 577 | "description": "install._local_setup_util_ps1", 578 | "peekOfCode": "def handle_dsv_types_except_source(type_, remainder, prefix):\n commands = []\n if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET):\n try:\n env_name, value = remainder.split(';', 1)\n except ValueError:\n raise RuntimeError(\n \"doesn't contain a semicolon separating the environment name \"\n 'from the value')\n try_prefixed_value = os.path.join(prefix, value) if value else prefix", 579 | "detail": "install._local_setup_util_ps1", 580 | "documentation": {} 581 | }, 582 | { 583 | "label": "FORMAT_STR_COMMENT_LINE", 584 | "kind": 5, 585 | "importPath": "install._local_setup_util_ps1", 586 | "description": "install._local_setup_util_ps1", 587 | "peekOfCode": "FORMAT_STR_COMMENT_LINE = '# {comment}'\nFORMAT_STR_SET_ENV_VAR = 'Set-Item -Path \"Env:{name}\" -Value \"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '$env:{name}'\nFORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'", 588 | "detail": "install._local_setup_util_ps1", 589 | "documentation": {} 590 | }, 591 | { 592 | "label": "FORMAT_STR_SET_ENV_VAR", 593 | "kind": 5, 594 | "importPath": "install._local_setup_util_ps1", 595 | "description": "install._local_setup_util_ps1", 596 | "peekOfCode": "FORMAT_STR_SET_ENV_VAR = 'Set-Item -Path \"Env:{name}\" -Value \"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '$env:{name}'\nFORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'", 597 | "detail": "install._local_setup_util_ps1", 598 | "documentation": {} 599 | }, 600 | { 601 | "label": "FORMAT_STR_USE_ENV_VAR", 602 | "kind": 5, 603 | "importPath": "install._local_setup_util_ps1", 604 | "description": "install._local_setup_util_ps1", 605 | "peekOfCode": "FORMAT_STR_USE_ENV_VAR = '$env:{name}'\nFORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'", 606 | "detail": "install._local_setup_util_ps1", 607 | "documentation": {} 608 | }, 609 | { 610 | "label": "FORMAT_STR_INVOKE_SCRIPT", 611 | "kind": 5, 612 | "importPath": "install._local_setup_util_ps1", 613 | "description": "install._local_setup_util_ps1", 614 | "peekOfCode": "FORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103", 615 | "detail": "install._local_setup_util_ps1", 616 | "documentation": {} 617 | }, 618 | { 619 | "label": "FORMAT_STR_REMOVE_LEADING_SEPARATOR", 620 | "kind": 5, 621 | "importPath": "install._local_setup_util_ps1", 622 | "description": "install._local_setup_util_ps1", 623 | "peekOfCode": "FORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(", 624 | "detail": "install._local_setup_util_ps1", 625 | "documentation": {} 626 | }, 627 | { 628 | "label": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR", 629 | "kind": 5, 630 | "importPath": "install._local_setup_util_ps1", 631 | "description": "install._local_setup_util_ps1", 632 | "peekOfCode": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '", 633 | "detail": "install._local_setup_util_ps1", 634 | "documentation": {} 635 | }, 636 | { 637 | "label": "DSV_TYPE_APPEND_NON_DUPLICATE", 638 | "kind": 5, 639 | "importPath": "install._local_setup_util_ps1", 640 | "description": "install._local_setup_util_ps1", 641 | "peekOfCode": "DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')", 642 | "detail": "install._local_setup_util_ps1", 643 | "documentation": {} 644 | }, 645 | { 646 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE", 647 | "kind": 5, 648 | "importPath": "install._local_setup_util_ps1", 649 | "description": "install._local_setup_util_ps1", 650 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(", 651 | "detail": "install._local_setup_util_ps1", 652 | "documentation": {} 653 | }, 654 | { 655 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS", 656 | "kind": 5, 657 | "importPath": "install._local_setup_util_ps1", 658 | "description": "install._local_setup_util_ps1", 659 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',", 660 | "detail": "install._local_setup_util_ps1", 661 | "documentation": {} 662 | }, 663 | { 664 | "label": "DSV_TYPE_SET", 665 | "kind": 5, 666 | "importPath": "install._local_setup_util_ps1", 667 | "description": "install._local_setup_util_ps1", 668 | "peekOfCode": "DSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')", 669 | "detail": "install._local_setup_util_ps1", 670 | "documentation": {} 671 | }, 672 | { 673 | "label": "DSV_TYPE_SET_IF_UNSET", 674 | "kind": 5, 675 | "importPath": "install._local_setup_util_ps1", 676 | "description": "install._local_setup_util_ps1", 677 | "peekOfCode": "DSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(", 678 | "detail": "install._local_setup_util_ps1", 679 | "documentation": {} 680 | }, 681 | { 682 | "label": "DSV_TYPE_SOURCE", 683 | "kind": 5, 684 | "importPath": "install._local_setup_util_ps1", 685 | "description": "install._local_setup_util_ps1", 686 | "peekOfCode": "DSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',", 687 | "detail": "install._local_setup_util_ps1", 688 | "documentation": {} 689 | }, 690 | { 691 | "label": "env_state", 692 | "kind": 5, 693 | "importPath": "install._local_setup_util_ps1", 694 | "description": "install._local_setup_util_ps1", 695 | "peekOfCode": "env_state = {}\ndef _append_unique_value(name, value):\n global env_state\n if name not in env_state:\n if os.environ.get(name):\n env_state[name] = set(os.environ[name].split(os.pathsep))\n else:\n env_state[name] = set()\n # append even if the variable has not been set yet, in case a shell script sets the\n # same variable without the knowledge of this Python script.", 696 | "detail": "install._local_setup_util_ps1", 697 | "documentation": {} 698 | }, 699 | { 700 | "label": "main", 701 | "kind": 2, 702 | "importPath": "install._local_setup_util_sh", 703 | "description": "install._local_setup_util_sh", 704 | "peekOfCode": "def main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',\n help='The additional file extension to be considered')", 705 | "detail": "install._local_setup_util_sh", 706 | "documentation": {} 707 | }, 708 | { 709 | "label": "get_packages", 710 | "kind": 2, 711 | "importPath": "install._local_setup_util_sh", 712 | "description": "install._local_setup_util_sh", 713 | "peekOfCode": "def get_packages(prefix_path, merged_install):\n \"\"\"\n Find packages based on colcon-specific files created during installation.\n :param Path prefix_path: The install prefix path of all packages\n :param bool merged_install: The flag if the packages are all installed\n directly in the prefix or if each package is installed in a subdirectory\n named after the package\n :returns: A mapping from the package name to the set of runtime\n dependencies\n :rtype: dict", 714 | "detail": "install._local_setup_util_sh", 715 | "documentation": {} 716 | }, 717 | { 718 | "label": "add_package_runtime_dependencies", 719 | "kind": 2, 720 | "importPath": "install._local_setup_util_sh", 721 | "description": "install._local_setup_util_sh", 722 | "peekOfCode": "def add_package_runtime_dependencies(path, packages):\n \"\"\"\n Check the path and if it exists extract the packages runtime dependencies.\n :param Path path: The resource file containing the runtime dependencies\n :param dict packages: A mapping from package names to the sets of runtime\n dependencies to add to\n \"\"\"\n content = path.read_text()\n dependencies = set(content.split(os.pathsep) if content else [])\n packages[path.name] = dependencies", 723 | "detail": "install._local_setup_util_sh", 724 | "documentation": {} 725 | }, 726 | { 727 | "label": "order_packages", 728 | "kind": 2, 729 | "importPath": "install._local_setup_util_sh", 730 | "description": "install._local_setup_util_sh", 731 | "peekOfCode": "def order_packages(packages):\n \"\"\"\n Order packages topologically.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies\n :returns: The package names\n :rtype: list\n \"\"\"\n # select packages with no dependencies in alphabetical order\n to_be_ordered = list(packages.keys())", 732 | "detail": "install._local_setup_util_sh", 733 | "documentation": {} 734 | }, 735 | { 736 | "label": "reduce_cycle_set", 737 | "kind": 2, 738 | "importPath": "install._local_setup_util_sh", 739 | "description": "install._local_setup_util_sh", 740 | "peekOfCode": "def reduce_cycle_set(packages):\n \"\"\"\n Reduce the set of packages to the ones part of the circular dependency.\n :param dict packages: A mapping from package name to the set of runtime\n dependencies which is modified in place\n \"\"\"\n last_depended = None\n while len(packages) > 0:\n # get all remaining dependencies\n depended = set()", 741 | "detail": "install._local_setup_util_sh", 742 | "documentation": {} 743 | }, 744 | { 745 | "label": "get_commands", 746 | "kind": 2, 747 | "importPath": "install._local_setup_util_sh", 748 | "description": "install._local_setup_util_sh", 749 | "peekOfCode": "def get_commands(pkg_name, prefix, primary_extension, additional_extension):\n commands = []\n package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv')\n if os.path.exists(package_dsv_path):\n commands += process_dsv_file(\n package_dsv_path, prefix, primary_extension, additional_extension)\n return commands\ndef process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):", 750 | "detail": "install._local_setup_util_sh", 751 | "documentation": {} 752 | }, 753 | { 754 | "label": "process_dsv_file", 755 | "kind": 2, 756 | "importPath": "install._local_setup_util_sh", 757 | "description": "install._local_setup_util_sh", 758 | "peekOfCode": "def process_dsv_file(\n dsv_path, prefix, primary_extension=None, additional_extension=None\n):\n commands = []\n if _include_comments():\n commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path}))\n with open(dsv_path, 'r') as h:\n content = h.read()\n lines = content.splitlines()\n basenames = OrderedDict()", 759 | "detail": "install._local_setup_util_sh", 760 | "documentation": {} 761 | }, 762 | { 763 | "label": "handle_dsv_types_except_source", 764 | "kind": 2, 765 | "importPath": "install._local_setup_util_sh", 766 | "description": "install._local_setup_util_sh", 767 | "peekOfCode": "def handle_dsv_types_except_source(type_, remainder, prefix):\n commands = []\n if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET):\n try:\n env_name, value = remainder.split(';', 1)\n except ValueError:\n raise RuntimeError(\n \"doesn't contain a semicolon separating the environment name \"\n 'from the value')\n try_prefixed_value = os.path.join(prefix, value) if value else prefix", 768 | "detail": "install._local_setup_util_sh", 769 | "documentation": {} 770 | }, 771 | { 772 | "label": "FORMAT_STR_COMMENT_LINE", 773 | "kind": 5, 774 | "importPath": "install._local_setup_util_sh", 775 | "description": "install._local_setup_util_sh", 776 | "peekOfCode": "FORMAT_STR_COMMENT_LINE = '# {comment}'\nFORMAT_STR_SET_ENV_VAR = 'export {name}=\"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '${name}'\nFORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'", 777 | "detail": "install._local_setup_util_sh", 778 | "documentation": {} 779 | }, 780 | { 781 | "label": "FORMAT_STR_SET_ENV_VAR", 782 | "kind": 5, 783 | "importPath": "install._local_setup_util_sh", 784 | "description": "install._local_setup_util_sh", 785 | "peekOfCode": "FORMAT_STR_SET_ENV_VAR = 'export {name}=\"{value}\"'\nFORMAT_STR_USE_ENV_VAR = '${name}'\nFORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'", 786 | "detail": "install._local_setup_util_sh", 787 | "documentation": {} 788 | }, 789 | { 790 | "label": "FORMAT_STR_USE_ENV_VAR", 791 | "kind": 5, 792 | "importPath": "install._local_setup_util_sh", 793 | "description": "install._local_setup_util_sh", 794 | "peekOfCode": "FORMAT_STR_USE_ENV_VAR = '${name}'\nFORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'", 795 | "detail": "install._local_setup_util_sh", 796 | "documentation": {} 797 | }, 798 | { 799 | "label": "FORMAT_STR_INVOKE_SCRIPT", 800 | "kind": 5, 801 | "importPath": "install._local_setup_util_sh", 802 | "description": "install._local_setup_util_sh", 803 | "peekOfCode": "FORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX=\"{prefix}\" _colcon_prefix_sh_source_script \"{script_path}\"' # noqa: E501\nFORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103", 804 | "detail": "install._local_setup_util_sh", 805 | "documentation": {} 806 | }, 807 | { 808 | "label": "FORMAT_STR_REMOVE_LEADING_SEPARATOR", 809 | "kind": 5, 810 | "importPath": "install._local_setup_util_sh", 811 | "description": "install._local_setup_util_sh", 812 | "peekOfCode": "FORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ \"$(echo -n ${name} | head -c 1)\" = \":\" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501\nFORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(", 813 | "detail": "install._local_setup_util_sh", 814 | "documentation": {} 815 | }, 816 | { 817 | "label": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR", 818 | "kind": 5, 819 | "importPath": "install._local_setup_util_sh", 820 | "description": "install._local_setup_util_sh", 821 | "peekOfCode": "FORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ \"$(echo -n ${name} | tail -c 1)\" = \":\" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501\nDSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '", 822 | "detail": "install._local_setup_util_sh", 823 | "documentation": {} 824 | }, 825 | { 826 | "label": "DSV_TYPE_APPEND_NON_DUPLICATE", 827 | "kind": 5, 828 | "importPath": "install._local_setup_util_sh", 829 | "description": "install._local_setup_util_sh", 830 | "peekOfCode": "DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')", 831 | "detail": "install._local_setup_util_sh", 832 | "documentation": {} 833 | }, 834 | { 835 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE", 836 | "kind": 5, 837 | "importPath": "install._local_setup_util_sh", 838 | "description": "install._local_setup_util_sh", 839 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate'\nDSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(", 840 | "detail": "install._local_setup_util_sh", 841 | "documentation": {} 842 | }, 843 | { 844 | "label": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS", 845 | "kind": 5, 846 | "importPath": "install._local_setup_util_sh", 847 | "description": "install._local_setup_util_sh", 848 | "peekOfCode": "DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists'\nDSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',", 849 | "detail": "install._local_setup_util_sh", 850 | "documentation": {} 851 | }, 852 | { 853 | "label": "DSV_TYPE_SET", 854 | "kind": 5, 855 | "importPath": "install._local_setup_util_sh", 856 | "description": "install._local_setup_util_sh", 857 | "peekOfCode": "DSV_TYPE_SET = 'set'\nDSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')", 858 | "detail": "install._local_setup_util_sh", 859 | "documentation": {} 860 | }, 861 | { 862 | "label": "DSV_TYPE_SET_IF_UNSET", 863 | "kind": 5, 864 | "importPath": "install._local_setup_util_sh", 865 | "description": "install._local_setup_util_sh", 866 | "peekOfCode": "DSV_TYPE_SET_IF_UNSET = 'set-if-unset'\nDSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(", 867 | "detail": "install._local_setup_util_sh", 868 | "documentation": {} 869 | }, 870 | { 871 | "label": "DSV_TYPE_SOURCE", 872 | "kind": 5, 873 | "importPath": "install._local_setup_util_sh", 874 | "description": "install._local_setup_util_sh", 875 | "peekOfCode": "DSV_TYPE_SOURCE = 'source'\ndef main(argv=sys.argv[1:]): # noqa: D103\n parser = argparse.ArgumentParser(\n description='Output shell commands for the packages in topological '\n 'order')\n parser.add_argument(\n 'primary_extension',\n help='The file extension of the primary shell')\n parser.add_argument(\n 'additional_extension', nargs='?',", 876 | "detail": "install._local_setup_util_sh", 877 | "documentation": {} 878 | }, 879 | { 880 | "label": "env_state", 881 | "kind": 5, 882 | "importPath": "install._local_setup_util_sh", 883 | "description": "install._local_setup_util_sh", 884 | "peekOfCode": "env_state = {}\ndef _append_unique_value(name, value):\n global env_state\n if name not in env_state:\n if os.environ.get(name):\n env_state[name] = set(os.environ[name].split(os.pathsep))\n else:\n env_state[name] = set()\n # append even if the variable has not been set yet, in case a shell script sets the\n # same variable without the knowledge of this Python script.", 885 | "detail": "install._local_setup_util_sh", 886 | "documentation": {} 887 | }, 888 | { 889 | "label": "generate_launch_description", 890 | "kind": 2, 891 | "importPath": "src.test_plot.launch.test.launch", 892 | "description": "src.test_plot.launch.test.launch", 893 | "peekOfCode": "def generate_launch_description():\n test_plotter = Node(\n package=\"test_plot\",\n executable=\"test_plot1.py\",\n name=\"test_plotter\",\n output=\"screen\",\n emulate_tty=True,\n )\n test_pub = Node(\n package=\"test_plot\",", 894 | "detail": "src.test_plot.launch.test.launch", 895 | "documentation": {} 896 | }, 897 | { 898 | "label": "Example_Node", 899 | "kind": 6, 900 | "importPath": "src.test_plot.scripts.test_plot", 901 | "description": "src.test_plot.scripts.test_plot", 902 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"", 903 | "detail": "src.test_plot.scripts.test_plot", 904 | "documentation": {} 905 | }, 906 | { 907 | "label": "main", 908 | "kind": 2, 909 | "importPath": "src.test_plot.scripts.test_plot", 910 | "description": "src.test_plot.scripts.test_plot", 911 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()", 912 | "detail": "src.test_plot.scripts.test_plot", 913 | "documentation": {} 914 | }, 915 | { 916 | "label": "Example_Node", 917 | "kind": 6, 918 | "importPath": "src.test_plot.scripts.test_plot1", 919 | "description": "src.test_plot.scripts.test_plot1", 920 | "peekOfCode": "class Example_Node(Node):\n \"\"\"Example Node for showing how to use matplotlib within ros 2 node\n Attributes:\n fig: Figure object for matplotlib\n ax: Axes object for matplotlib\n x: x values for matplotlib\n y: y values for matplotlib\n lock: lock for threading\n _sub: Subscriber for node\n \"\"\"", 921 | "detail": "src.test_plot.scripts.test_plot1", 922 | "documentation": {} 923 | }, 924 | { 925 | "label": "main", 926 | "kind": 2, 927 | "importPath": "src.test_plot.scripts.test_plot1", 928 | "description": "src.test_plot.scripts.test_plot1", 929 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = Example_Node()\n executor = rclpy.executors.MultiThreadedExecutor()\n executor.add_node(node)\n thread = threading.Thread(target=executor.spin, daemon=True)\n thread.start()\n node._plt()\nif __name__ == \"__main__\":\n main()", 930 | "detail": "src.test_plot.scripts.test_plot1", 931 | "documentation": {} 932 | }, 933 | { 934 | "label": "TestPub", 935 | "kind": 6, 936 | "importPath": "src.test_plot.scripts.test_pub", 937 | "description": "src.test_plot.scripts.test_pub", 938 | "peekOfCode": "class TestPub(Node):\n def __init__(self) -> None:\n super().__init__(\"test_pub\")\n self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup()\n self.pub = self.create_publisher(Test, \"test\", 10, callback_group=self.cbg)\n self.counter = 4\n time.sleep(5)\n self.timer = self.create_timer(1, self.loop_pub)\n def loop_pub(self) -> None:\n self.counter += 1", 939 | "detail": "src.test_plot.scripts.test_pub", 940 | "documentation": {} 941 | }, 942 | { 943 | "label": "main", 944 | "kind": 2, 945 | "importPath": "src.test_plot.scripts.test_pub", 946 | "description": "src.test_plot.scripts.test_pub", 947 | "peekOfCode": "def main(args=None):\n rclpy.init(args=args)\n node = TestPub()\n rclpy.spin(node)\n rclpy.shutdown()\nif __name__ == \"__main__\":\n main()", 948 | "detail": "src.test_plot.scripts.test_pub", 949 | "documentation": {} 950 | } 951 | ] -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "browse": { 5 | "databaseFilename": "${default}", 6 | "limitSymbolsToIncludedHeaders": false 7 | }, 8 | "includePath": [ 9 | "/home/tim-ub-pc/Documents/code/plt_ws/matplotlib_ros_tutorials/install/test_msg/include/**", 10 | "/home/tim-ub-pc/Documents/code/SA/install/power_energy_propogation/include/**", 11 | "/home/tim-ub-pc/Documents/code/SA/install/cc_msgs/include/**", 12 | "/opt/ros/jazzy/include/**", 13 | "include/**", 14 | "/usr/include/**" 15 | ], 16 | "name": "ROS", 17 | "intelliSenseMode": "gcc-x64", 18 | "compilerPath": "/usr/bin/gcc", 19 | "cStandard": "gnu11", 20 | "cppStandard": "c++14" 21 | } 22 | ], 23 | "version": 4 24 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.autoComplete.extraPaths": [ 3 | "/home/tim-ub-pc/Documents/code/plt_ws/matplotlib_ros_tutorials/install/test_msg/lib/python3.12/site-packages", 4 | "/home/tim-ub-pc/Documents/code/SA/install/cc_msgs/lib/python3.12/site-packages", 5 | "/opt/ros/jazzy/lib/python3.12/site-packages" 6 | ], 7 | "python.analysis.extraPaths": [ 8 | "/home/tim-ub-pc/Documents/code/plt_ws/matplotlib_ros_tutorials/install/test_msg/lib/python3.12/site-packages", 9 | "/home/tim-ub-pc/Documents/code/SA/install/cc_msgs/lib/python3.12/site-packages", 10 | "/opt/ros/jazzy/lib/python3.12/site-packages" 11 | ] 12 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using MatplotLib with threading in Python to allow for Seamless Real Time Data Visualization 2 | 3 | ## Explanation of Threading and Matplotlib 4 | Matplotlib requires synchronous plotting functionality to occur on the main thread. Within ROS anything that is executed in a timer or callback is executed on a secondary thread. This means if a user is attempting to plot regularly from information that is retrieved from either service-client, or publisher-subscriber requires manual pushing of plotting to the main thread to allow for the images to be displayed. 5 | 6 | To explain how to do this two examples are shown. The first example shows how the threading can be achieved without ROS to explain what is happening on the Python side. The second example follows similar logic to the first example but is implemented in ROS2 to show what changes are specific to ROS. 7 | 8 | # Example of Threading Without ROS 9 | 10 | ```python 11 | import numpy as np 12 | import matplotlib.pyplot as plt 13 | import matplotlib.animation as anim 14 | import threading 15 | import time 16 | 17 | 18 | class Tester: 19 | """Example of how to use matplotlib with threading. 20 | 21 | Attributes: 22 | fig: Figure object for matplotlib 23 | ax: Axes object for matplotlib 24 | x: x values for matplotlib 25 | y: y values for matplotlib 26 | counter: counter for class 27 | lock: lock for matplotlib 28 | """ 29 | 30 | def __init__(self): 31 | # Initialize figure and axes and save to class 32 | self.fig, self.ax = plt.subplots() 33 | # create initial values to plot 34 | self.plt_x = [i for i in range(5)] 35 | self.plt_width = [0.5 for i in range(5)] 36 | self.plt_counter = 0 37 | self._lock = threading.Lock() 38 | 39 | def plt_func(self, _): 40 | """Function for matplotlib animation. 41 | 42 | Args: 43 | _: Dummy variable for matplotlib animation 44 | 45 | Returns: 46 | Axes object for matplotlib 47 | """ 48 | # lock thread 49 | with self._lock: 50 | x = np.array(self.plt_x) 51 | y = np.array(self.plt_width) 52 | self.ax.barh(y, 0.5, left=x, color="red") 53 | return self.ax 54 | 55 | def loop_logic(self): 56 | """Looping logic for adding data to plot.""" 57 | time.sleep(3) 58 | print("init") 59 | while True: 60 | self.plt_add() 61 | time.sleep(1) 62 | 63 | def plt_add(self): 64 | """"Add data to class arrays.""" 65 | with self._lock: 66 | if self.plt_counter > 4: 67 | self.plt_x.append(self.plt_counter) 68 | self.plt_width.append(0.5) 69 | self.plt_counter += 1 70 | print(self.plt_counter) 71 | 72 | def _plt(self): 73 | print("INIT PLOT") 74 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000) 75 | print("showing") 76 | plt.show() 77 | 78 | 79 | if __name__ == "__main__": 80 | test = Tester() 81 | thread = threading.Thread(target=test.loop_logic) 82 | thread.start() 83 | test._plt() 84 | ``` 85 | 86 | # Explanation of Example 1 87 | 88 | In this example, the class Tester is used to simulate a node class when used in ROS. First, in the init function, a Matplotlib figure and axis are saved to the class. Both will be continually updated throughout the lifecycle of the class. The x, and y member variables are initialized with data to show how to update existing data. A threading lock must also be created to prevent the simultaneous access of the data that is to be plotted. 89 | 90 | The loop logic function begins by adding data to the x and y member variables every three seconds. This loop logic calls a second function ```plt_add``` that uses the threading lock ```_lock``` to add data to the member variables x and y. 91 | 92 | To allow for continuous updating of the axis the ```FuncAnimation``` function is used which updates the figures with a given callable at the frequency given by the keyword argument interval. Any callable used by FuncAnimation must return the axis that will update the given figure. 93 | 94 | The function ```plt_func``` follows this convention using the threading lock to retrieve the data from the member variables x and y and adding them to the axis and returning it. 95 | 96 | In the main function, the class is initialized and the ```loop_logic``` function is placed into a second thread and the thread is started. The ```_plt``` function is started in the main thread. 97 | 98 | # Example Using ROS 99 | 100 | Using this same logic the next example implements this plotting in a ROS NODE. 101 | 102 | ```python 103 | #! /usr/bin/env python3 104 | 105 | import threading 106 | import typing 107 | 108 | import matplotlib.pyplot as plt 109 | import matplotlib.animation as anim 110 | import numpy as np 111 | import numpy.typing as npt 112 | import rclpy 113 | from test_msg.msg import Test 114 | from rclpy.subscription import Subscription 115 | from rclpy.node import Node 116 | 117 | 118 | class Example_Node(Node): 119 | """Example Node for showing how to use matplotlib within ros 2 node 120 | 121 | Attributes: 122 | fig: Figure object for matplotlib 123 | ax: Axes object for matplotlib 124 | x: x values for matplotlib 125 | y: y values for matplotlib 126 | lock: lock for threading 127 | _sub: Subscriber for node 128 | """ 129 | 130 | def __init__(self): 131 | """Initialize.""" 132 | super().__init__("example_node") 133 | # Initialize figure and axes and save to class 134 | self.fig, self.ax = plt.subplots() 135 | # create Thread lock to prevent multiaccess threading errors 136 | self._lock = threading.Lock() 137 | # create initial values to plot 138 | self.x = [i for i in range(5)] 139 | self.width = [0.5 for i in range(5)] 140 | # create subscriber 141 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup() 142 | self._sub: Subscription = self.create_subscription( 143 | Test, "test", self._callback, 10, callback_group=self.cbg 144 | ) 145 | 146 | def _callback(self, msg: Test): 147 | """Callback for subscriber 148 | 149 | Args: 150 | msg: message from subscriber 151 | Message format 152 | ---------------- 153 | int32 num 154 | """ 155 | # lock thread 156 | with self._lock: 157 | # update values 158 | number: int = msg.num 159 | self.x.append(number) 160 | self.width.append(0.5) 161 | 162 | def plt_func(self, _): 163 | """Function for for adding data to axis. 164 | 165 | Args: 166 | _ : Dummy variable that is required for matplotlib animation. 167 | 168 | Returns: 169 | Axes object for matplotlib 170 | """ 171 | # lock thread 172 | with self._lock: 173 | x = np.array(self.x) 174 | y = np.array(self.width) 175 | self.ax.barh(y, 0.5, left=x, color="red") 176 | 177 | return self.ax 178 | 179 | def _plt(self): 180 | """Function for initializing and showing matplotlib animation.""" 181 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000) 182 | plt.show() 183 | 184 | 185 | def main(args=None): 186 | rclpy.init(args=args) 187 | node = Example_Node() 188 | executor = rclpy.executors.MultiThreadedExecutor() 189 | executor.add_node(node) 190 | thread = threading.Thread(target=executor.spin, daemon=True) 191 | thread.start() 192 | node._plt() 193 | 194 | 195 | if __name__ == "__main__": 196 | main() 197 | 198 | ``` 199 | 200 | # Major Differences Between Example 1 and 2 201 | 202 | The main change is the loop_logic function from the previous example becomes a callback for the class's subscriber. 203 | 204 | Then in the main function, a multi-threaded executor is created and the example node is added to the executor. The spinning of this executor is then placed into the thread and an extra keyword is added ```daemon=True``` to allow for the ROS2 daemon to execute. Otherwise, the layout is the same. 205 | 206 | # Location of Examples and How to Run 207 | 208 | The Example Code can be found within the ```src``` folder of this repository. To run the examples, first, clone the repository and then build the package using colcon. To build this repository you must be using ROS2 Foxy or newer. The following commands will install the required dependencies, build the package, and run the example. 209 | 210 | ```bash 211 | cd "repository" 212 | source /opt/ros/$ROSDISTRO/setup.bash 213 | pip install -r requirements.txt 214 | colcon build 215 | . install/setup.bash 216 | ros2 launch test_plot test.launch.py 217 | ``` 218 | 219 | # Boiler Plate Code 220 | 221 | # Boiler Plate Code 222 | 223 | Boiler plate code for plotting can be found on my profile and can be cloned with the following command. 224 | 225 | ```bash 226 | git clone https://github.com/timdodge54/boiler_plate_plotting.git 227 | ``` 228 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | numpy -------------------------------------------------------------------------------- /src/test_msg/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(test_msg) 3 | 4 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 5 | add_compile_options(-Wall -Wextra -Wpedantic) 6 | endif() 7 | 8 | # find dependencies 9 | find_package(ament_cmake REQUIRED) 10 | 11 | find_package(std_msgs REQUIRED) 12 | find_package(rosidl_default_generators REQUIRED) 13 | find_package(builtin_interfaces REQUIRED) 14 | 15 | set(msg_files 16 | "msg/Test.msg" 17 | ) 18 | 19 | rosidl_generate_interfaces(${PROJECT_NAME} 20 | ${msg_files} 21 | DEPENDENCIES builtin_interfaces std_msgs 22 | ) 23 | ament_export_dependencies(rosidl_default_runtime) 24 | ament_export_dependencies(builtin_interfaces) 25 | ament_export_dependencies(std_msgs) 26 | 27 | include_directories( 28 | ${${PROJECT_NAME}_INCLUDE_DIRS} 29 | 30 | # ${std_msgs_INCLUDE_DIRS} 31 | ) 32 | 33 | if(BUILD_TESTING) 34 | find_package(ament_lint_auto REQUIRED) 35 | 36 | # the following line skips the linter which checks for copyrights 37 | # uncomment the line when a copyright and license is not present in all source files 38 | # set(ament_cmake_copyright_FOUND TRUE) 39 | # the following line skips cpplint (only works in a git repo) 40 | # uncomment the line when this package is not in a git repo 41 | # set(ament_cmake_cpplint_FOUND TRUE) 42 | ament_lint_auto_find_test_dependencies() 43 | endif() 44 | 45 | ament_package() 46 | -------------------------------------------------------------------------------- /src/test_msg/msg/Test.msg: -------------------------------------------------------------------------------- 1 | int64 num -------------------------------------------------------------------------------- /src/test_msg/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test_msg 5 | 0.0.0 6 | TODO: Package description 7 | Timothy Dodge 8 | Apache License 2.0 9 | 10 | ament_cmake 11 | rosidl_default_generators 12 | 13 | rosidl_default_generators 14 | 15 | rosidl_default_runtime 16 | 17 | rosidl_interface_packages 18 | 19 | 20 | builtin_interfaces 21 | std_msgs 22 | 23 | ament_lint_auto 24 | ament_lint_common 25 | 26 | 27 | ament_cmake 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/test_plot/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(test_plot) 3 | 4 | # Default to C99 5 | if(NOT CMAKE_C_STANDARD) 6 | set(CMAKE_C_STANDARD 99) 7 | endif() 8 | 9 | # Default to C++14 10 | if(NOT CMAKE_CXX_STANDARD) 11 | set(CMAKE_CXX_STANDARD 14) 12 | endif() 13 | 14 | if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 15 | add_compile_options(-Wall -Wextra -Wpedantic) 16 | endif() 17 | 18 | add_compile_options(-std=c++14) 19 | 20 | # add_compile_options(-g) 21 | add_compile_options(-march=native -O3 -DNBUG -omit-frame-pointer) 22 | 23 | # # Find catkin macros and libraries 24 | # # if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 25 | # # is used, also find other catkin packages 26 | find_package(ament_cmake REQUIRED) 27 | find_package(ament_cmake_python REQUIRED) 28 | find_package(rclpy REQUIRED) 29 | find_package(std_msgs REQUIRED) 30 | find_package(common_interfaces REQUIRED) 31 | find_package(test_msg REQUIRED) 32 | 33 | # find_package(Boost REQUIRED COMPONENTS system) 34 | # include_directories( 35 | # include 36 | # ${YAML_CPP_INCLUDE_DIRS} 37 | # ) 38 | 39 | # install(TARGETS 40 | # threshold_operator_planner 41 | # DESTINATION lib/${PROJECT_NAME}) 42 | 43 | # ament_python_install_package(${PROJECT_NAME}) 44 | # ament_python_install_package(UTA_Bus_Tracking) 45 | install(PROGRAMS 46 | scripts/test_plot.py 47 | scripts/test_plot1.py 48 | scripts/test_pub.py 49 | DESTINATION lib/${PROJECT_NAME}) 50 | 51 | install(DIRECTORY 52 | launch 53 | DESTINATION share/${PROJECT_NAME}/ 54 | ) 55 | 56 | ament_export_include_directories(include) 57 | ament_export_dependencies(std_msgs) 58 | 59 | if(BUILD_TESTING) 60 | find_package(ament_lint_auto REQUIRED) 61 | 62 | # # the following line skips the linter which checks for copyrights 63 | # # uncomment the line when a copyright and license is not present in all source files 64 | # # set(ament_cmake_copyright_FOUND TRUE) 65 | # # the following line skips cpplint (only works in a git repo) 66 | # # uncomment the line when this package is not in a git repo 67 | # # set(ament_cmake_cpplint_FOUND TRUE) 68 | ament_lint_auto_find_test_dependencies() 69 | endif() 70 | 71 | ament_package() 72 | -------------------------------------------------------------------------------- /src/test_plot/launch/test.launch.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import launch.actions as actions 4 | from ament_index_python.packages import get_package_share_directory 5 | from launch import LaunchDescription 6 | from launch_ros.actions import Node 7 | 8 | 9 | def generate_launch_description(): 10 | test_plotter = Node( 11 | package="test_plot", 12 | executable="test_plot1.py", 13 | name="test_plotter", 14 | output="screen", 15 | emulate_tty=True, 16 | ) 17 | test_pub = Node( 18 | package="test_plot", 19 | executable="test_pub.py", 20 | name="test_pub", 21 | output="screen", 22 | emulate_tty=True, 23 | ) 24 | 25 | ld = LaunchDescription() 26 | ld.add_action(test_pub) 27 | ld.add_action(test_plotter) 28 | return ld 29 | -------------------------------------------------------------------------------- /src/test_plot/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | test_plot 5 | 0.0.0 6 | TODO: Package description 7 | Timothy Dodge 8 | Apache License 2.0 9 | 10 | ament_cmake 11 | rclpy 12 | test_msg 13 | 14 | ament_lint_auto 15 | ament_lint_common 16 | rclpy 17 | 18 | 19 | 20 | ament_cmake 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/test_plot/scripts/test_plot.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import threading 4 | import typing 5 | 6 | import matplotlib.pyplot as plt 7 | import matplotlib.animation as anim 8 | import numpy as np 9 | import numpy.typing as npt 10 | import rclpy 11 | from test_msg.msg import Test 12 | from rclpy.subscription import Subscription 13 | from rclpy.node import Node 14 | 15 | 16 | class Example_Node(Node): 17 | """Example Node for showing how to use matplotlib within ros 2 node 18 | 19 | Attributes: 20 | fig: Figure object for matplotlib 21 | ax: Axes object for matplotlib 22 | x: x values for matplotlib 23 | y: y values for matplotlib 24 | lock: lock for threading 25 | _sub: Subscriber for node 26 | """ 27 | 28 | def __init__(self): 29 | """Initialize.""" 30 | super().__init__("example_node") 31 | # Initialize figure and axes and save to class 32 | self.fig, self.ax = plt.subplots() 33 | # create Thread lock to prevent multiaccess threading errors 34 | self._lock = threading.Lock() 35 | # create initial values to plot 36 | self.x = [i for i in range(5)] 37 | self.width = [0.5 for i in range(5)] 38 | # create subscriber 39 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup() 40 | self._sub: Subscription = self.create_subscription( 41 | Test, "test", self._callback, 10, callback_group=self.cbg 42 | ) 43 | 44 | def _callback(self, msg: Test): 45 | """Callback for subscriber 46 | 47 | Args: 48 | msg: message from subscriber 49 | Message format 50 | ---------------- 51 | int32 num 52 | """ 53 | # lock thread 54 | with self._lock: 55 | # update values 56 | number: int = msg.num 57 | self.x.append(number) 58 | self.width.append(0.5) 59 | # update counter 60 | 61 | def plt_func(self, _): 62 | """Function for for adding data to axis. 63 | 64 | Args: 65 | _ : Dummy variable that is required for matplotlib animation. 66 | 67 | Returns: 68 | Axes object for matplotlib 69 | """ 70 | # lock thread 71 | with self._lock: 72 | x = np.array(self.x) 73 | y = np.array(self.width) 74 | self.ax.barh(y, 0.5, left=x, color="red") 75 | 76 | return self.ax 77 | 78 | def _plt(self): 79 | """Function for initializing and showing matplotlib animation.""" 80 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000) 81 | plt.show() 82 | 83 | 84 | def main(args=None): 85 | rclpy.init(args=args) 86 | node = Example_Node() 87 | executor = rclpy.executors.MultiThreadedExecutor() 88 | executor.add_node(node) 89 | thread = threading.Thread(target=executor.spin, daemon=True) 90 | thread.start() 91 | node._plt() 92 | 93 | 94 | if __name__ == "__main__": 95 | main() 96 | -------------------------------------------------------------------------------- /src/test_plot/scripts/test_plot1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import threading 4 | import typing 5 | 6 | import matplotlib.pyplot as plt 7 | import matplotlib.animation as anim 8 | import numpy as np 9 | import numpy.typing as npt 10 | import rclpy 11 | from test_msg.msg import Test 12 | from rclpy.subscription import Subscription 13 | from rclpy.node import Node 14 | 15 | 16 | class Example_Node(Node): 17 | """Example Node for showing how to use matplotlib within ros 2 node 18 | 19 | Attributes: 20 | fig: Figure object for matplotlib 21 | ax: Axes object for matplotlib 22 | x: x values for matplotlib 23 | y: y values for matplotlib 24 | lock: lock for threading 25 | _sub: Subscriber for node 26 | """ 27 | 28 | def __init__(self): 29 | """Initialize.""" 30 | super().__init__("example_node") 31 | # Initialize figure and axes and save to class 32 | self.fig, self.ax = plt.subplots() 33 | # create Thread lock to prevent multiaccess threading errors 34 | self._lock = threading.Lock() 35 | # create initial values to plot 36 | self.x = [i for i in range(5)] 37 | self.current_x = 5 38 | self.width = [0.5 for i in range(5)] 39 | # create subscriber 40 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup() 41 | self._sub: Subscription = self.create_subscription( 42 | Test, "test", self._callback, 10, callback_group=self.cbg 43 | ) 44 | 45 | def _callback(self, msg: Test): 46 | """Callback for subscriber 47 | 48 | Args: 49 | msg: message from subscriber 50 | Message format 51 | ---------------- 52 | int32 num 53 | """ 54 | # lock thread 55 | with self._lock: 56 | # update values 57 | number: int = msg.num 58 | self.x.append(self.current_x) 59 | self.width.append(number) 60 | self.current_x += 1 61 | # update counter 62 | 63 | def plt_func(self, _): 64 | """Function for for adding data to axis. 65 | 66 | Args: 67 | _ : Dummy variable that is required for matplotlib animation. 68 | 69 | Returns: 70 | Axes object for matplotlib 71 | """ 72 | # lock thread 73 | with self._lock: 74 | x = np.array(self.x) 75 | y = np.array(self.width) 76 | self.ax.plot(x, y, color="red") 77 | 78 | return self.ax 79 | 80 | def _plt(self): 81 | """Function for initializing and showing matplotlib animation.""" 82 | self.ani = anim.FuncAnimation(self.fig, self.plt_func, interval=1000) 83 | plt.show() 84 | 85 | 86 | def main(args=None): 87 | rclpy.init(args=args) 88 | node = Example_Node() 89 | executor = rclpy.executors.MultiThreadedExecutor() 90 | executor.add_node(node) 91 | thread = threading.Thread(target=executor.spin, daemon=True) 92 | thread.start() 93 | node._plt() 94 | 95 | 96 | if __name__ == "__main__": 97 | main() 98 | -------------------------------------------------------------------------------- /src/test_plot/scripts/test_pub.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import time 3 | 4 | import rclpy 5 | import rclpy.callback_groups 6 | from test_msg.msg import Test 7 | from rclpy.client import Client 8 | from rclpy.node import Node 9 | import numpy as np 10 | 11 | 12 | class TestPub(Node): 13 | def __init__(self) -> None: 14 | super().__init__("test_pub") 15 | self.cbg = rclpy.callback_groups.MutuallyExclusiveCallbackGroup() 16 | self.pub = self.create_publisher(Test, "test", 10, callback_group=self.cbg) 17 | self.counter = 4 18 | time.sleep(5) 19 | self.timer = self.create_timer(1, self.loop_pub) 20 | 21 | def loop_pub(self) -> None: 22 | self.counter += 1 23 | msg = Test() 24 | msg.num = np.random.randint(0, 30) 25 | self.pub.publish(msg) 26 | 27 | 28 | def main(args=None): 29 | rclpy.init(args=args) 30 | node = TestPub() 31 | rclpy.spin(node) 32 | rclpy.shutdown() 33 | 34 | 35 | if __name__ == "__main__": 36 | main() 37 | --------------------------------------------------------------------------------