diff --git a/README.md b/README.md
index 1d696cf..45d0b82 100644
--- a/README.md
+++ b/README.md
@@ -357,6 +357,9 @@ colcon test
# Run specific package tests
colcon test --packages-select agent composer core
+
+#Running a specific test method
+python3 -m pytest src/composer/test/test_muto_composer.py::TestMutoComposer::test_parse_payload_archive_format -v
```
### Contributing
diff --git a/docs/samples/april-tag-robot/README.md b/docs/samples/april-tag-robot/README.md
new file mode 100644
index 0000000..d7f1474
--- /dev/null
+++ b/docs/samples/april-tag-robot/README.md
@@ -0,0 +1,333 @@
+# AprilTag Detection Sample
+
+
+
+Real-time AprilTag detection and visualization pipeline demonstrating computer vision workflows with Eclipse Muto orchestration and Eclipse Symphony fleet management.
+
+## Overview
+
+This sample implements a complete AprilTag detection system using:
+- **Camera input**: Live camera feed or recorded bag files
+- **Detection**: Fast AprilTag family detection (tag36h11, tagStandard41h12, etc.)
+- **Visualization**: Optional detection overlay and tracking display
+- **Orchestration**: Muto-managed composable node containers
+- **Fleet Management**: Symphony-based remote deployment and updates
+
+### Key Features
+
+- **Lightweight perception**: Focus on detection without full 6-DoF pose estimation
+- **Composable architecture**: Efficient memory sharing between vision nodes
+- **Multi-family support**: Configurable tag families and detection parameters
+- **Remote deployment**: Over-the-air updates via Symphony orchestration
+- **Telemetry ready**: Detection metrics and performance monitoring
+
+> **Note**: This sample uses [apriltag_detector](https://github.com/ros-misc-utilities/apriltag_detector) for pure detection. For combined detection + pose estimation, consider [apriltag_ros](https://github.com/christianrauch/apriltag_ros).
+
+## Artifact Map
+
+Directory: [`samples/april-tag-robot/`](./)
+
+| File | Purpose |
+|------|---------|
+| `apriltag-detector-muto-stack.json` | Declarative Muto stack (JSON) for detector + draw nodes (composable container) |
+| `apriltag-tracking-solution.json` | Symphony Solution (wraps stack as base64 payload) |
+| `apriltag-tracking-instance.json` | Symphony Instance binding Solution → Target |
+| `apriltag_workspace/stack.json` | Alternative workspace/stack form (if using archive packaging) |
+| `apriltag_workspace/launch/apriltag.launch.yaml` | Upstream style launch YAML (reference only) |
+| `apriltag_workspace/config/tags_36h11.yaml` | Tag family parameter file |
+
+Target definition is auto‑registered by the running Muto Agent (see talker–listener sample). For a static reference, review [`../talker-listener/test-robot-debug-target.json`](../talker-listener/test-robot-debug-target.json).
+
+## Scenario Overview
+
+You operate a fleet of robots each with a monocular camera. You need to:
+1. Deploy an initial AprilTag detection capability (v1).
+2. Safely roll out an improved pipeline (v2) that supports additional tag families and optional tracking/visualization.
+3. Use canary rollout + rollback policies to limit risk.
+
+The complete deep‑dive OTA narrative (architecture + sequence diagrams) lives in `ros_variant/muto.md` (search for "AprilTag" section). This README keeps a concise, actionable version focused on running the sample.
+
+### Version Delta (Conceptual)
+| Aspect | v1 (baseline) | v2 (enhanced) |
+|--------|---------------|---------------|
+| Families | `tag36h11` | `tag36h11`, `tagStandard41h12` |
+| Nodes | detector, draw | detector, draw, (optional future tracker) |
+| Telemetry | raw detections | detections + extended metrics |
+| Policy | none | optional: only when docked |
+
+Refer to the architecture + sequence diagrams in `muto.md` for full context. Keeping this README lean.
+
+## Prerequisites
+
+- **Camera hardware**: USB camera, Intel RealSense, or other ROS 2 compatible camera
+- **AprilTag packages**: Install detection dependencies
+ ```bash
+ sudo apt install ros-$ROS_DISTRO-apriltag ros-$ROS_DISTRO-apriltag-msgs
+ # Or build from source: https://github.com/ros-misc-utilities/apriltag_detector
+ ```
+- **Eclipse Muto + Symphony**: Complete setup from [Quick Start Guide](../../muto-quickstart.md)
+
+## Manual Testing (Native ROS 2)
+
+Before orchestrated deployment, verify the detection pipeline:
+
+```bash
+# Terminal 1: Start camera (adjust topic as needed)
+ros2 run usb_cam usb_cam_node_exe
+
+# Terminal 2: Start AprilTag detector
+ros2 run apriltag_detector apriltag_detector_node --ros-args \
+ -r /image:=/image_raw \
+ -p tag_family:=tag36h11
+
+# Terminal 3: View detections
+ros2 topic echo /apriltag/detections
+```
+
+## Deploy the Sample
+
+### Step 1: Start Symphony + Muto
+Follow the [Quick Start Guide](../../muto-quickstart.md) to get Muto + Symphony running. Ensure a target like `test-robot-debug` appears in Symphony:
+
+```bash
+# Verify Symphony API is running
+curl http://localhost:8082/v1alpha2/greetings
+
+# Check if Muto agent registered the target
+curl -s http://localhost:8082/v1alpha2/targets | jq '.items[].metadata.name'
+```
+
+### Step 2: Review Stack Configuration
+The AprilTag stack definition [`apriltag-detector-muto-stack.json`](./apriltag-detector-muto-stack.json) defines a composable container with two nodes:
+
+- **apriltag_detector_node**: Detects AprilTags in camera images
+- **apriltag_draw_node**: Draws detection overlays on images
+
+Key configuration areas:
+```json
+{
+ "composable": [
+ {
+ "container_name": "apriltag_container",
+ "node": [
+ {
+ "package": "apriltag_detector",
+ "executable": "apriltag_detector_node",
+ "parameters": [
+ {"tag_family": "tag36h11"},
+ {"tag_edge_size": 0.05}
+ ],
+ "remappings": [
+ {"from": "/image", "to": "/camera/image_raw"}
+ ]
+ }
+ ]
+ }
+ ]
+}
+```
+
+Edit parameters as needed:
+- `tag_family`: Supported families include `tag36h11`, `tagStandard41h12`
+- `tag_edge_size`: Physical tag size in meters
+- Image topic remapping to match your camera driver
+
+### Step 3: Deploy Solution
+Navigate to the samples directory and create the Solution:
+
+```bash
+cd samples/
+./define-solution.sh april-tag-robot/apriltag-tracking-solution.json
+```
+
+The Solution wraps the stack JSON as a base64-encoded payload. If you modified the stack definition, regenerate the encoding:
+
+```bash
+cd april-tag-robot/
+STACK=apriltag-detector-muto-stack.json
+BASE64=$(cat "$STACK" | base64 -w0)
+jq --arg d "$BASE64" '.spec.components[0].properties.data=$d' apriltag-tracking-solution.json > /tmp/new-solution.json
+mv /tmp/new-solution.json apriltag-tracking-solution.json
+```
+
+### Step 4: Deploy Instance
+Create the Instance to bind the Solution to your target:
+
+```bash
+./define-instance.sh april-tag-robot/apriltag-tracking-instance.json
+```
+
+### Step 5: Verify Deployment
+**ROS 2 Environment** (in container or host with ROS setup):
+```bash
+# Check AprilTag topics are available
+ros2 topic list | grep apriltag
+
+# Verify detection output
+ros2 topic echo /apriltag/detections --once
+
+# Monitor container status
+ros2 node list | grep apriltag
+```
+
+**Symphony Environment**:
+```bash
+# Check instance status
+curl -s http://localhost:8082/v1alpha2/instances | \
+ jq '.items[] | select(.metadata.name|test("apriltag")) | {name: .metadata.name, phase: .status.phase}'
+
+# View deployment details
+curl -s http://localhost:8082/v1alpha2/instances/apriltag-tracking-instance | jq '.status'
+```
+
+### Step 6: Update and Rollout (Optional)
+To demonstrate OTA updates, create a modified stack (e.g., add a second tag family):
+
+1. Edit `apriltag-detector-muto-stack.json` to add another detector node
+2. Create a new Solution version (`apriltag-tracking-solution-v2.json`)
+3. Submit as a canary update:
+
+```bash
+# Create solution v2
+./define-solution.sh april-tag-robot/apriltag-tracking-solution-v2.json
+
+# Deploy to specific target (canary)
+./define-instance.sh april-tag-robot/apriltag-tracking-instance-v2.json
+```
+
+## Monitoring and Telemetry
+
+### Key Topics
+Monitor these ROS 2 topics to verify AprilTag detection:
+
+| Topic | Message Type | Purpose |
+|-------|--------------|---------|
+| `/apriltag/detections` | `apriltag_msgs/AprilTagDetectionArray` | Core detection results with tag IDs and poses |
+| `/apriltag/images/debug` | `sensor_msgs/Image` | Visualization with detection overlays (if enabled) |
+| `/camera/image_raw` | `sensor_msgs/Image` | Input camera stream |
+| `/apriltag/detections/compressed` | `CompressedImage` | Compressed debug visualization |
+
+### Sample Detection Output
+```bash
+ros2 topic echo /apriltag/detections --once
+```
+Expected structure:
+```yaml
+detections:
+- id: 1
+ family: tag36h11
+ center: {x: 320.5, y: 240.2}
+ corners: [{x: 315.0, y: 235.0}, {x: 326.0, y: 235.0}, ...]
+ pose:
+ position: {x: 0.12, y: -0.05, z: 0.8}
+ orientation: {x: 0.0, y: 0.0, z: 0.707, w: 0.707}
+```
+
+## Troubleshooting
+
+### Common Issues
+
+**No detections appearing**:
+```bash
+# Check if camera is publishing
+ros2 topic hz /camera/image_raw
+
+# Verify detector node is running
+ros2 node list | grep apriltag
+
+# Check parameter configuration
+ros2 param get /apriltag_container/apriltag_detector_node tag_family
+```
+
+**Deployment failing**:
+```bash
+# Check Symphony instance status
+curl -s http://localhost:8082/v1alpha2/instances/apriltag-tracking-instance | jq '.status.observedState'
+
+# View Muto agent logs
+ros2 topic echo /muto/logs --once
+
+# Check container health
+docker ps | grep apriltag # or podman ps
+```
+
+**Performance issues**:
+- Reduce image resolution in camera driver
+- Adjust `tag_decimate` parameter (trade accuracy for speed)
+- Verify adequate CPU resources for container
+
+### Validation Checklist
+
+| Step | Command | Expected Result |
+|------|---------|-----------------|
+| Symphony API available | `curl http://localhost:8082/v1alpha2/greetings` | "Hello from Symphony..." |
+| Target registered | `curl -s http://localhost:8082/v1alpha2/targets \| jq '.[].metadata.name'` | Shows target name |
+| Instance deployed | `curl -s http://localhost:8082/v1alpha2/instances \| grep apriltag` | Instance exists |
+| Camera publishing | `ros2 topic hz /camera/image_raw` | Shows frame rate |
+| Detector active | `ros2 node list \| grep apriltag` | Detector node listed |
+| Detections streaming | `ros2 topic echo /apriltag/detections --once` | Detection data appears |
+
+## Advanced Configuration
+
+### Update Policies
+Configure deployment policies in Symphony for production scenarios:
+
+```json
+{
+ "updatePolicy": {
+ "canary": {
+ "percentage": 10,
+ "conditions": ["battery_state=docked", "cpu_usage<80%"]
+ },
+ "rollback": {
+ "triggers": ["detection_fps<5", "error_rate>0.1"]
+ }
+ }
+}
+```
+
+### Multi-Camera Setup
+For robots with multiple cameras, create separate detector instances:
+
+```json
+{
+ "node": [
+ {
+ "package": "apriltag_detector",
+ "executable": "apriltag_detector_node",
+ "name": "front_camera_detector",
+ "remappings": [{"from": "/image", "to": "/front_camera/image_raw"}]
+ },
+ {
+ "package": "apriltag_detector",
+ "executable": "apriltag_detector_node",
+ "name": "rear_camera_detector",
+ "remappings": [{"from": "/image", "to": "/rear_camera/image_raw"}]
+ }
+ ]
+}
+```
+
+## Next Steps
+
+### Extensions
+- **Depth Integration**: Add pose refinement using depth cameras
+- **Tracking**: Implement persistent tag tracking across frames
+- **Fleet Coordination**: Share tag detections between robots via DDS
+- **Performance**: GPU-accelerated detection for real-time applications
+
+### Production Considerations
+- Configure signed container images for security compliance
+- Implement proper tag size calibration procedures
+- Add detection confidence thresholds and filtering
+- Set up centralized logging and metrics collection
+
+## References
+- [AprilTag Detector ROS 2 Package](https://github.com/ros-misc-utilities/apriltag_detector)
+- [AprilTag Library Documentation](https://april.eecs.umich.edu/software/apriltag)
+- [Symphony OTA Updates Guide](https://github.com/eclipse-symphony/symphony/blob/main/docs/README.md)
+- [Muto Architecture Documentation](../../README.md)
+
+---
+This sample demonstrates basic AprilTag detection in a fleet management context. Build upon this foundation with tracking, multi-sensor fusion, and advanced deployment policies as your use case evolves.
+
diff --git a/docs/samples/muto/sample-composable.json b/docs/samples/muto/sample-composable.json
new file mode 100644
index 0000000..bb28e2b
--- /dev/null
+++ b/docs/samples/muto/sample-composable.json
@@ -0,0 +1,26 @@
+{
+ "name": "Muto Simple Composable Client-Server Stack",
+ "context": "eteration_office",
+ "stackId": "org.eclipse.muto.sandbox:composable_client_server",
+ "composable": [
+ {
+ "name": "muto_demo_container",
+ "namespace": "",
+ "package": "rclcpp_components",
+ "executable": "component_container",
+ "node": [
+ {
+ "pkg": "composition",
+ "plugin": "composition::Server",
+ "name": "server"
+ },
+ {
+ "pkg": "composition",
+ "plugin": "composition::Client",
+ "name": "client"
+ }
+ ]
+ }
+ ],
+ "node": []
+}
\ No newline at end of file
diff --git a/docs/samples/muto/sample-muto-stack.json b/docs/samples/muto/sample-muto-stack.json
new file mode 100644
index 0000000..7f68cad
--- /dev/null
+++ b/docs/samples/muto/sample-muto-stack.json
@@ -0,0 +1,91 @@
+{
+ "name": "F1tenth Multiagent Gym",
+ "context": "eteration_office",
+ "stackId": "org.eclipse.muto.sandbox:f1tenth-multiagent-gym.launch",
+ "stack": [
+ {
+ "thingId": "org.eclipse.muto.sandbox:racecar1.launch"
+ },
+ {
+ "thingId": "org.eclipse.muto.sandbox:racecar2.launch"
+ },
+ {
+ "thingId": "org.eclipse.muto.sandbox:racecar3.launch"
+ }
+ ],
+ "arg": [
+ {
+ "name": "map",
+ "value": "$(find f1tenth_gym_ros)/maps/Spielberg_map.yaml"
+ }
+ ],
+ "param": [],
+ "node": [
+ {
+ "name": "map_server",
+ "pkg": "nav2_map_server",
+ "exec": "map_server",
+ "param": [
+ {
+ "name": "yaml_filename",
+ "value": "$(arg map)"
+ },
+ {
+ "name": "topic",
+ "value": "map"
+ },
+ {
+ "name": "frame_id",
+ "value": "map"
+ },
+ {
+ "name": "output",
+ "value": "screen"
+ },
+ {
+ "name": "use_sim_time",
+ "value": "True"
+ }
+ ]
+ },
+ {
+ "name": "gym_bridge",
+ "pkg": "f1tenth_gym_ros",
+ "exec": "gym_bridge",
+ "param": [
+ {
+ "from": "$(find f1tenth_gym_ros)/config/sim.yaml"
+ }
+ ]
+ },
+ {
+ "pkg": "rviz2",
+ "name": "rviz",
+ "exec": "rviz2",
+ "args": "-d $(find f1tenth_gym_ros)/launch/gym_bridge.rviz",
+ "output": "screen"
+ },
+ {
+ "pkg": "nav2_lifecycle_manager",
+ "exec": "lifecycle_manager",
+ "name": "lifecycle_manager_localization",
+ "output": "screen",
+ "param": [
+ {
+ "name": "use_sim_time",
+ "value": "True"
+ },
+ {
+ "name": "autostart",
+ "value": "True"
+ },
+ {
+ "name": "node_names",
+ "value": [
+ "map_server"
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/samples/symphony/define-instance.sh b/docs/samples/symphony/define-instance.sh
index 4d216fc..a7adc6c 100755
--- a/docs/samples/symphony/define-instance.sh
+++ b/docs/samples/symphony/define-instance.sh
@@ -1,5 +1,25 @@
#!/bin/bash
+
+# Check if JSON file argument is provided
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 "
+ echo "Example: $0 apriltag-tracking-instance.json"
+ exit 1
+fi
+
+JSON_FILE="$1"
+
+# Check if file exists
+if [ ! -f "$JSON_FILE" ]; then
+ echo "Error: File '$JSON_FILE' not found!"
+ exit 1
+fi
+
+# Extract solution name from filename (remove path and .json extension)
+ROOT_NAME=$(basename "$JSON_FILE" .json)
+INSTANCE_NAME="${ROOT_NAME}"
+
export SYMPHONY_API_URL=http://localhost:8082/v1alpha2/
TOKEN=$(curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":""}' "${SYMPHONY_API_URL}users/auth" | jq -r '.accessToken')
@@ -7,11 +27,26 @@ TOKEN=$(curl -X POST -H "Content-Type: application/json" -d '{"username":"admin"
# Prompt user to press Enter to continue after the target has been registered
-curl -v -s -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" "${SYMPHONY_API_URL}instances"
+curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" "${SYMPHONY_API_URL}instances"
-# Read the content of solution.json file and send it as data in the POST request
+# Read & mutate JSON: overwrite metadata.name with INSTANCE_NAME using jq
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-SOLUTION_DATA=$(cat "$SCRIPT_DIR/instance.json")
-
-curl -v -s -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d "$SOLUTION_DATA" "${SYMPHONY_API_URL}instances/test-robot-debug-instance"
-
+if ! command -v jq >/dev/null 2>&1; then
+ echo "Error: jq is required but not installed." >&2
+ exit 2
+fi
+
+# Use --arg to safely inject shell variable
+SOLUTION_DATA=$(jq --arg name "$INSTANCE_NAME" '(.metadata //= {}) | .metadata.name = $name' "$JSON_FILE")
+
+
+HTTP_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $TOKEN" \
+ -d "$SOLUTION_DATA" \
+ "${SYMPHONY_API_URL}instances/${INSTANCE_NAME}")
+if [ "$HTTP_RESPONSE" -eq 200 ] || [ "$HTTP_RESPONSE" -eq 204 ]; then
+ echo "Instance '${INSTANCE_NAME}' created successfully."
+else
+ echo "Failed to create instance '${INSTANCE_NAME}'. HTTP status: $HTTP_RESPONSE"
+fi
diff --git a/docs/samples/symphony/define-solution.sh b/docs/samples/symphony/define-solution.sh
index 4f69936..0543bd1 100755
--- a/docs/samples/symphony/define-solution.sh
+++ b/docs/samples/symphony/define-solution.sh
@@ -58,9 +58,9 @@ encode_base64() {
base64 -i "$file" 2>/dev/null || base64 < "$file" 2>/dev/null
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ -n "$WSL_DISTRO_NAME" ]] || [[ -n "$IS_WSL" ]] || [[ "$(uname -r)" == *Microsoft* ]] || [[ "$(uname -r)" == *microsoft* ]]; then
# WSL/Windows environments - try different approaches
- if base64 -w 0 "$file" 2>/dev/null; then
+ if command -v base64 >/dev/null 2>&1 && base64 -w 0 "$file" >/dev/null 2>&1; then
base64 -w 0 "$file"
- elif base64 < "$file" 2>/dev/null; then
+ elif command -v base64 >/dev/null 2>&1 && base64 < "$file" >/dev/null 2>&1; then
base64 < "$file" | tr -d '\n'
else
# Fallback for older WSL versions
@@ -68,7 +68,7 @@ encode_base64() {
fi
else
# Linux/Unix (GNU base64)
- if base64 -w 0 "$file" 2>/dev/null; then
+ if command -v base64 >/dev/null 2>&1 && base64 -w 0 "$file" >/dev/null 2>&1; then
base64 -w 0 "$file"
else
# Fallback for systems without -w option
@@ -80,7 +80,6 @@ encode_base64() {
# Base64 encode the contents of the JSON file
echo "Encoding stack data to base64..."
STACK_DATA_BASE64=$(encode_base64 "$JSON_FILE")
-
if [ -z "$STACK_DATA_BASE64" ]; then
echo "Error: Failed to base64 encode the JSON file"
exit 1
diff --git a/docs/samples/symphony/delete-instance.sh b/docs/samples/symphony/delete-instance.sh
new file mode 100755
index 0000000..de64b8b
--- /dev/null
+++ b/docs/samples/symphony/delete-instance.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+
+# Check if JSON file argument is provided
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 "
+ echo "Example: $0 apriltag-tracking-instance.json"
+ exit 1
+fi
+
+JSON_FILE="$1"
+
+# Check if file exists
+if [ ! -f "$JSON_FILE" ]; then
+ echo "Error: File '$JSON_FILE' not found!"
+ exit 1
+fi
+
+# Extract solution name from filename (remove path and .json extension)
+ROOT_NAME=$(basename "$JSON_FILE" .json)
+INSTANCE_NAME="${ROOT_NAME}"
+
+export SYMPHONY_API_URL=http://localhost:8082/v1alpha2/
+
+TOKEN=$(curl -X POST -H "Content-Type: application/json" -d '{"username":"admin","password":""}' "${SYMPHONY_API_URL}users/auth" | jq -r '.accessToken')
+
+
+# Prompt user to press Enter to continue after the target has been registered
+
+curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" "${SYMPHONY_API_URL}instances"
+
+# Read & mutate JSON: overwrite metadata.name with INSTANCE_NAME using jq
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+if ! command -v jq >/dev/null 2>&1; then
+ echo "Error: jq is required but not installed." >&2
+ exit 2
+fi
+
+# Use --arg to safely inject shell variable
+SOLUTION_DATA=$(jq --arg name "$INSTANCE_NAME" '(.metadata //= {}) | .metadata.name = $name' "$JSON_FILE")
+
+# DELETE THE INSTANCE AND SHOW THE RESULT OF THE DELETE as SUCCESSFUL OR NOT
+
+echo ""
+echo "---"
+echo "Deleting instance with metadata.name=$INSTANCE_NAME"
+HTTP_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $TOKEN" \
+ "${SYMPHONY_API_URL}instances/${INSTANCE_NAME}")
+
+if [ "$HTTP_RESPONSE" -eq 200 ] || [ "$HTTP_RESPONSE" -eq 204 ]; then
+ echo "Instance '${INSTANCE_NAME}' deleted successfully."
+else
+ echo "Failed to delete instance '${INSTANCE_NAME}'. HTTP status: $HTTP_RESPONSE"
+ exit 3
+fi
+
diff --git a/docs/samples/symphony/docker-compose.yaml b/docs/samples/symphony/docker-compose.yaml
new file mode 100644
index 0000000..965eea1
--- /dev/null
+++ b/docs/samples/symphony/docker-compose.yaml
@@ -0,0 +1,46 @@
+
+services:
+ symphony-api:
+ image: ghcr.io/eclipse-symphony/symphony-api:0.48-proxy.40
+ container_name: symphony-api
+ ports:
+ - "8082:8082"
+ environment:
+ - USE_SERVICE_ACCOUNT_TOKENS=false
+ - SYMPHONY_API_URL=http://localhost:8082/v1alpha2/
+ - CONFIG=/symphony-api-no-k8s.json
+ - LOG_LEVEL=Error
+ volumes:
+ - ./providers:/extensions
+ restart: unless-stopped
+ symphony-portal:
+ image: ghcr.io/eclipse-symphony/symphony-portal:0.48-proxy.40
+ container_name: symphony-portal
+ ports:
+ - "3000:3000"
+ environment:
+ - SYMPHONY_API=http://symphony-api:8082/v1alpha2/
+ - NEXTAUTH_SECRET=SymphonyKey
+ - NEXTAUTH_URL=http://localhost:3000
+ restart: unless-stopped
+ depends_on:
+ - symphony-api
+ mosquitto:
+ image: eclipse-mosquitto:2
+ container_name: mosquitto
+ ports:
+ - "1883:1883" # MQTT (TCP)
+ # - "9001:9001" # (optional) WebSocket listener, enable in config first
+ volumes:
+ - ./mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
+ - mosquitto-data:/mosquitto/data
+ - mosquitto-logs:/mosquitto/log
+ restart: unless-stopped
+ healthcheck:
+ test: ["CMD-SHELL", "mosquitto_sub -h localhost -p 1883 -t '$$SYS/#' -C 1 -W 3 >/dev/null 2>&1 || exit 1"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+volumes:
+ mosquitto-data:
+ mosquitto-logs:
\ No newline at end of file
diff --git a/docs/samples/symphony/mosquitto/mosquitto.conf b/docs/samples/symphony/mosquitto/mosquitto.conf
new file mode 100644
index 0000000..59f8023
--- /dev/null
+++ b/docs/samples/symphony/mosquitto/mosquitto.conf
@@ -0,0 +1,6 @@
+listener 1883 0.0.0.0
+allow_anonymous true
+
+# (optional) WebSocket listener if you also expose 9001:
+# listener 9001
+# protocol websockets
diff --git a/docs/samples/talker-listener/README.md b/docs/samples/talker-listener/README.md
new file mode 100644
index 0000000..02b997e
--- /dev/null
+++ b/docs/samples/talker-listener/README.md
@@ -0,0 +1,239 @@
+# Talker-Listener sample (Muto + Symphony)
+
+This folder contains a small ROS 2 talker/listener stack used as a working example for Eclipse Muto orchestration and Eclipse Symphony solution/instance management.
+
+This README describes:
+- How to start the local Muto system (native or container/dev workflow)
+- How to start Symphony services (docker compose)
+- How to register a solution and create an instance using the helper scripts
+- Two ways to represent a stack: plain JSON (`stack/json`) and a packaged archive (`stack/archive`)
+- How to create a new stack/archive with `create_archive.sh`
+
+## Prerequisites
+
+- ROS 2 (Humble or later) and `colcon` if running Muto natively
+- Docker and Docker Compose (or `docker compose`) for Symphony
+- `curl`, `jq`, and `base64` available for the helper scripts
+
+For a complete system setup and additional details see: `../../muto-quickstart.md` and the repository `README.md`.
+
+## Quick path — start Muto (locally)
+
+If you already have a built workspace and want to run Muto locally (native ROS 2):
+
+1. Build and source your workspace (example):
+
+ 1. Build
+
+ source /opt/ros/humble/setup.bash
+ colcon build --symlink-install
+
+ 2. Source the install overlay
+
+ source install/setup.bash
+
+2. Launch the main Muto system (example args used in docs and tests):
+
+ ```bash
+ ros2 launch launch/muto.launch.py \
+ vehicle_namespace:=org.eclipse.muto.test \
+ vehicle_name:=test-robot-debug \
+ enable_symphony:=true \
+ log_level:=INFO
+ ```
+
+This will start agent/composer/plugins and — when enabled — the Symphony provider so the device can talk to the Symphony control plane.
+
+Notes
+- If you prefer running inside the development container described in `docs/development-container.md`, follow that doc to enter the container and run the same `ros2 launch` command after building.
+- Verify nodes with `ros2 node list | grep muto` and topics with `ros2 topic list` / `ros2 topic echo /chatter`.
+
+## Start Symphony (docker-compose)
+
+Symphony (API + Portal + Mosquitto) can be started from `docs/samples/symphony` using Docker Compose:
+
+1. From repository root:
+
+ ```bash
+ cd docs/samples/symphony
+ docker compose up -d
+ ```
+
+2. Verify services:
+
+ ```bash
+ # Symphony API
+ curl http://localhost:8082/v1alpha2/greetings
+
+ # Symphony Portal GUI
+ open http://localhost:3000 # or visit in your browser
+
+ # MQTT broker
+ # mosquitto: tcp://localhost:1883
+ ```
+
+If you need to stop the services:
+
+ ```bash
+ docker compose down
+ ```
+
+Notes
+- The `docker-compose.yaml` in `docs/samples/symphony` includes a ready-to-run Symphony API, Symphony Portal, and an Eclipse Mosquitto broker.
+
+## Automated demo with run-demo.sh
+
+For a streamlined end-to-end experience, use the `run-demo.sh` script in this folder. It automates the process of verifying Symphony availability, waiting for Muto nodes to be ready, defining the archive-based solution, and creating an instance.
+
+### Prerequisites for run-demo.sh
+
+- Symphony services must already be running (see "Start Symphony (docker-compose)" section).
+- Muto must be launched (see "Quick path — start Muto (locally)" section).
+- Required tools: `curl`, `jq`, `base64`, `ros2`.
+
+### How to run run-demo.sh
+
+From the repository root:
+
+```bash
+cd docs/samples/talker-listener
+./run-demo.sh
+```
+
+### What run-demo.sh does
+
+1. **Checks prerequisites**: Verifies that required commands (`curl`, `jq`, `base64`, `ros2`) are available.
+2. **Waits for Symphony API**: Polls `http://localhost:8082/v1alpha2/greetings` until the API responds (up to 60 seconds).
+3. **Waits for Muto nodes**: Checks `ros2 node list` for the presence of required Muto nodes (up to 60 seconds):
+ - `/muto/agent`
+ - `/muto/commands_plugin`
+ - `/muto/compose_plugin`
+ - `/muto/core_twin`
+ - `/muto/gateway`
+ - `/muto/launch_plugin`
+ - `/muto/muto_composer`
+ - `/muto/muto_symphony_provider`
+ - `/muto/provision_plugin`
+4. **Defines the solution**: Uses `define-solution.sh` to post `talker-listener-xarchive.json` to Symphony.
+5. **Creates the instance**: Uses `define-instance.sh` to create an instance with `talker-listener-xarchive-instance.json`.
+6. **Provides next steps**: Prints commands to check Symphony Portal/API, verify ROS topics, and clean up.
+
+If any step fails (e.g., Symphony not running, Muto nodes not appearing), the script exits with an error message.
+
+
+## Define a solution (stack) and create an instance
+
+The repo includes helper scripts in `docs/samples/symphony` that use the Symphony API to create solutions and instances:
+
+- `define-solution.sh` — posts a solution to Symphony. Usage: `./define-solution.sh `
+- `define-instance.sh` — creates an instance (binds a solution to a target). Usage: `./define-instance.sh `
+- `delete-instance.sh` — deletes an instance. Usage: `./delete-instance.sh `
+
+Examples (recommended sequence):
+
+1. Start Symphony (see previous section) and ensure it responds on `http://localhost:8082`.
+
+2. Create a solution describing the stack. From `docs/samples/symphony` run one of the options below.
+
+Option A: Use the plain JSON stack (stack/json)
+
+- The talker/listener JSON stack: `../talker-listener-json.json` (or `../talker-listener/talker-listener-json.json` depending on where you execute the script).
+- Example (run from `docs/samples/symphony`):
+
+ ```bash
+ ./define-solution.sh ../talker-listener/talker-listener-json.json
+ ```
+
+Option B: Use the packaged stack archive (stack/archive)
+
+- A stack archive JSON is a manifest that contains base64-encoded tar.gz data (content_type: `stack/archive`). Example file: `talker-listener-xarchive.json`.
+- Example (run from `docs/samples/symphony`):
+
+ ```bash
+ ./define-solution.sh ../talker-listener/talker-listener-xarchive.json
+ ```
+
+Both approaches create a Symphony solution that can later be instantiated against a target.
+
+3. Create an instance to bind the solution to a registered target (robot). The sample instance files are in this folder:
+
+- `talker-listener-json-instance.json` — uses the JSON stack solution
+- `talker-listener-xarchive-instance.json` — uses the archive-based solution
+
+Example (run from `docs/samples/symphony`):
+
+ ```bash
+ ./define-instance.sh ../talker-listener/talker-listener-json-instance.json
+ ```
+
+or for the archive-based solution:
+
+ ```bash
+ ./define-instance.sh ../talker-listener/talker-listener-xarchive-instance.json
+ ```
+
+After creating an instance, verify from Symphony API:
+
+ ```bash
+ curl -H "Content-Type: application/json" http://localhost:8082/v1alpha2/instances
+ curl -H "Content-Type: application/json" http://localhost:8082/v1alpha2/solutions
+ ```
+
+And on the robot / Muto side check whether the stack launched:
+
+ ```bash
+ ros2 topic list
+ ros2 topic echo /chatter
+ ```
+
+## Creating a new stack archive (packaging a directory)
+
+If you want to create a `stack/archive` manifest from a directory containing a launch-based ROS stack, use `create_archive.sh` located in this folder.
+
+Usage
+
+1. Prepare a directory with the stack contents (for example the included `sample-stack/` directory which contains a `launch/` file).
+
+2. Run the script from the repo or this folder. The script expects two arguments: `` and ``.
+
+Example (create manifest in the same folder):
+
+ ```bash
+ cd docs/samples/talker-listener
+ ./create_archive.sh sample-stack .
+ ```
+
+This will produce a JSON file named like `-archive.json` containing:
+- `metadata` (name/description/content_type)
+- `launch.data`: base64-encoded tar.gz of the `sample-stack/` directory
+- `launch.properties`: checksum, the launch file, flatten flag and other metadata
+
+You can then publish the generated file to Symphony with `define-solution.sh`:
+
+ ```bash
+ cd ../symphony
+ ./define-solution.sh ../talker-listener/sample-stack-archive.json
+ ```
+
+## Files in this folder
+
+- `talker-listener-json.json` — stack definition in plain JSON (content_type: `stack/json`)
+- `talker-listener-json-instance.json` — instance file for the JSON stack
+- `talker-listener-xarchive.json` — stack archive manifest (base64-encoded tar data)
+- `talker-listener-xarchive-instance.json` — instance file for the archive stack
+- `sample-stack-archive.json` — example pre-created archive manifest (created by `create_archive.sh`)
+- `sample-stack/` — example directory that can be packaged with `create_archive.sh`
+- `run-demo.sh` — automated end-to-end demo script (requires Symphony and Muto running)
+- `create_archive.sh` — helper script that packages a directory into a `stack/archive` JSON manifest
+
+## Troubleshooting & tips
+
+- Authentication: the helper scripts assume Symphony API is running locally and accept the default admin login (empty password). Adjust `SYMPHONY_API_URL` and credentials in the scripts if your environment differs.
+- Tools: `jq` and `curl` are required by the helper scripts. Install them with your OS package manager.
+- If the Muto nodes do not appear after instance creation, check `ros2 node list`, the Muto logs, and ensure the Symphony provider in Muto is enabled (`enable_symphony:=true` when launching the Muto system).
+
+## Summary
+
+This folder demonstrates both the minimal JSON stack format and the archive-based stack format. Use `define-solution.sh` to publish stacks to Symphony and `define-instance.sh` to bind them to targets (robots). Use `create_archive.sh` to create new archive-type manifests from a directory containing a ROS launch stack.
+
+If you want me to also add example commands to automatically register a target (target JSON/templates are in `docs/samples/symphony/target.json`) or to create a small troubleshooting checklist, I can add that next.
\ No newline at end of file
diff --git a/docs/samples/talker-listener/create_archive.sh b/docs/samples/talker-listener/create_archive.sh
new file mode 100755
index 0000000..59475d2
--- /dev/null
+++ b/docs/samples/talker-listener/create_archive.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+# Script to create a base64 encoded tar archive of a directory's contents and save it as JSON
+# Usage: ./create_archive.sh
+
+# Input validation
+if [ $# -ne 2 ]; then
+ echo "Usage: $0 "
+ echo " : Directory to archive"
+ echo " : Directory where the JSON file will be written"
+ exit 1
+fi
+
+INPUT_DIR="$1"
+OUTPUT_DIR="$2"
+
+# Validate input directory
+if [ ! -d "$INPUT_DIR" ]; then
+ echo "Error: $INPUT_DIR is not a directory"
+ exit 1
+fi
+
+# Validate output directory
+if [ ! -d "$OUTPUT_DIR" ]; then
+ echo "Error: $OUTPUT_DIR is not a directory"
+ exit 1
+fi
+
+
+
+# Extract solution name from filename (remove path and .json extension)
+ROOT_NAME=$(basename "$INPUT_DIR")
+MANIFEST_NAME="${ROOT_NAME}-archive"
+
+# Function to encode base64 from stdin
+encode_base64() {
+ # Read from stdin and encode to base64
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ # macOS (BSD base64)
+ base64
+ elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ -n "$WSL_DISTRO_NAME" ]] || [[ -n "$IS_WSL" ]] || [[ "$(uname -r)" == *Microsoft* ]] || [[ "$(uname -r)" == *microsoft* ]]; then
+ # WSL/Windows environments
+ base64 | tr -d '\n'
+ else
+ # Linux/Unix (GNU base64)
+ base64 -w 0
+ fi
+}
+
+# Create tar archive of all contents and encode in base64
+STACK_DATA_BASE64=$(tar -C $INPUT_DIR -czf - . | encode_base64)
+
+# SHA256 checksum of the base64 encoded data
+CHECKSUM=$(echo -n "$STACK_DATA_BASE64" | sha256sum | awk '{print $1}')
+echo "SHA256 Checksum: $CHECKSUM"
+
+# Find the relative path of the first file that ends with launch.py
+LAUNCH_FILE=$(cd "$INPUT_DIR" && find . -type f -name "*.launch.py" | head -n 1 | sed 's|^\./||')
+if [ -z "$LAUNCH_FILE" ]; then
+ echo "Error: No launch.py file found in the directory"
+ exit 1
+fi
+echo "Found launch file: $LAUNCH_FILE"
+
+# Create JSON manifest
+STACK_MANIFEST=$(cat << EOF
+{
+ "metadata": {
+ "name": "${MANIFEST_NAME}",
+ "description": "A simple talker-listener stack example using demo_nodes_cpp package.",
+ "content_type": "stack/archive"
+ },
+ "launch": {
+ "data": "${STACK_DATA_BASE64}",
+ "properties": {
+ "algorithm": "sha256",
+ "checksum": "${CHECKSUM}",
+ "launch_file": "${LAUNCH_FILE}",
+ "command": "launch",
+ "launch_args": [
+ {
+ "name": "foo",
+ "default": "bar"
+ }
+ ],
+ "ros_args": [],
+ "flatten": true
+ }
+ }
+}
+EOF
+)
+
+# Write to JSON file in output directory
+JSON_FILE="$OUTPUT_DIR/${MANIFEST_NAME}.json"
+echo "$STACK_MANIFEST" > "$JSON_FILE"
+echo "Created stack manifest JSON file: $JSON_FILE"
\ No newline at end of file
diff --git a/docs/samples/talker-listener/run-demo.sh b/docs/samples/talker-listener/run-demo.sh
new file mode 100755
index 0000000..85fd2c5
--- /dev/null
+++ b/docs/samples/talker-listener/run-demo.sh
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+# Lightweight end-to-end demo wrapper for the talker-listener sample.
+# Assumes Symphony is already running; waits for the API, verifies Muto nodes,
+# then defines the solution and creates an instance.
+# Usage: ./run-demo.sh
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+SYMPHONY_DIR="$SCRIPT_DIR/../symphony"
+EXAMPLE="talker-listener-xarchive"
+SOLUTION_JSON="$SCRIPT_DIR/${EXAMPLE}.json"
+INSTANCE_JSON="$SCRIPT_DIR/${EXAMPLE}-instance.json"
+
+# Check requirements (we assume Symphony is already running)
+for cmd in curl jq base64 ros2; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo "Required command not found: $cmd" >&2
+ echo "Please install it and re-run the script." >&2
+ exit 1
+ fi
+done
+
+echo "Assuming Symphony services are already running."
+
+# Wait for Symphony API to be available
+API_URL="http://localhost:8082/v1alpha2/greetings"
+echo "Waiting for Symphony API at $API_URL..."
+for i in $(seq 1 60); do
+ if curl -sS "$API_URL" >/dev/null 2>&1; then
+ echo "Symphony API is up"
+ break
+ fi
+ printf "."
+ sleep 1
+ if [ "$i" -eq 60 ]; then
+ printf '\nTimed out waiting for Symphony API\n' >&2
+ exit 2
+ fi
+done
+
+# Note: solution/instance creation will be performed after verifying Muto nodes
+echo "Symphony API is available. Will define solution & create instance after Muto nodes are verified."
+
+# Wait for expected Muto nodes to appear
+REQUIRED_NODES=(
+ "/muto/agent"
+ "/muto/commands_plugin"
+ "/muto/compose_plugin"
+ "/muto/core_twin"
+ "/muto/gateway"
+ "/muto/launch_plugin"
+ "/muto/muto_composer"
+ "/muto/muto_symphony_provider"
+ "/muto/provision_plugin"
+)
+
+echo "Waiting for Muto nodes to appear (timeout 60s)..."
+missing=()
+for i in $(seq 1 60); do
+ NODE_LIST=$(ros2 node list 2>/dev/null || true)
+ missing=()
+ for n in "${REQUIRED_NODES[@]}"; do
+ if ! printf '%s\n' "$NODE_LIST" | grep -F -x "$n" >/dev/null 2>&1; then
+ missing+=("$n")
+ fi
+ done
+ if [ ${#missing[@]} -eq 0 ]; then
+ echo "All Muto nodes are present"
+ break
+ fi
+ printf "."
+ sleep 1
+ if [ "$i" -eq 60 ]; then
+ printf '\nTimed out waiting for Muto nodes. Missing: %s\n' "${missing[*]}" >&2
+ exit 3
+ fi
+done
+
+# Define solution and create instance now that Muto nodes are present
+echo "Defining solution using $SOLUTION_JSON"
+"$SYMPHONY_DIR/define-solution.sh" "$SOLUTION_JSON"
+
+# Give Symphony a moment to register solution
+sleep 1
+
+echo "Creating instance using $INSTANCE_JSON"
+"$SYMPHONY_DIR/define-instance.sh" "$INSTANCE_JSON"
+
+# Final status
+echo ""
+echo "---"
+echo "Demo finished. Check Symphony instances and Muto:"
+echo " http://localhost:3000 Symphony Portal"
+echo " http://localhost:8082/v1alpha2/instances Symphony API"
+echo "On the robot Muto verify nodes/topics:"
+echo " ros2 node list | grep muto"
+echo " ros2 topic echo /chatter"
+
+echo "Next steps:"
+echo "1. To stop symphony, run:"
+echo "cd $SYMPHONY_DIR && docker compose down"
+echo "2. To delete the instance, run:"
+echo "$SYMPHONY_DIR/delete-instance.sh $INSTANCE_JSON"
+echo "3. To stop muto"
+echo "ros2 daemon stop"
+
diff --git a/docs/samples/talker-listener/sample-stack/README.md b/docs/samples/talker-listener/sample-stack/README.md
new file mode 100644
index 0000000..a8645cb
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/README.md
@@ -0,0 +1,60 @@
+# talker-listener-ws
+
+A ROS 2 Python workspace that pairs a talker and listener node on the `chatter` topic. The talker publishes a greeting once per second, and the listener logs every received message. Use this project as a minimal template for experimenting with ROS 2 publishers, subscribers, and launch files.
+
+## Repository Layout
+- `config/talker.yaml` — parameter file loaded by the launch script (currently defines `publish_frequency` and `message`).
+- `launch/talker_listener.launch.py` — starts both nodes in a single process using the configuration above.
+- `src/muto_talker/` — Python package that exposes the `muto_talker` node.
+- `src/muto_listener/` — Python package that exposes the `muto_listener` node.
+
+## Prerequisites
+- A ROS 2 distribution (Foxy, Galactic, Humble, or Rolling) with Python support.
+- `colcon` and `rosdep` installed for building and resolving dependencies.
+- A sourced ROS 2 environment (`source /opt/ros//setup.bash`).
+
+## Build
+```bash
+# From the workspace root
+rosdep install --from-paths src --ignore-src -r -y
+colcon build
+source install/setup.bash
+```
+You can scope the build to one package at a time (e.g. `--packages-select muto_talker`) while iterating.
+
+## Run
+After sourcing `install/setup.bash`:
+
+```bash
+# Run each node separately
+ros2 run muto_talker muto_talker
+ros2 run muto_listener muto_listener
+
+# Launch both nodes together
+ros2 launch --launch-file-path launch/talker_listener.launch.py
+```
+> Tip: If you prefer `ros2 launch muto_talker talker_listener.launch.py`, add the launch file to the `data_files` section of the package setup so it is installed under `share/muto_talker/launch`.
+
+The talker publishes `std_msgs/msg/String` messages such as `"Hello SDV Hackathon Chapter III! 3"`; the listener prints each payload to its logger.
+
+## Configuration
+The launch file loads `config/talker.yaml`, which currently defines:
+```yaml
+/talker:
+ ros__parameters:
+ publish_frequency: 1.0
+ message: "Hello, SDV Hackathon Chapter III!"
+```
+The node implementation still uses inline defaults (1 Hz and the string shown above). Update `muto_talker/muto_talker.py` to declare and read these parameters if you want runtime configurability.
+
+## Testing & Linting
+The packages include the standard ROS 2 Python linters.
+```bash
+colcon test --packages-select muto_talker muto_listener
+```
+This hooks `ament_flake8`, `ament_pep257`, and copyright checks.
+
+## Next Steps
+- Extend the talker to declare parameters and use `publish_frequency` and `message` at runtime.
+- Add more message types or QoS policies to experiment with ROS 2 communication strength.
+- Package and install launch files so they are discoverable via `ros2 launch` without an explicit path.
diff --git a/docs/samples/talker-listener/sample-stack/config/talker.yaml b/docs/samples/talker-listener/sample-stack/config/talker.yaml
new file mode 100644
index 0000000..adec5af
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/config/talker.yaml
@@ -0,0 +1,4 @@
+/talker:
+ ros__parameters:
+ publish_frequency: 1.0
+ message: "SDV Hackathon Chapter III!"
\ No newline at end of file
diff --git a/docs/samples/talker-listener/sample-stack/launch/talker_listener.launch.py b/docs/samples/talker-listener/sample-stack/launch/talker_listener.launch.py
new file mode 100644
index 0000000..2808bc4
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/launch/talker_listener.launch.py
@@ -0,0 +1,33 @@
+from launch import LaunchDescription
+from launch_ros.actions import Node
+import os
+
+
+def generate_launch_description():
+ talker_config = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "config",
+ "talker.yaml",
+ )
+ )
+ node_talker = Node(
+ name="talker",
+ package="muto_talker",
+ executable="muto_talker",
+ output="screen",
+ parameters=[talker_config],
+ )
+
+ node_listener = Node(
+ name="listener",
+ package="muto_listener",
+ executable="muto_listener",
+ output="screen",
+ )
+
+ ld = LaunchDescription()
+ ld.add_action(node_talker)
+ ld.add_action(node_listener)
+ return ld
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_listener/muto_listener/__init__.py b/docs/samples/talker-listener/sample-stack/src/muto_listener/muto_listener/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_listener/muto_listener/muto_listener.py b/docs/samples/talker-listener/sample-stack/src/muto_listener/muto_listener/muto_listener.py
new file mode 100644
index 0000000..6003139
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_listener/muto_listener/muto_listener.py
@@ -0,0 +1,23 @@
+import rclpy
+from rclpy.node import Node
+from std_msgs.msg import String
+
+class MutoListener(Node):
+ def __init__(self):
+ super().__init__('muto_listener')
+ self.get_logger().info('MutoListener node has been started.')
+ self.create_subscription(String, 'chatter', self.listener_callback, 10)
+
+ def listener_callback(self, msg):
+ self.get_logger().info(f'I heard: {msg.data}')
+
+
+def main(args=None):
+ rclpy.init(args=args)
+ muto_listener = MutoListener()
+ rclpy.spin(muto_listener)
+ muto_listener.destroy_node()
+ rclpy.shutdown()
+
+if __name__ == '__main__':
+ main()
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_listener/package.xml b/docs/samples/talker-listener/sample-stack/src/muto_listener/package.xml
new file mode 100644
index 0000000..5f94ab3
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_listener/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ muto_listener
+ 0.0.0
+ TODO: Package description
+ sel
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_listener/resource/muto_listener b/docs/samples/talker-listener/sample-stack/src/muto_listener/resource/muto_listener
new file mode 100644
index 0000000..e69de29
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_listener/setup.cfg b/docs/samples/talker-listener/sample-stack/src/muto_listener/setup.cfg
new file mode 100644
index 0000000..66d9342
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_listener/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/muto_listener
+[install]
+install_scripts=$base/lib/muto_listener
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_listener/setup.py b/docs/samples/talker-listener/sample-stack/src/muto_listener/setup.py
new file mode 100644
index 0000000..ea8ed68
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_listener/setup.py
@@ -0,0 +1,26 @@
+from setuptools import find_packages, setup
+
+package_name = 'muto_listener'
+
+setup(
+ name=package_name,
+ version='0.0.0',
+ packages=find_packages(exclude=['test']),
+ data_files=[
+ ('share/ament_index/resource_index/packages',
+ ['resource/' + package_name]),
+ ('share/' + package_name, ['package.xml']),
+ ],
+ install_requires=['setuptools'],
+ zip_safe=True,
+ maintainer='sel',
+ maintainer_email='ibrahim.sel@eteration.com',
+ description='TODO: Package description',
+ license='TODO: License declaration',
+ tests_require=['pytest'],
+ entry_points={
+ 'console_scripts': [
+ 'muto_listener = muto_listener.muto_listener:main'
+ ],
+ },
+)
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_talker/muto_talker/__init__.py b/docs/samples/talker-listener/sample-stack/src/muto_talker/muto_talker/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_talker/muto_talker/muto_talker.py b/docs/samples/talker-listener/sample-stack/src/muto_talker/muto_talker/muto_talker.py
new file mode 100644
index 0000000..dfecd57
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_talker/muto_talker/muto_talker.py
@@ -0,0 +1,31 @@
+import rclpy
+from rclpy.node import Node
+from std_msgs.msg import String
+
+class MutoTalker(Node):
+ def __init__(self):
+ super().__init__('muto_talker')
+ self.get_logger().info('MutoTalker node has been started.')
+ self.declare_parameter('publish_frequency', 1.0)
+ self.declare_parameter('message', 'Hello, SDV Hackathon Chapter III!')
+
+ self.publisher_ = self.create_publisher(String, 'chatter', 10)
+ self.timer = self.create_timer(self.get_parameter('publish_frequency').get_parameter_value().double_value, self.timer_callback)
+ self.ct = 0
+
+ def timer_callback(self):
+ msg = String()
+ msg.data = self.get_parameter('message').get_parameter_value().string_value + f' {self.ct}'
+ self.publisher_.publish(msg)
+ self.ct += 1
+ self.get_logger().info(f'Published: {msg.data}')
+
+def main(args=None):
+ rclpy.init(args=args)
+ muto_talker = MutoTalker()
+ rclpy.spin(muto_talker)
+ muto_talker.destroy_node()
+ rclpy.shutdown()
+
+if __name__ == '__main__':
+ main()
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_talker/package.xml b/docs/samples/talker-listener/sample-stack/src/muto_talker/package.xml
new file mode 100644
index 0000000..abfbd89
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_talker/package.xml
@@ -0,0 +1,18 @@
+
+
+
+ muto_talker
+ 0.0.0
+ TODO: Package description
+ sel
+ TODO: License declaration
+
+ ament_copyright
+ ament_flake8
+ ament_pep257
+ python3-pytest
+
+
+ ament_python
+
+
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_talker/resource/muto_talker b/docs/samples/talker-listener/sample-stack/src/muto_talker/resource/muto_talker
new file mode 100644
index 0000000..e69de29
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_talker/setup.cfg b/docs/samples/talker-listener/sample-stack/src/muto_talker/setup.cfg
new file mode 100644
index 0000000..6fc00b8
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_talker/setup.cfg
@@ -0,0 +1,4 @@
+[develop]
+script_dir=$base/lib/muto_talker
+[install]
+install_scripts=$base/lib/muto_talker
diff --git a/docs/samples/talker-listener/sample-stack/src/muto_talker/setup.py b/docs/samples/talker-listener/sample-stack/src/muto_talker/setup.py
new file mode 100644
index 0000000..82d7b64
--- /dev/null
+++ b/docs/samples/talker-listener/sample-stack/src/muto_talker/setup.py
@@ -0,0 +1,26 @@
+from setuptools import find_packages, setup
+
+package_name = 'muto_talker'
+
+setup(
+ name=package_name,
+ version='0.0.0',
+ packages=find_packages(exclude=['test']),
+ data_files=[
+ ('share/ament_index/resource_index/packages',
+ ['resource/' + package_name]),
+ ('share/' + package_name, ['package.xml']),
+ ],
+ install_requires=['setuptools'],
+ zip_safe=True,
+ maintainer='sel',
+ maintainer_email='ibrahim.sel@eteration.com',
+ description='TODO: Package description',
+ license='TODO: License declaration',
+ tests_require=['pytest'],
+ entry_points={
+ 'console_scripts': [
+ 'muto_talker = muto_talker.muto_talker:main'
+ ],
+ },
+)
diff --git a/docs/samples/talker-listener/talker-listener-json-instance.json b/docs/samples/talker-listener/talker-listener-json-instance.json
new file mode 100644
index 0000000..90dda37
--- /dev/null
+++ b/docs/samples/talker-listener/talker-listener-json-instance.json
@@ -0,0 +1,14 @@
+{
+ "metadata": {
+ "name": "talker-listener-json-instance",
+ "labels": {
+ "muto": "demo"
+ }
+ },
+ "spec": {
+ "solution": "talker-listener-json:1",
+ "target": {
+ "name": "test-robot-debug"
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/samples/talker-listener/talker-listener-json.json b/docs/samples/talker-listener/talker-listener-json.json
new file mode 100644
index 0000000..12bad96
--- /dev/null
+++ b/docs/samples/talker-listener/talker-listener-json.json
@@ -0,0 +1,21 @@
+{
+ "metadata": {
+ "name": "Muto Simple Talker-Listener Stack",
+ "description": "A simple talker-listener stack example using demo_nodes_cpp package.",
+ "content_type": "stack/json"
+ },
+ "launch": {
+ "node": [
+ {
+ "name": "talker",
+ "pkg": "demo_nodes_cpp",
+ "exec": "talker"
+ },
+ {
+ "name": "listener",
+ "pkg": "demo_nodes_cpp",
+ "exec": "listener"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/docs/samples/talker-listener/talker-listener-xarchive-instance.json b/docs/samples/talker-listener/talker-listener-xarchive-instance.json
new file mode 100644
index 0000000..e8cee21
--- /dev/null
+++ b/docs/samples/talker-listener/talker-listener-xarchive-instance.json
@@ -0,0 +1,14 @@
+{
+ "metadata": {
+ "name": "talker-listener-xarchive-instance",
+ "labels": {
+ "muto": "demo"
+ }
+ },
+ "spec": {
+ "solution": "talker-listener-xarchive:1",
+ "target": {
+ "name": "test-robot-debug"
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/samples/talker-listener/talker-listener-xarchive.json b/docs/samples/talker-listener/talker-listener-xarchive.json
new file mode 100644
index 0000000..2669168
--- /dev/null
+++ b/docs/samples/talker-listener/talker-listener-xarchive.json
@@ -0,0 +1,25 @@
+{
+ "metadata": {
+ "name": "Muto Simple Talker-Listener Stack",
+ "description": "A simple talker-listener stack example using demo_nodes_cpp package.",
+ "content_type": "stack/archive"
+ },
+ "launch": {
+ "data": "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",
+ "properties": {
+ "algorithm": "sha256",
+ "checksum": "553fd2dc7d0eb41e7d65c467d358e7962d3efbb0e2f2e4f8158e926a081f96d0",
+ "launch_file": "launch/talker_listener.launch.py",
+ "command": "launch",
+ "launch_args": [
+ {
+ "name": "arg1",
+ "default": "val1"
+ }
+ ],
+ "ros_args": [],
+ "flatten": true
+ }
+ }
+
+}
diff --git a/docs/samples/talker-listener/test-robot-debug-target.json b/docs/samples/talker-listener/test-robot-debug-target.json
new file mode 100644
index 0000000..f13d538
--- /dev/null
+++ b/docs/samples/talker-listener/test-robot-debug-target.json
@@ -0,0 +1,34 @@
+{
+ "metadata": {
+ "name": "ankaios-target"
+ },
+ "spec": {
+ "forceRedeploy": true,
+ "components": [
+ {
+ "name": "muto",
+ "type": "muto-agent",
+ "properties": {
+ }
+ }
+ ],
+ "topologies": [
+ {
+ "bindings": [
+ {
+ "role": "muto",
+ "provider": "providers.target.mqtt",
+ "config": {
+ "name": "proxy",
+ "brokerAddress": "tcp://mosquitto:1883",
+ "clientID": "symphony",
+ "requestTopic": "coa-request",
+ "responseTopic": "coa-response",
+ "timeoutSeconds": "30"
+ }
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/launch/muto.launch.py b/launch/muto.launch.py
index 3486033..2e9bf57 100644
--- a/launch/muto.launch.py
+++ b/launch/muto.launch.py
@@ -142,11 +142,11 @@ def generate_launch_description():
arguments=['--ros-args', '--log-level', log_level]
)
- node_native_plugin = Node(
+ node_provision_plugin = Node(
namespace=muto_namespace,
- name="native_plugin",
+ name="provision_plugin",
package="composer",
- executable="native_plugin",
+ executable="provision_plugin",
output="screen",
parameters=[
muto_config_file,
@@ -200,7 +200,7 @@ def generate_launch_description():
ld.add_action(node_twin)
ld.add_action(node_composer)
ld.add_action(node_compose_plugin)
- ld.add_action(node_native_plugin)
+ ld.add_action(node_provision_plugin)
ld.add_action(node_launch_plugin)
ld.add_action(symphony_provider)