Create a Package
Before creating a package, make sure to have a clear understanding of what the package is for, and what it's not for. A package tends to deal with a bunch of related issues that can be intuitively/logically grouped together. Addressing too diverse issues in the package defeats the purpose of the package.
To create a package using/for the MC-ONE stack, it is advised to first create a fresh github repo. The name is not really important, but if this is to be an official MC-ONE extension, consider starting the repo and package name with mc_. The package is essentially a ROS2 package, so detailed instructions can be found here: https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html#create-a-package
Describing the Package
If other developers and engineers use the package, it is important to clearly describe it in the README.md file. This description should contain a detailed description of what the package is for, the nodes it contains, as well as the data files. It should explain dependencies on other packages, and what ROS topics, ROS services and ROS actions are used/described. Explain any special initialization needed, special files/models to download, and in case it controls hardware, any firmware that needs to be flashed. Also describe any new ROS or JSON message formats. It is also advised to describe example usage of the nodes.
Docker
Each package tends to also be a Docker container, described in Dockerfile. In general, the Dockerfile describes the dependencies, downloads external resources/DL models, and calls colcon build to build the package. To keep the container name and other build parameters stable, it is advised to add a build.sh script which builds the Docker container. This typically is a one-liner, like so:
docker build -f Dockerfile -t mindchildren/my_package --network=host .
Where my_package is the package name.
To capture the runtime parameters, volume mappings, ports, etc. It is also advised to add run_*.sh scripts to run the individual nodes, like so:
docker run --rm -it -v "$(pwd)":/context -v /dev:/dev --network host --privileged mindchildren/my_package:latest ros2 run my_package my_node
Where my_package is the package name and my_node is the node name.
Python-only nodes
The main directory of the repo is the ROS2 package directory containing the package.xml, setup.cfg, setup.py, etc. files. Nodes appear in a subdirectory with the same name as the package (if your package is called my_package, the nodes can be found in my_package/my_package) This directory is a Python package, so it should also contain the empty __init__.py file.
Python and C++ nodes
It is also possible to combine Python nodes with C++ (or Rust) nodes. To do this, follow the CMake-based instructions of the ROS2 documentation. Generally, for CMake-based packages, the package.xml file depends on ament_cmake. If the package should also host Python nodes, add a ament_cmake_python dependency as well.
Rust nodes
As it is not currently officially supported by ROS2, building a Rust node can be a bit involved. Here is a guide.
First, describe the Rust executables as you would for any Rust project in Cargo.toml. Make sure to include r2r as a dependency. The most recent version of this project can be found at https://github.com/des256/r2r, so add the following dependency to Cargo.toml:
r2r = { git = "https://github.com/des256/r2r" }
Then, in CMakeLists.txt add build instructions for the Rust nodes, like so:
include(r2r_cargo.cmake)
r2r_cargo(std_msgs sensor_msgs rcl rcl_action)
And add the install directive for each node:
install(PROGRAMS ${CMAKE_SOURCE_DIR}/target/colcon/my_node DESTINATION lib/${PROJECT_NAME})
Where my_node is the node name.