From 89655d3240f389892257054acc74cdd2a3916e63 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Thu, 4 Jul 2024 08:09:41 -0700 Subject: [PATCH 1/6] Add Fabric Synchronization Guide --- docs/guides/fabric_synchronization_guide.md | 148 ++++++++++++++++++ .../scripts/run_fabric_sink_rp4.sh | 28 ++++ .../scripts/run_fabric_sink_x86.sh | 28 ++++ .../scripts/run_fabric_source_rp4.sh | 26 +++ .../scripts/run_fabric_source_x86.sh | 26 +++ .../scripts/stop_fabric_source.sh | 19 +++ 6 files changed, 275 insertions(+) create mode 100644 docs/guides/fabric_synchronization_guide.md create mode 100755 examples/fabric-admin/scripts/run_fabric_sink_rp4.sh create mode 100755 examples/fabric-admin/scripts/run_fabric_sink_x86.sh create mode 100755 examples/fabric-admin/scripts/run_fabric_source_rp4.sh create mode 100755 examples/fabric-admin/scripts/run_fabric_source_x86.sh create mode 100755 examples/fabric-admin/scripts/stop_fabric_source.sh diff --git a/docs/guides/fabric_synchronization_guide.md b/docs/guides/fabric_synchronization_guide.md new file mode 100644 index 00000000000000..3a245c4b8fa3b4 --- /dev/null +++ b/docs/guides/fabric_synchronization_guide.md @@ -0,0 +1,148 @@ +# Fabric Synchronization Guide + +- [Fabric Synchronization Guide](#fabric-synchronization-guide) + - [Fabric Sync Example Applications](#fabric-sync-example-applications) + - [Run Fabric Sync Demo on RP4](#run-fabric-sync-demo-on-rp4) + +## Fabric Sync Example Applications + +Fabric-Admin and Fabric-Bridge applications are provided to demonstrate Fabric +Synchronization feature.. You can find them in the examples. + +### Building the Example Application + +- Building the Fabric-Admin Application + + [Fabric-Admin](https://github.com/project-chip/connectedhomeip/tree/master/examples/fabric-admin/README.md) + +* Building the Fabric-Bridge Application + + [Fabric-Bridge](https://github.com/project-chip/connectedhomeip/tree/master/examples/fabric-bridge-app/linux/README.md) + +## Run Fabric Sync Demo on RP4 + +### Setup Fabric Source + +Connect to the Fabric Source server: + +``` +ssh ubuntu@xxx.xxx.xxx.xxx +``` + +Password: + +Run the Fabric Source script: + +``` +./run_fabric_source.sh +``` + +### Setup Fabric Sink + +Connect to the Fabric Sink server: + +``` +ssh ubuntu@xxx.xxx.xxx.xxx +``` + +Password: + +Run the Fabric Sink script: + +``` +./run_fabric_sink.sh +``` + +### Fabric Sync Setup + +Enable Fabric Auto Sync: + +In Fabric-Sync console: + +``` +fabricsync enable-auto-sync 1 +``` + +Pair the Fabric-Source bridge to Fabric-Sync with node ID 1: + +``` +fabricsync add-bridge 1 +``` + +### Pair Light Example to Fabric-Source + +Pair the Light Example with node ID 3 using its payload number: + +``` +pairing already-discovered 3 20202021 5540 +``` + +After the Light Example is successfully paired in Fabric-Source, it will be +synced to Fabric-Sink with a new assigned node ID. + +Toggle the Light Example: + +From Fabric-Source: + +``` +onoff on 1 +onoff off 1 +``` + +From Fabric-Sink: (Use the node ID assigned) + +``` +onoff on x 1 +onoff off x 1 +``` + +### Remove Light Example from Fabric-Source + +Unpair the Light Example: + +``` +pairing unpair +``` + +After the Light Example is successfully unpaired from Fabric-Source, it will +also be removed from the Fabric-Sink. + +### Pair Commercial Switch to Fabric-Source + +Pair the switch using its payload number: + +In Fabric-Source console: + +``` +pairing code-wifi +``` + +After the switch is successfully paired in Fabric-Source, it will be synced to +Fabric-Sink with a new assigned node ID. + +Toggle the switch: + +From Fabric-Source: + +``` +onoff on 1 +onoff off 1 +``` + +From Fabric-Sink: (Use the node ID assigned) + +``` +onoff on 1 +onoff off 1 +``` + +### Remove Switch from Fabric-Source + +Unpair the switch: + +``` +pairing unpair +``` + +After the switch is successfully unpaired from Fabric-Source, it will also be +removed from the Fabric-Sink. diff --git a/examples/fabric-admin/scripts/run_fabric_sink_rp4.sh b/examples/fabric-admin/scripts/run_fabric_sink_rp4.sh new file mode 100755 index 00000000000000..d372948a87be2f --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_sink_rp4.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Check if --verbose option is specified +VERBOSE=false +for arg in "$@"; do + if [ "$arg" == "--verbose" ]; then + VERBOSE=true + break + fi +done + +# Kill fabric-admin if it is running +fabric_admin_pid=$(pgrep -f fabric-admin) +if [ ! -z "$fabric_admin_pid" ]; then + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Remove /tmp/chip_* files and directories +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" + +# Start fabric-admin with or without log file path based on --verbose option +if [ "$VERBOSE" = true ]; then + ./fabric-admin +else + ./fabric-admin --log-file-path /tmp/fabric_admin.log +fi diff --git a/examples/fabric-admin/scripts/run_fabric_sink_x86.sh b/examples/fabric-admin/scripts/run_fabric_sink_x86.sh new file mode 100755 index 00000000000000..78d2bbb6ce1469 --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_sink_x86.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Check if --verbose option is specified +VERBOSE=false +for arg in "$@"; do + if [ "$arg" == "--verbose" ]; then + VERBOSE=true + break + fi +done + +# Kill fabric-admin if it is running +fabric_admin_pid=$(pgrep -f fabric-admin) +if [ ! -z "$fabric_admin_pid" ]; then + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Remove /tmp/chip_* files and directories +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" + +# Start fabric-admin with or without log file path based on --verbose option +if [ "$VERBOSE" = true ]; then + out/debug/standalone/fabric-admin +else + out/debug/standalone/fabric-admin --log-file-path /tmp/fabric_admin.log +fi diff --git a/examples/fabric-admin/scripts/run_fabric_source_rp4.sh b/examples/fabric-admin/scripts/run_fabric_source_rp4.sh new file mode 100755 index 00000000000000..cffb849b16ae78 --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_source_rp4.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Kill fabric-admin if it is running +fabric_admin_pid=$(pgrep -f fabric-admin) +if [ ! -z "$fabric_admin_pid" ]; then + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Kill fabric-bridge-app if it is running +fabric_bridge_app_pid=$(pgrep -f fabric-bridge-app) +if [ ! -z "$fabric_bridge_app_pid" ]; then + kill -9 "$fabric_bridge_app_pid" + echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" +fi + +# Remove /tmp/chip_* files and directories +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" + +# Start fabric-bridge-app and redirect its output to /dev/null +./fabric-bridge-app >/dev/null 2>&1 & +echo "Started fabric-bridge-app" + +# Start fabric-admin +./fabric-admin --ble-adapter 0 diff --git a/examples/fabric-admin/scripts/run_fabric_source_x86.sh b/examples/fabric-admin/scripts/run_fabric_source_x86.sh new file mode 100755 index 00000000000000..d7181699123d4e --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_source_x86.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Kill fabric-admin if it is running +fabric_admin_pid=$(pgrep -f fabric-admin) +if [ ! -z "$fabric_admin_pid" ]; then + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Kill fabric-bridge-app if it is running +fabric_bridge_app_pid=$(pgrep -f fabric-bridge-app) +if [ ! -z "$fabric_bridge_app_pid" ]; then + kill -9 "$fabric_bridge_app_pid" + echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" +fi + +# Remove /tmp/chip_* files and directories +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" + +# Start fabric-bridge-app and redirect its output to /dev/null +out/debug/standalone/fabric-bridge-app >/dev/null 2>&1 & +echo "Started fabric-bridge-app" + +# Start fabric-admin +out/debug/standalone/fabric-admin diff --git a/examples/fabric-admin/scripts/stop_fabric_source.sh b/examples/fabric-admin/scripts/stop_fabric_source.sh new file mode 100755 index 00000000000000..f499e2b70b9c8b --- /dev/null +++ b/examples/fabric-admin/scripts/stop_fabric_source.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Kill fabric-admin if it is running +fabric_admin_pid=$(pgrep -f fabric-admin) +if [ ! -z "$fabric_admin_pid" ]; then + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Kill fabric-bridge-app if it is running +fabric_bridge_app_pid=$(pgrep -f fabric-bridge-app) +if [ ! -z "$fabric_bridge_app_pid" ]; then + kill -9 "$fabric_bridge_app_pid" + echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" +fi + +# Remove /tmp/chip_* files and directories +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" From df29dc94a60408f88d97158ed05e0e1675778df4 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Fri, 5 Jul 2024 09:42:10 -0700 Subject: [PATCH 2/6] Update docs/guides/fabric_synchronization_guide.md Co-authored-by: Andrei Litvin --- docs/guides/fabric_synchronization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/fabric_synchronization_guide.md b/docs/guides/fabric_synchronization_guide.md index 3a245c4b8fa3b4..d39b26a34bc43a 100644 --- a/docs/guides/fabric_synchronization_guide.md +++ b/docs/guides/fabric_synchronization_guide.md @@ -7,7 +7,7 @@ ## Fabric Sync Example Applications Fabric-Admin and Fabric-Bridge applications are provided to demonstrate Fabric -Synchronization feature.. You can find them in the examples. +Synchronization feature. You can find them in the examples. ### Building the Example Application From 597d01063cba06eea869ad09ac22cd5f0829eaf8 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Tue, 9 Jul 2024 14:42:33 -0700 Subject: [PATCH 3/6] Address review comments --- docs/guides/fabric_synchronization_guide.md | 20 +++++++- .../images/matter_fabric_synchronization.png | Bin 0 -> 38846 bytes .../fabric-admin/scripts/run_fabric_sink.sh | 41 ++++++++++++++++ .../scripts/run_fabric_sink_rp4.sh | 28 ----------- .../scripts/run_fabric_sink_x86.sh | 28 ----------- .../fabric-admin/scripts/run_fabric_source.sh | 44 ++++++++++++++++++ .../scripts/run_fabric_source_rp4.sh | 26 ----------- .../scripts/run_fabric_source_x86.sh | 26 ----------- .../scripts/stop_fabric_source.sh | 7 ++- 9 files changed, 108 insertions(+), 112 deletions(-) create mode 100644 docs/guides/images/matter_fabric_synchronization.png create mode 100755 examples/fabric-admin/scripts/run_fabric_sink.sh delete mode 100755 examples/fabric-admin/scripts/run_fabric_sink_rp4.sh delete mode 100755 examples/fabric-admin/scripts/run_fabric_sink_x86.sh create mode 100755 examples/fabric-admin/scripts/run_fabric_source.sh delete mode 100755 examples/fabric-admin/scripts/run_fabric_source_rp4.sh delete mode 100755 examples/fabric-admin/scripts/run_fabric_source_x86.sh diff --git a/docs/guides/fabric_synchronization_guide.md b/docs/guides/fabric_synchronization_guide.md index d39b26a34bc43a..303ac0d381b560 100644 --- a/docs/guides/fabric_synchronization_guide.md +++ b/docs/guides/fabric_synchronization_guide.md @@ -6,8 +6,24 @@ ## Fabric Sync Example Applications -Fabric-Admin and Fabric-Bridge applications are provided to demonstrate Fabric -Synchronization feature. You can find them in the examples. +Fabric-Admin and Fabric-Bridge example applications are provided to demonstrate +Fabric Synchronization feature. You can find them in the examples. + +![matter_fabric_synchronization](images/matter_fabric_synchronization.png) + +Fabric-Admin example app implements the Fabric Administrator role and +communicate with the Fabric-Bridge-App on the other side, facilitating the +Fabric Synchronization process. + +Fabric-Bridge-App example app implements the Aggregator device type with Fabric +Synchronization condition met and demonstrates the end-to-end Fabric +Synchronization feature using dynamic endpoints. + +Fabric Synchronization can be triggered from either side. The initiator of the +Fabric Synchronization process, who shares their devices, takes on the +Commissioner role. The recipient of the Fabric Synchronization request, who +receives the shared devices, assumes the Commissionee role. This flexibility +enables a seamless and efficient synchronization process. ### Building the Example Application diff --git a/docs/guides/images/matter_fabric_synchronization.png b/docs/guides/images/matter_fabric_synchronization.png new file mode 100644 index 0000000000000000000000000000000000000000..95c99c4fe150b952513bb0576912e6004dbffefc GIT binary patch literal 38846 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 JX!Qrr{|_*Mk|_WH literal 0 HcmV?d00001 diff --git a/examples/fabric-admin/scripts/run_fabric_sink.sh b/examples/fabric-admin/scripts/run_fabric_sink.sh new file mode 100755 index 00000000000000..e800ad72f809fe --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_sink.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Default paths +FABRIC_ADMIN_PATH="./fabric-admin" +FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" + +# Detect platform and set default paths +if [[ $(uname -m) == "x86_64" ]]; then + FABRIC_ADMIN_PATH="out/debug/standalone/fabric-admin" +fi + +# Check if --verbose option is specified +VERBOSE=false +for arg in "$@"; do + if [ "$arg" == "--verbose" ]; then + VERBOSE=true + break + fi +done + +# Kill fabric-admin if it is running +echo "Checking for running fabric-admin process..." +fabric_admin_pid=$(pgrep -f "$FABRIC_ADMIN_PATH") +if [ ! -z "$fabric_admin_pid" ]; then + echo "Found fabric-admin with PID $fabric_admin_pid, attempting to kill..." + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Remove /tmp/chip_* files and directories +echo "Removing /tmp/chip_* files and directories..." +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" + +# Start fabric-admin with or without log file path based on --verbose option +echo "Starting fabric-admin..." +if [ "$VERBOSE" = true ]; then + "$FABRIC_ADMIN_PATH" +else + "$FABRIC_ADMIN_PATH" --log-file-path "$FABRIC_ADMIN_LOG" +fi diff --git a/examples/fabric-admin/scripts/run_fabric_sink_rp4.sh b/examples/fabric-admin/scripts/run_fabric_sink_rp4.sh deleted file mode 100755 index d372948a87be2f..00000000000000 --- a/examples/fabric-admin/scripts/run_fabric_sink_rp4.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Check if --verbose option is specified -VERBOSE=false -for arg in "$@"; do - if [ "$arg" == "--verbose" ]; then - VERBOSE=true - break - fi -done - -# Kill fabric-admin if it is running -fabric_admin_pid=$(pgrep -f fabric-admin) -if [ ! -z "$fabric_admin_pid" ]; then - kill -9 "$fabric_admin_pid" - echo "Killed fabric-admin with PID $fabric_admin_pid" -fi - -# Remove /tmp/chip_* files and directories -sudo rm -rf /tmp/chip_* -echo "Removed /tmp/chip_* files and directories" - -# Start fabric-admin with or without log file path based on --verbose option -if [ "$VERBOSE" = true ]; then - ./fabric-admin -else - ./fabric-admin --log-file-path /tmp/fabric_admin.log -fi diff --git a/examples/fabric-admin/scripts/run_fabric_sink_x86.sh b/examples/fabric-admin/scripts/run_fabric_sink_x86.sh deleted file mode 100755 index 78d2bbb6ce1469..00000000000000 --- a/examples/fabric-admin/scripts/run_fabric_sink_x86.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Check if --verbose option is specified -VERBOSE=false -for arg in "$@"; do - if [ "$arg" == "--verbose" ]; then - VERBOSE=true - break - fi -done - -# Kill fabric-admin if it is running -fabric_admin_pid=$(pgrep -f fabric-admin) -if [ ! -z "$fabric_admin_pid" ]; then - kill -9 "$fabric_admin_pid" - echo "Killed fabric-admin with PID $fabric_admin_pid" -fi - -# Remove /tmp/chip_* files and directories -sudo rm -rf /tmp/chip_* -echo "Removed /tmp/chip_* files and directories" - -# Start fabric-admin with or without log file path based on --verbose option -if [ "$VERBOSE" = true ]; then - out/debug/standalone/fabric-admin -else - out/debug/standalone/fabric-admin --log-file-path /tmp/fabric_admin.log -fi diff --git a/examples/fabric-admin/scripts/run_fabric_source.sh b/examples/fabric-admin/scripts/run_fabric_source.sh new file mode 100755 index 00000000000000..3b2bb2b07bc8cf --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_source.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Default paths +FABRIC_ADMIN_PATH="./fabric-admin" +FABRIC_BRIDGE_APP_PATH="./fabric-bridge-app" +FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" +FABRIC_BRIDGE_APP_LOG="/tmp/fabric_bridge_app.log" + +# Detect platform and set default paths +PLATFORM=$(uname -m) +if [[ "$PLATFORM" == "x86_64" ]]; then + FABRIC_ADMIN_PATH="out/debug/standalone/fabric-admin" + FABRIC_BRIDGE_APP_PATH="out/debug/standalone/fabric-bridge-app" +fi + +# Check if --verbose option is specified +VERBOSE=false +for arg in "$@"; do + if [ "$arg" == "--verbose" ]; then + VERBOSE=true + break + fi +done + +# Stop any running instances and clean up + +if [[ "$PLATFORM" == "x86_64" ]]; then + ./examples/fabric-admin/scripts/stop_fabric_source.sh +elif [[ "$PLATFORM" == "aarch64" ]]; then + ./stop_fabric_source.sh +fi + +# Start fabric-bridge-app if available and redirect its output to /dev/null +if [ -f "$FABRIC_BRIDGE_APP_PATH" ]; then + "$FABRIC_BRIDGE_APP_PATH" >"$FABRIC_BRIDGE_APP_LOG" 2>&1 & + echo "Started fabric-bridge-app" +fi + +# Start fabric-admin with or without log file path based on --verbose option +if [ "$VERBOSE" = true ]; then + "$FABRIC_ADMIN_PATH" +else + "$FABRIC_ADMIN_PATH" --log-file-path "$FABRIC_ADMIN_LOG" +fi diff --git a/examples/fabric-admin/scripts/run_fabric_source_rp4.sh b/examples/fabric-admin/scripts/run_fabric_source_rp4.sh deleted file mode 100755 index cffb849b16ae78..00000000000000 --- a/examples/fabric-admin/scripts/run_fabric_source_rp4.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# Kill fabric-admin if it is running -fabric_admin_pid=$(pgrep -f fabric-admin) -if [ ! -z "$fabric_admin_pid" ]; then - kill -9 "$fabric_admin_pid" - echo "Killed fabric-admin with PID $fabric_admin_pid" -fi - -# Kill fabric-bridge-app if it is running -fabric_bridge_app_pid=$(pgrep -f fabric-bridge-app) -if [ ! -z "$fabric_bridge_app_pid" ]; then - kill -9 "$fabric_bridge_app_pid" - echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" -fi - -# Remove /tmp/chip_* files and directories -sudo rm -rf /tmp/chip_* -echo "Removed /tmp/chip_* files and directories" - -# Start fabric-bridge-app and redirect its output to /dev/null -./fabric-bridge-app >/dev/null 2>&1 & -echo "Started fabric-bridge-app" - -# Start fabric-admin -./fabric-admin --ble-adapter 0 diff --git a/examples/fabric-admin/scripts/run_fabric_source_x86.sh b/examples/fabric-admin/scripts/run_fabric_source_x86.sh deleted file mode 100755 index d7181699123d4e..00000000000000 --- a/examples/fabric-admin/scripts/run_fabric_source_x86.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -# Kill fabric-admin if it is running -fabric_admin_pid=$(pgrep -f fabric-admin) -if [ ! -z "$fabric_admin_pid" ]; then - kill -9 "$fabric_admin_pid" - echo "Killed fabric-admin with PID $fabric_admin_pid" -fi - -# Kill fabric-bridge-app if it is running -fabric_bridge_app_pid=$(pgrep -f fabric-bridge-app) -if [ ! -z "$fabric_bridge_app_pid" ]; then - kill -9 "$fabric_bridge_app_pid" - echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" -fi - -# Remove /tmp/chip_* files and directories -sudo rm -rf /tmp/chip_* -echo "Removed /tmp/chip_* files and directories" - -# Start fabric-bridge-app and redirect its output to /dev/null -out/debug/standalone/fabric-bridge-app >/dev/null 2>&1 & -echo "Started fabric-bridge-app" - -# Start fabric-admin -out/debug/standalone/fabric-admin diff --git a/examples/fabric-admin/scripts/stop_fabric_source.sh b/examples/fabric-admin/scripts/stop_fabric_source.sh index f499e2b70b9c8b..3a9e0f9bbe4f8f 100755 --- a/examples/fabric-admin/scripts/stop_fabric_source.sh +++ b/examples/fabric-admin/scripts/stop_fabric_source.sh @@ -1,14 +1,17 @@ #!/bin/bash +FABRIC_ADMIN_PATH="./fabric-admin" +FABRIC_BRIDGE_APP_PATH="./fabric-bridge-app" + # Kill fabric-admin if it is running -fabric_admin_pid=$(pgrep -f fabric-admin) +fabric_admin_pid=$(pgrep -f "$FABRIC_ADMIN_PATH") if [ ! -z "$fabric_admin_pid" ]; then kill -9 "$fabric_admin_pid" echo "Killed fabric-admin with PID $fabric_admin_pid" fi # Kill fabric-bridge-app if it is running -fabric_bridge_app_pid=$(pgrep -f fabric-bridge-app) +fabric_bridge_app_pid=$(pgrep -f "$FABRIC_BRIDGE_APP_PATH") if [ ! -z "$fabric_bridge_app_pid" ]; then kill -9 "$fabric_bridge_app_pid" echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" From b6594892aa926edf19ce5c477c0f7cc07c50dc78 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Wed, 10 Jul 2024 10:13:21 -0700 Subject: [PATCH 4/6] Update docs/guides/fabric_synchronization_guide.md Co-authored-by: Andrei Litvin --- docs/guides/fabric_synchronization_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/fabric_synchronization_guide.md b/docs/guides/fabric_synchronization_guide.md index 303ac0d381b560..36107744a97930 100644 --- a/docs/guides/fabric_synchronization_guide.md +++ b/docs/guides/fabric_synchronization_guide.md @@ -12,7 +12,7 @@ Fabric Synchronization feature. You can find them in the examples. ![matter_fabric_synchronization](images/matter_fabric_synchronization.png) Fabric-Admin example app implements the Fabric Administrator role and -communicate with the Fabric-Bridge-App on the other side, facilitating the +communicates with the Fabric-Bridge-App on the other side, facilitating the Fabric Synchronization process. Fabric-Bridge-App example app implements the Aggregator device type with Fabric From 0c6e281318e0b5e53aa27ff31d6d7884ee2055f1 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Wed, 10 Jul 2024 10:58:22 -0700 Subject: [PATCH 5/6] Update scripts with mutiple pathes --- .../fabric-admin/scripts/run_fabric_sink.sh | 59 ++++++++-- .../fabric-admin/scripts/run_fabric_source.sh | 108 +++++++++++++++--- 2 files changed, 138 insertions(+), 29 deletions(-) diff --git a/examples/fabric-admin/scripts/run_fabric_sink.sh b/examples/fabric-admin/scripts/run_fabric_sink.sh index e800ad72f809fe..3013965479268a 100755 --- a/examples/fabric-admin/scripts/run_fabric_sink.sh +++ b/examples/fabric-admin/scripts/run_fabric_sink.sh @@ -1,27 +1,64 @@ #!/bin/bash # Default paths -FABRIC_ADMIN_PATH="./fabric-admin" +DEFAULT_CHOICES=( + "./fabric-admin" + "out/debug/standalone/fabric-admin" + "out/linux-x64-fabric-admin/fabric-admin" + "out/darwin-arm64-fabric-admin/fabric-admin" +) FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" +FABRIC_ADMIN_PATH="" -# Detect platform and set default paths -if [[ $(uname -m) == "x86_64" ]]; then - FABRIC_ADMIN_PATH="out/debug/standalone/fabric-admin" -fi +# Function to find fabric-admin binary +find_fabric_admin() { + local choices=("$@") + for path in "${choices[@]}"; do + if [[ -e "$path" ]]; then + echo "$path" + return 0 + fi + done + return 1 +} -# Check if --verbose option is specified +# Parse arguments VERBOSE=false +SPECIFIED_PATH="" + for arg in "$@"; do - if [ "$arg" == "--verbose" ]; then - VERBOSE=true - break - fi + case $arg in + --verbose) + VERBOSE=true + ;; + --path=*) + SPECIFIED_PATH="${arg#*=}" + ;; + esac done +# Use specified path if provided +if [[ -n "$SPECIFIED_PATH" ]]; then + if [[ -e "$SPECIFIED_PATH" ]]; then + FABRIC_ADMIN_PATH="$SPECIFIED_PATH" + else + echo >&2 "Specified path does not exist: $SPECIFIED_PATH" + exit 1 + fi +else + FABRIC_ADMIN_PATH=$(find_fabric_admin "${DEFAULT_CHOICES[@]}") + if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the fabric-admin binary" + exit 1 + fi +fi + +echo "PATH IS: $FABRIC_ADMIN_PATH" + # Kill fabric-admin if it is running echo "Checking for running fabric-admin process..." fabric_admin_pid=$(pgrep -f "$FABRIC_ADMIN_PATH") -if [ ! -z "$fabric_admin_pid" ]; then +if [[ -n "$fabric_admin_pid" ]]; then echo "Found fabric-admin with PID $fabric_admin_pid, attempting to kill..." kill -9 "$fabric_admin_pid" echo "Killed fabric-admin with PID $fabric_admin_pid" diff --git a/examples/fabric-admin/scripts/run_fabric_source.sh b/examples/fabric-admin/scripts/run_fabric_source.sh index 3b2bb2b07bc8cf..95df7a135bb596 100755 --- a/examples/fabric-admin/scripts/run_fabric_source.sh +++ b/examples/fabric-admin/scripts/run_fabric_source.sh @@ -1,33 +1,105 @@ #!/bin/bash +# Get the path to the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + # Default paths -FABRIC_ADMIN_PATH="./fabric-admin" -FABRIC_BRIDGE_APP_PATH="./fabric-bridge-app" +DEFAULT_ADMIN_CHOICES=( + "./fabric-admin" + "out/debug/standalone/fabric-admin" + "out/linux-x64-fabric-admin/fabric-admin" + "out/darwin-arm64-fabric-admin/fabric-admin" +) +DEFAULT_BRIDGE_CHOICES=( + "./fabric-bridge-app" + "out/debug/standalone/fabric-bridge-app" + "out/linux-x64-fabric-bridge-app/fabric-bridge-app" + "out/darwin-arm64-fabric-bridge-app/fabric-bridge-app" +) FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" FABRIC_BRIDGE_APP_LOG="/tmp/fabric_bridge_app.log" +FABRIC_ADMIN_PATH="" +FABRIC_BRIDGE_APP_PATH="" -# Detect platform and set default paths -PLATFORM=$(uname -m) -if [[ "$PLATFORM" == "x86_64" ]]; then - FABRIC_ADMIN_PATH="out/debug/standalone/fabric-admin" - FABRIC_BRIDGE_APP_PATH="out/debug/standalone/fabric-bridge-app" -fi +# Function to find a binary +find_binary() { + local choices=("$@") + for path in "${choices[@]}"; do + if [[ -e "$path" ]]; then + echo "$path" + return 0 + fi + done + return 1 +} -# Check if --verbose option is specified +# Parse arguments VERBOSE=false +SPECIFIED_ADMIN_PATH="" +SPECIFIED_BRIDGE_PATH="" + for arg in "$@"; do - if [ "$arg" == "--verbose" ]; then - VERBOSE=true - break - fi + case $arg in + --verbose) + VERBOSE=true + ;; + --admin-path=*) + SPECIFIED_ADMIN_PATH="${arg#*=}" + ;; + --bridge-path=*) + SPECIFIED_BRIDGE_PATH="${arg#*=}" + ;; + esac done -# Stop any running instances and clean up +# Use specified paths if provided +if [[ -n "$SPECIFIED_ADMIN_PATH" ]]; then + if [[ -e "$SPECIFIED_ADMIN_PATH" ]]; then + FABRIC_ADMIN_PATH="$SPECIFIED_ADMIN_PATH" + else + echo >&2 "Specified admin path does not exist: $SPECIFIED_ADMIN_PATH" + exit 1 + fi +else + FABRIC_ADMIN_PATH=$(find_binary "${DEFAULT_ADMIN_CHOICES[@]}") + if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the fabric-admin binary" + exit 1 + fi +fi -if [[ "$PLATFORM" == "x86_64" ]]; then - ./examples/fabric-admin/scripts/stop_fabric_source.sh -elif [[ "$PLATFORM" == "aarch64" ]]; then - ./stop_fabric_source.sh +if [[ -n "$SPECIFIED_BRIDGE_PATH" ]]; then + if [[ -e "$SPECIFIED_BRIDGE_PATH" ]]; then + FABRIC_BRIDGE_APP_PATH="$SPECIFIED_BRIDGE_PATH" + else + echo >&2 "Specified bridge path does not exist: $SPECIFIED_BRIDGE_PATH" + exit 1 + fi +else + FABRIC_BRIDGE_APP_PATH=$(find_binary "${DEFAULT_BRIDGE_CHOICES[@]}") + if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the fabric-bridge-app binary" + exit 1 + fi +fi + +echo "Admin path: $FABRIC_ADMIN_PATH" +echo "Bridge path: $FABRIC_BRIDGE_APP_PATH" + +# Determine the path to stop_fabric_source.sh based on the location of run_fabric_source.sh +RUN_FABRIC_SOURCE_PATH=$(find_binary "$SCRIPT_DIR/run_fabric_source.sh") +if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the run_fabric_source.sh script" + exit 1 +fi +STOP_FABRIC_SOURCE_PATH="${RUN_FABRIC_SOURCE_PATH/run_fabric_source/stop_fabric_source}" + +# Stop any running instances and clean up +if [[ -e "$STOP_FABRIC_SOURCE_PATH" ]]; then + "$STOP_FABRIC_SOURCE_PATH" +else + echo >&2 "Could not find the stop_fabric_source.sh script" + exit 1 fi # Start fabric-bridge-app if available and redirect its output to /dev/null From 495725a328531bb71218aacb4e59cd9f7a2896c5 Mon Sep 17 00:00:00 2001 From: Yufeng Wang Date: Wed, 10 Jul 2024 11:50:09 -0700 Subject: [PATCH 6/6] grep for /fabric-bridge-app without the prefix --- examples/fabric-admin/scripts/stop_fabric_source.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fabric-admin/scripts/stop_fabric_source.sh b/examples/fabric-admin/scripts/stop_fabric_source.sh index 3a9e0f9bbe4f8f..85fff9e3a878cc 100755 --- a/examples/fabric-admin/scripts/stop_fabric_source.sh +++ b/examples/fabric-admin/scripts/stop_fabric_source.sh @@ -1,7 +1,7 @@ #!/bin/bash -FABRIC_ADMIN_PATH="./fabric-admin" -FABRIC_BRIDGE_APP_PATH="./fabric-bridge-app" +FABRIC_ADMIN_PATH="/fabric-admin" +FABRIC_BRIDGE_APP_PATH="/fabric-bridge-app" # Kill fabric-admin if it is running fabric_admin_pid=$(pgrep -f "$FABRIC_ADMIN_PATH")