diff --git a/examples/common/pigweed/BUILD.gn b/examples/common/pigweed/BUILD.gn index c0178e419a1d5d..e523bea380c03f 100644 --- a/examples/common/pigweed/BUILD.gn +++ b/examples/common/pigweed/BUILD.gn @@ -80,6 +80,20 @@ pw_proto_library("button_service") { prefix = "button_service" } +pw_proto_library("fabric_admin_service") { + sources = [ "protos/fabric_admin_service.proto" ] + deps = [ "$dir_pw_protobuf:common_protos" ] + strip_prefix = "protos" + prefix = "fabric_admin_service" +} + +pw_proto_library("fabric_bridge_service") { + sources = [ "protos/fabric_bridge_service.proto" ] + deps = [ "$dir_pw_protobuf:common_protos" ] + strip_prefix = "protos" + prefix = "fabric_bridge_service" +} + pw_proto_library("lighting_service") { sources = [ "protos/lighting_service.proto" ] deps = [ "$dir_pw_protobuf:common_protos" ] diff --git a/examples/common/pigweed/protos/fabric_admin_service.proto b/examples/common/pigweed/protos/fabric_admin_service.proto new file mode 100644 index 00000000000000..e52fd2951ac0d7 --- /dev/null +++ b/examples/common/pigweed/protos/fabric_admin_service.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +import 'pw_protobuf_protos/common.proto'; + +package chip.rpc; + +// Define the message for a synchronized end device with necessary fields +message DeviceInfo { + uint64 node_id = 1; +} + +// Define the response message to convey the status of the operation +message OperationStatus { + bool success = 1; +} + +service FabricAdmin { + rpc OpenCommissioningWindow(DeviceInfo) returns (OperationStatus){} +} + diff --git a/examples/common/pigweed/protos/fabric_bridge_service.proto b/examples/common/pigweed/protos/fabric_bridge_service.proto new file mode 100644 index 00000000000000..5bd4f8efd779e7 --- /dev/null +++ b/examples/common/pigweed/protos/fabric_bridge_service.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +import 'pw_protobuf_protos/common.proto'; + +package chip.rpc; + +// Define the message for a synchronized end device with necessary fields +message SynchronizedDevice { + uint64 node_id = 1; +} + +service FabricBridge { + rpc AddSynchronizedDevice(SynchronizedDevice) returns (pw.protobuf.Empty){} +} + diff --git a/examples/common/pigweed/rpc_console/py/BUILD.gn b/examples/common/pigweed/rpc_console/py/BUILD.gn index a03dc980872739..db9f22fe45fff9 100644 --- a/examples/common/pigweed/rpc_console/py/BUILD.gn +++ b/examples/common/pigweed/rpc_console/py/BUILD.gn @@ -46,6 +46,8 @@ pw_python_package("chip_rpc") { "${chip_root}/examples/common/pigweed:descriptor_service.python", "${chip_root}/examples/common/pigweed:device_service.python", "${chip_root}/examples/common/pigweed:echo_service.python", + "${chip_root}/examples/common/pigweed:fabric_admin_service.python", + "${chip_root}/examples/common/pigweed:fabric_bridge_service.python", "${chip_root}/examples/common/pigweed:lighting_service.python", "${chip_root}/examples/common/pigweed:locking_service.python", "${chip_root}/examples/common/pigweed:ot_cli_service.python", diff --git a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py index 1591722bfdbeab..50f0b030f51725 100644 --- a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py +++ b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py @@ -53,6 +53,8 @@ from descriptor_service import descriptor_service_pb2 from device_service import device_service_pb2 from echo_service import echo_pb2 +from fabric_admin_service import fabric_admin_service_pb2 +from fabric_bridge_service import fabric_bridge_service_pb2 from lighting_service import lighting_service_pb2 from locking_service import locking_service_pb2 from ot_cli_service import ot_cli_service_pb2 @@ -136,6 +138,8 @@ def show_console(device: str, baudrate: int, descriptor_service_pb2, device_service_pb2, echo_pb2, + fabric_admin_service_pb2, + fabric_bridge_service_pb2, lighting_service_pb2, locking_service_pb2, ot_cli_service_pb2, diff --git a/examples/common/pigweed/rpc_services/FabricAdmin.h b/examples/common/pigweed/rpc_services/FabricAdmin.h new file mode 100644 index 00000000000000..5254b9e9054a0c --- /dev/null +++ b/examples/common/pigweed/rpc_services/FabricAdmin.h @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "app/util/attribute-storage.h" +#include "fabric_admin_service/fabric_admin_service.rpc.pb.h" +#include "pigweed/rpc_services/internal/StatusUtils.h" +#include +#include +#include +#include + +namespace chip { +namespace rpc { + +class FabricAdmin : public pw_rpc::nanopb::FabricAdmin::Service +{ +public: + virtual ~FabricAdmin() = default; + + virtual pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response) + { + return pw::Status::Unimplemented(); + } +}; + +} // namespace rpc +} // namespace chip diff --git a/examples/common/pigweed/rpc_services/FabricBridge.h b/examples/common/pigweed/rpc_services/FabricBridge.h new file mode 100644 index 00000000000000..bce32ebd3d99b2 --- /dev/null +++ b/examples/common/pigweed/rpc_services/FabricBridge.h @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "app/util/attribute-storage.h" +#include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h" +#include "pigweed/rpc_services/internal/StatusUtils.h" +#include +#include +#include +#include + +namespace chip { +namespace rpc { + +class FabricBridge : public pw_rpc::nanopb::FabricBridge::Service +{ +public: + virtual ~FabricBridge() = default; + + virtual pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) + { + return pw::Status::Unimplemented(); + } +}; + +} // namespace rpc +} // namespace chip diff --git a/examples/fabric-admin/BUILD.gn b/examples/fabric-admin/BUILD.gn index ddaa33483257b1..ad7eb217f9914c 100644 --- a/examples/fabric-admin/BUILD.gn +++ b/examples/fabric-admin/BUILD.gn @@ -22,10 +22,18 @@ import("${chip_root}/src/lib/core/core.gni") assert(chip_build_tools) +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") + +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") +} + config("config") { include_dirs = [ ".", "${chip_root}/examples/common", + "${chip_root}/examples/platform/linux", "${chip_root}/zzz_generated/app-common/app-common", "${chip_root}/zzz_generated/chip-tool", "${chip_root}/src/lib", @@ -39,6 +47,10 @@ config("config") { } cflags = [ "-Wconversion" ] + + if (chip_enable_pw_rpc) { + defines += [ "PW_RPC_ENABLED" ] + } } static_library("fabric-admin-utils") { @@ -59,6 +71,7 @@ static_library("fabric-admin-utils") { "commands/common/HexConversion.h", "commands/common/RemoteDataModelLogger.cpp", "commands/common/RemoteDataModelLogger.h", + "commands/fabric-sync/FabricSyncCommand.cpp", "commands/pairing/OpenCommissioningWindowCommand.cpp", "commands/pairing/OpenCommissioningWindowCommand.h", "commands/pairing/PairingCommand.cpp", @@ -95,6 +108,40 @@ static_library("fabric-admin-utils") { public_configs = [ ":config" ] + if (chip_enable_pw_rpc) { + defines = [ + "PW_RPC_FABRIC_ADMIN_SERVICE=1", + "PW_RPC_FABRIC_BRIDGE_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/platform/linux/RpcClientProcessor.cpp", + "${chip_root}/examples/platform/linux/RpcClientProcessor.h", + "${chip_root}/examples/platform/linux/system_rpc_server.cc", + "rpc/RpcClient.cpp", + "rpc/RpcClient.h", + "rpc/RpcServer.cpp", + "rpc/RpcServer.h", + ] + + deps += [ + "$dir_pw_hdlc:default_addresses", + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_log", + "$dir_pw_rpc:server", + "$dir_pw_rpc/system_server:facade", + "$dir_pw_rpc/system_server:socket", + "$dir_pw_stream:socket_stream", + "$dir_pw_sync:mutex", + "${chip_root}/config/linux/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:fabric_admin_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:fabric_bridge_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:rpc_services", + ] + + deps += pw_build_LINK_DEPS + } + if (chip_enable_transport_trace) { public_deps += [ "${chip_root}/examples/common/tracing:trace_handlers_decoder" ] diff --git a/examples/fabric-admin/commands/fabric-sync/Commands.h b/examples/fabric-admin/commands/fabric-sync/Commands.h new file mode 100644 index 00000000000000..f2be577065b617 --- /dev/null +++ b/examples/fabric-admin/commands/fabric-sync/Commands.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include +#include + +void registerCommandsFabricSync(Commands & commands, CredentialIssuerCommands * credsIssuerConfig) +{ + const char * clusterName = "FabricSync"; + + commands_list clusterCommands = { + make_unique(credsIssuerConfig), + }; + + commands.RegisterCommandSet(clusterName, clusterCommands, "Commands for fabric synchronization."); +} diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp new file mode 100644 index 00000000000000..c2a1b5df8fb80e --- /dev/null +++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "FabricSyncCommand.h" +#include +#include +#include + +#if defined(PW_RPC_ENABLED) +#include "pw_assert/check.h" +#include "pw_hdlc/decoder.h" +#include "pw_hdlc/default_addresses.h" +#include "pw_hdlc/rpc_channel.h" +#include "pw_rpc/client.h" +#include "pw_stream/socket_stream.h" + +#include +#endif + +using namespace ::chip; + +CHIP_ERROR FabricSyncAddDeviceCommand::RunCommand(NodeId remoteId) +{ +#if defined(PW_RPC_ENABLED) + AddSynchronizedDevice(remoteId); + return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif +} diff --git a/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h new file mode 100644 index 00000000000000..cf739ccfb3a520 --- /dev/null +++ b/examples/fabric-admin/commands/fabric-sync/FabricSyncCommand.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include + +class FabricSyncAddDeviceCommand : public CHIPCommand +{ +public: + FabricSyncAddDeviceCommand(CredentialIssuerCommands * credIssuerCommands) : CHIPCommand("add-device", credIssuerCommands) + { + AddArgument("nodeid", 0, UINT64_MAX, &mNodeId); + } + + /////////// CHIPCommand Interface ///////// + CHIP_ERROR RunCommand() override { return RunCommand(mNodeId); } + + chip::System::Clock::Timeout GetWaitDuration() const override { return chip::System::Clock::Seconds16(1); } + +private: + chip::NodeId mNodeId; + + CHIP_ERROR RunCommand(NodeId remoteId); +}; diff --git a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp index aaf3c36461cffb..c5c844256f5351 100644 --- a/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp +++ b/examples/fabric-admin/commands/interactive/InteractiveCommands.cpp @@ -28,11 +28,18 @@ #include #include +#if defined(PW_RPC_ENABLED) +#include +#endif + +using namespace chip; + +namespace { + constexpr char kInteractiveModePrompt[] = ">>> "; constexpr char kInteractiveModeHistoryFileName[] = "chip_tool_history"; constexpr char kInteractiveModeStopCommand[] = "quit()"; - -namespace { +constexpr uint16_t kRetryIntervalS = 5; // File pointer for the log file FILE * sLogFile = nullptr; @@ -67,7 +74,7 @@ void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, return; } - uint64_t timeMs = chip::System::SystemClock().GetMonotonicMilliseconds64().count(); + uint64_t timeMs = System::SystemClock().GetMonotonicMilliseconds64().count(); uint64_t seconds = timeMs / 1000; uint64_t milliseconds = timeMs % 1000; @@ -82,6 +89,26 @@ void ENFORCE_FORMAT(3, 0) LoggingCallback(const char * module, uint8_t category, funlockfile(sLogFile); } +#if defined(PW_RPC_ENABLED) +void AttemptRpcClientConnect(System::Layer * systemLayer, void * appState) +{ + if (InitRpcClient(kFabricBridgeServerPort) == CHIP_NO_ERROR) + { + ChipLogProgress(NotSpecified, "Connected to Fabric-Bridge"); + } + else + { + ChipLogError(NotSpecified, "Failed to connect to Fabric-Bridge, retry in %d seconds....", kRetryIntervalS); + systemLayer->StartTimer(System::Clock::Seconds16(kRetryIntervalS), AttemptRpcClientConnect, nullptr); + } +} + +void ExecuteDeferredConnect(intptr_t ignored) +{ + AttemptRpcClientConnect(&DeviceLayer::SystemLayer(), nullptr); +} +#endif + } // namespace char * InteractiveStartCommand::GetCommand(char * command) @@ -134,9 +161,13 @@ CHIP_ERROR InteractiveStartCommand::RunCommand() OpenLogFile(mLogFilePath.Value()); // Redirect logs to the custom logging callback - chip::Logging::SetLogRedirectCallback(LoggingCallback); + Logging::SetLogRedirectCallback(LoggingCallback); } +#if defined(PW_RPC_ENABLED) + DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredConnect, 0); +#endif + char * command = nullptr; int status; while (true) @@ -167,7 +198,7 @@ bool InteractiveCommand::ParseCommand(char * command, int * status) // If scheduling the cleanup fails, there is not much we can do. // But if something went wrong while the application is leaving it could be because things have // not been cleaned up properly, so it is still useful to log the failure. - LogErrorOnFailure(chip::DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredCleanups, 0)); + LogErrorOnFailure(DeviceLayer::PlatformMgr().ScheduleWork(ExecuteDeferredCleanups, 0)); return false; } diff --git a/examples/fabric-admin/main.cpp b/examples/fabric-admin/main.cpp index a1002d83170d5b..f5f98cc0d960db 100644 --- a/examples/fabric-admin/main.cpp +++ b/examples/fabric-admin/main.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,20 @@ #include #include +#if defined(PW_RPC_ENABLED) +#include +#endif + +using namespace chip; + +void ApplicationInit() +{ +#if defined(PW_RPC_ENABLED) + InitRpcServer(kFabricAdminServerPort); + ChipLogProgress(NotSpecified, "PW_RPC initialized."); +#endif +} + // ================================================================================ // Main Code // ================================================================================ @@ -45,6 +60,7 @@ int main(int argc, char * argv[]) ExampleCredentialIssuerCommands credIssuerCommands; Commands commands; + registerCommandsFabricSync(commands, &credIssuerCommands); registerCommandsInteractive(commands, &credIssuerCommands); registerCommandsPairing(commands, &credIssuerCommands); registerClusters(commands, &credIssuerCommands); @@ -56,5 +72,7 @@ int main(int argc, char * argv[]) c_args.push_back(const_cast(arg.c_str())); } + ApplicationInit(); + return commands.Run(static_cast(c_args.size()), c_args.data()); } diff --git a/examples/fabric-admin/rpc/RpcClient.cpp b/examples/fabric-admin/rpc/RpcClient.cpp new file mode 100644 index 00000000000000..a1a34d309ba17a --- /dev/null +++ b/examples/fabric-admin/rpc/RpcClient.cpp @@ -0,0 +1,89 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RpcClient.h" +#include "RpcClientProcessor.h" + +#include +#include +#include + +#include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h" +#include "pw_assert/check.h" +#include "pw_function/function.h" +#include "pw_hdlc/decoder.h" +#include "pw_hdlc/default_addresses.h" +#include "pw_hdlc/rpc_channel.h" +#include "pw_rpc/client.h" +#include "pw_stream/socket_stream.h" + +using namespace chip; + +namespace { + +// Constants +constexpr uint32_t kDefaultChannelId = 1; + +// Fabric Bridge Client +rpc::pw_rpc::nanopb::FabricBridge::Client fabricBridgeClient(rpc::client::GetDefaultRpcClient(), kDefaultChannelId); +pw::rpc::NanopbUnaryReceiver<::pw_protobuf_Empty> addSynchronizedDeviceCall; + +// Callback function to be called when the RPC response is received +void OnAddDeviceResponseCompleted(const pw_protobuf_Empty & response, pw::Status status) +{ + if (status.ok()) + { + ChipLogProgress(NotSpecified, "AddSynchronizedDevice RPC call succeeded!"); + } + else + { + ChipLogProgress(NotSpecified, "AddSynchronizedDevice RPC call failed with status: %d\n", status.code()); + } +} + +} // namespace + +CHIP_ERROR InitRpcClient(uint16_t rpcServerPort) +{ + rpc::client::SetRpcServerPort(rpcServerPort); + return rpc::client::StartPacketProcessing(); +} + +CHIP_ERROR AddSynchronizedDevice(chip::NodeId nodeId) +{ + ChipLogProgress(NotSpecified, "AddSynchronizedDevice"); + + if (addSynchronizedDeviceCall.active()) + { + ChipLogError(NotSpecified, "OpenCommissioningWindow is in progress\n"); + return CHIP_ERROR_BUSY; + } + + chip_rpc_SynchronizedDevice device; + device.node_id = nodeId; + + // The RPC will remain active as long as `addSynchronizedDeviceCall` is alive. + addSynchronizedDeviceCall = fabricBridgeClient.AddSynchronizedDevice(device, OnAddDeviceResponseCompleted); + + if (!addSynchronizedDeviceCall.active()) + { + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} diff --git a/examples/fabric-admin/rpc/RpcClient.h b/examples/fabric-admin/rpc/RpcClient.h new file mode 100644 index 00000000000000..efe3c24acc3b23 --- /dev/null +++ b/examples/fabric-admin/rpc/RpcClient.h @@ -0,0 +1,48 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +constexpr uint16_t kFabricBridgeServerPort = 33002; + +/** + * @brief Initializes the RPC client with the specified server port. + * + * This function sets the RPC server port and starts packet processing for the RPC client. + * + * @param rpcServerPort The port number on which the RPC server is running. + * @return CHIP_NO_ERROR on successful initialization, or an appropriate CHIP_ERROR on failure. + */ +CHIP_ERROR InitRpcClient(uint16_t rpcServerPort); + +/** + * @brief Adds a synchronized device to the RPC client. + * + * This function attempts to add a device identified by its `nodeId` to the synchronized device list. + * It logs the progress and checks if an `OpenCommissioningWindow` operation is already in progress. + * If an operation is in progress, it returns `CHIP_ERROR_BUSY`. + * + * @param nodeId The Node ID of the device to be added. + * @return CHIP_ERROR An error code indicating the success or failure of the operation. + * - CHIP_NO_ERROR: The RPC command was successfully sent. + * - CHIP_ERROR_BUSY: Another operation is currently in progress. + * - CHIP_ERROR_INTERNAL: An internal error occurred while activating the RPC call. + */ +CHIP_ERROR AddSynchronizedDevice(chip::NodeId nodeId); diff --git a/examples/fabric-admin/rpc/RpcServer.cpp b/examples/fabric-admin/rpc/RpcServer.cpp new file mode 100644 index 00000000000000..b3cbdcea05409a --- /dev/null +++ b/examples/fabric-admin/rpc/RpcServer.cpp @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pw_rpc/server.h" +#include "pw_rpc_system_server/rpc_server.h" +#include "pw_rpc_system_server/socket.h" + +#include +#include + +#if defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE +#include "pigweed/rpc_services/FabricAdmin.h" +#endif + +namespace { + +#if defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE +class FabricAdmin final : public chip::rpc::FabricAdmin +{ +public: + pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response) override + { + chip::NodeId nodeId = request.node_id; + ChipLogProgress(NotSpecified, "Received OpenCommissioningWindow request: 0x%lx", nodeId); + response.success = false; + + return pw::OkStatus(); + } +}; + +FabricAdmin fabric_admin_service; +#endif // defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE + +void RegisterServices(pw::rpc::Server & server) +{ +#if defined(PW_RPC_FABRIC_ADMIN_SERVICE) && PW_RPC_FABRIC_ADMIN_SERVICE + server.RegisterService(fabric_admin_service); +#endif +} + +} // namespace + +void RunRpcService() +{ + pw::rpc::system_server::Init(); + RegisterServices(pw::rpc::system_server::Server()); + pw::rpc::system_server::Start(); +} + +void InitRpcServer(uint16_t rpcServerPort) +{ + pw::rpc::system_server::set_socket_port(rpcServerPort); + std::thread rpc_service(RunRpcService); + rpc_service.detach(); +} diff --git a/examples/fabric-admin/rpc/RpcServer.h b/examples/fabric-admin/rpc/RpcServer.h new file mode 100644 index 00000000000000..bc03bc0ac4abd3 --- /dev/null +++ b/examples/fabric-admin/rpc/RpcServer.h @@ -0,0 +1,23 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +constexpr uint16_t kFabricAdminServerPort = 33001; + +void InitRpcServer(uint16_t rpcServerPort); diff --git a/examples/fabric-admin/with_pw_rpc.gni b/examples/fabric-admin/with_pw_rpc.gni new file mode 100644 index 00000000000000..abb9ac65f27e78 --- /dev/null +++ b/examples/fabric-admin/with_pw_rpc.gni @@ -0,0 +1,42 @@ +# Copyright (c) 2024 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# add this gni as import in your build args to use pigweed in the example +# 'import("//with_pw_rpc.gni")' + +import("//build_overrides/chip.gni") + +import("${chip_root}/config/standalone/args.gni") + +import("//build_overrides/pigweed.gni") + +pw_log_BACKEND = "$dir_pw_log_basic" +pw_assert_BACKEND = "$dir_pw_assert_log:check_backend" +pw_sys_io_BACKEND = "$dir_pw_sys_io_stdio" +pw_trace_BACKEND = "$dir_pw_trace_tokenized" +pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main" +pw_rpc_system_server_BACKEND = "${chip_root}/config/linux/lib/pw_rpc:pw_rpc" +dir_pw_third_party_nanopb = "${chip_root}/third_party/nanopb/repo" +pw_chrono_SYSTEM_CLOCK_BACKEND = "$dir_pw_chrono_stl:system_clock" +pw_sync_MUTEX_BACKEND = "$dir_pw_sync_stl:mutex_backend" +pw_thread_YIELD_BACKEND = "$dir_pw_thread_stl:yield" +pw_thread_SLEEP_BACKEND = "$dir_pw_thread_stl:sleep" + +pw_build_LINK_DEPS = [ + "$dir_pw_assert:impl", + "$dir_pw_log:impl", +] + +chip_enable_pw_rpc = true +chip_use_pw_logging = true diff --git a/examples/fabric-bridge-app/linux/BUILD.gn b/examples/fabric-bridge-app/linux/BUILD.gn index ea7e6e0b31b331..3e82f044c047f9 100644 --- a/examples/fabric-bridge-app/linux/BUILD.gn +++ b/examples/fabric-bridge-app/linux/BUILD.gn @@ -16,8 +16,20 @@ import("//build_overrides/chip.gni") import("${chip_root}/build/chip/tools.gni") +import("//with_pw_rpc.gni") + assert(chip_build_tools) +declare_args() { + bridge_enable_pw_rpc = false +} + +if (bridge_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_build/target_types.gni") + import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") +} + executable("fabric-bridge-app") { sources = [ "${chip_root}/examples/fabric-bridge-app/fabric-bridge-common/include/CHIPProjectAppConfig.h", @@ -38,6 +50,45 @@ executable("fabric-bridge-app") { include_dirs = [ "include" ] + if (bridge_enable_pw_rpc) { + defines = [ + "PW_RPC_FABRIC_ADMIN_SERVICE=1", + "PW_RPC_FABRIC_BRIDGE_SERVICE=1", + ] + + sources += [ + "${chip_root}/examples/platform/linux/RpcClientProcessor.cpp", + "${chip_root}/examples/platform/linux/RpcClientProcessor.h", + "${chip_root}/examples/platform/linux/system_rpc_server.cc", + "RpcClient.cpp", + "RpcServer.cpp", + "include/RpcClient.h", + "include/RpcServer.h", + ] + + deps += [ + "$dir_pw_hdlc:default_addresses", + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_log", + "$dir_pw_rpc:server", + "$dir_pw_rpc/system_server:facade", + "$dir_pw_rpc/system_server:socket", + "$dir_pw_stream:socket_stream", + "$dir_pw_sync:mutex", + "${chip_root}/config/linux/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/common/pigweed:fabric_admin_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:fabric_bridge_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:rpc_services", + ] + + deps += pw_build_LINK_DEPS + + include_dirs += [ + "${chip_root}/examples/common", + "${chip_root}/examples/platform/linux", + ] + } + output_dir = root_out_dir } diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp new file mode 100644 index 00000000000000..c09a447cff28c9 --- /dev/null +++ b/examples/fabric-bridge-app/linux/RpcClient.cpp @@ -0,0 +1,88 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RpcClient.h" +#include "RpcClientProcessor.h" + +#include +#include +#include + +#include "fabric_admin_service/fabric_admin_service.rpc.pb.h" +#include "pw_assert/check.h" +#include "pw_hdlc/decoder.h" +#include "pw_hdlc/default_addresses.h" +#include "pw_hdlc/rpc_channel.h" +#include "pw_rpc/client.h" +#include "pw_stream/socket_stream.h" + +using namespace chip; + +namespace { + +// Constants +constexpr uint32_t kDefaultChannelId = 1; + +// Fabric Admin Client +rpc::pw_rpc::nanopb::FabricAdmin::Client fabricAdminClient(rpc::client::GetDefaultRpcClient(), kDefaultChannelId); +pw::rpc::NanopbUnaryReceiver<::chip_rpc_OperationStatus> openCommissioningWindowCall; + +// Callback function to be called when the RPC response is received +void OnOpenCommissioningWindowCompleted(const chip_rpc_OperationStatus & response, pw::Status status) +{ + if (status.ok()) + { + ChipLogProgress(NotSpecified, "OpenCommissioningWindow received operation status: %d", response.success); + } + else + { + ChipLogProgress(NotSpecified, "OpenCommissioningWindow RPC call failed with status: %d\n", status.code()); + } +} + +} // namespace + +CHIP_ERROR InitRpcClient(uint16_t rpcServerPort) +{ + rpc::client::SetRpcServerPort(rpcServerPort); + return rpc::client::StartPacketProcessing(); +} + +CHIP_ERROR OpenCommissioningWindow(NodeId nodeId) +{ + ChipLogProgress(NotSpecified, "OpenCommissioningWindow\n"); + + if (openCommissioningWindowCall.active()) + { + ChipLogError(NotSpecified, "OpenCommissioningWindow is in progress\n"); + return CHIP_ERROR_BUSY; + } + + chip_rpc_DeviceInfo device; + device.node_id = nodeId; + + // The RPC will remain active as long as `openCommissioningWindowCall` is alive. + openCommissioningWindowCall = fabricAdminClient.OpenCommissioningWindow(device, OnOpenCommissioningWindowCompleted); + + if (!openCommissioningWindowCall.active()) + { + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp new file mode 100644 index 00000000000000..c971811b193016 --- /dev/null +++ b/examples/fabric-bridge-app/linux/RpcServer.cpp @@ -0,0 +1,68 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pw_rpc/server.h" +#include "pw_rpc_system_server/rpc_server.h" +#include "pw_rpc_system_server/socket.h" + +#include +#include + +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE +#include "pigweed/rpc_services/FabricBridge.h" +#endif + +namespace { + +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE +class FabricBridge final : public chip::rpc::FabricBridge +{ +public: + pw::Status AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & request, pw_protobuf_Empty & response) override + { + chip::NodeId nodeId = request.node_id; + ChipLogProgress(NotSpecified, "Received AddSynchronizedDevice: " ChipLogFormatX64, ChipLogValueX64(nodeId)); + return pw::OkStatus(); + } +}; + +FabricBridge fabric_bridge_service; +#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE + +void RegisterServices(pw::rpc::Server & server) +{ +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE + server.RegisterService(fabric_bridge_service); +#endif +} + +} // namespace + +void RunRpcService() +{ + pw::rpc::system_server::Init(); + RegisterServices(pw::rpc::system_server::Server()); + pw::rpc::system_server::Start(); +} + +void InitRpcServer(uint16_t rpcServerPort) +{ + pw::rpc::system_server::set_socket_port(rpcServerPort); + std::thread rpc_service(RunRpcService); + rpc_service.detach(); +} diff --git a/examples/fabric-bridge-app/linux/include/RpcClient.h b/examples/fabric-bridge-app/linux/include/RpcClient.h new file mode 100644 index 00000000000000..bd424e9d275910 --- /dev/null +++ b/examples/fabric-bridge-app/linux/include/RpcClient.h @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +constexpr uint16_t kFabricAdminServerPort = 33001; + +/** + * Initializes the RPC client by setting the server port and starting packet processing. + * + * @param rpcServerPort The port number of the RPC server. + * @return CHIP_ERROR An error code indicating the success or failure of the initialization process. + * - CHIP_NO_ERROR: Initialization was successful. + * - Other error codes indicating specific failure reasons. + */ +CHIP_ERROR InitRpcClient(uint16_t rpcServerPort); + +/** + * Opens a commissioning window for a specified node. + * + * @param nodeId The identifier of the node for which the commissioning window should be opened. + * @return CHIP_ERROR An error code indicating the success or failure of the operation. + * - CHIP_NO_ERROR: The RPC command was successfully sent. + * - CHIP_ERROR_BUSY: Another commissioning window is currently in progress. + * - CHIP_ERROR_INTERNAL: An internal error occurred. + */ +CHIP_ERROR OpenCommissioningWindow(chip::NodeId nodeId); diff --git a/examples/fabric-bridge-app/linux/include/RpcServer.h b/examples/fabric-bridge-app/linux/include/RpcServer.h new file mode 100644 index 00000000000000..f86858b19bdfe3 --- /dev/null +++ b/examples/fabric-bridge-app/linux/include/RpcServer.h @@ -0,0 +1,23 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +constexpr uint16_t kFabricBridgeServerPort = 33002; + +void InitRpcServer(uint16_t rpcServerPort); diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp index 600d4a2a376d55..47670561430c90 100644 --- a/examples/fabric-bridge-app/linux/main.cpp +++ b/examples/fabric-bridge-app/linux/main.cpp @@ -25,19 +25,26 @@ #include #include +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE +#include "RpcClient.h" +#include "RpcServer.h" +#endif + #include #include #include using namespace chip; -#define POLL_INTERVAL_MS (100) #define ZCL_DESCRIPTOR_CLUSTER_REVISION (1u) #define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_CLUSTER_REVISION (2u) #define ZCL_BRIDGED_DEVICE_BASIC_INFORMATION_FEATURE_MAP (0u) namespace { +constexpr uint16_t kPollIntervalMs = 100; +constexpr uint16_t kRetryIntervalS = 3; + bool KeyboardHit() { int bytesWaiting; @@ -57,13 +64,38 @@ void BridgePollingThread() ChipLogProgress(NotSpecified, "Exiting....."); exit(0); } +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE + else if (ch == 'o') + { + CHIP_ERROR err = OpenCommissioningWindow(0x1234); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Failed to call OpenCommissioningWindow RPC: %" CHIP_ERROR_FORMAT, err.Format()); + } + } +#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE continue; } // Sleep to avoid tight loop reading commands - usleep(POLL_INTERVAL_MS * 1000); + usleep(kPollIntervalMs * 1000); + } +} + +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE +void AttemptRpcClientConnect(System::Layer * systemLayer, void * appState) +{ + if (InitRpcClient(kFabricAdminServerPort) == CHIP_NO_ERROR) + { + ChipLogProgress(NotSpecified, "Connected to Fabric-Admin"); + } + else + { + ChipLogError(NotSpecified, "Failed to connect to Fabric-Admin, retry in %d seconds....", kRetryIntervalS); + systemLayer->StartTimer(System::Clock::Seconds16(kRetryIntervalS), AttemptRpcClientConnect, nullptr); } } +#endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE DeviceManager gDeviceManager; @@ -71,6 +103,11 @@ DeviceManager gDeviceManager; void ApplicationInit() { +#if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE + InitRpcServer(kFabricBridgeServerPort); + AttemptRpcClientConnect(&DeviceLayer::SystemLayer(), nullptr); +#endif + // Start a thread for bridge polling std::thread pollingThread(BridgePollingThread); pollingThread.detach(); diff --git a/examples/fabric-bridge-app/linux/with_pw_rpc.gni b/examples/fabric-bridge-app/linux/with_pw_rpc.gni new file mode 100644 index 00000000000000..e1bd567cf22db2 --- /dev/null +++ b/examples/fabric-bridge-app/linux/with_pw_rpc.gni @@ -0,0 +1,42 @@ +# Copyright (c) 2024 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# add this gni as import in your build args to use pigweed in the example +# 'import("//with_pw_rpc.gni")' + +import("//build_overrides/chip.gni") + +import("${chip_root}/config/standalone/args.gni") + +import("//build_overrides/pigweed.gni") + +pw_log_BACKEND = "$dir_pw_log_basic" +pw_assert_BACKEND = "$dir_pw_assert_log:check_backend" +pw_sys_io_BACKEND = "$dir_pw_sys_io_stdio" +pw_trace_BACKEND = "$dir_pw_trace_tokenized" +pw_unit_test_MAIN = "$dir_pw_unit_test:logging_main" +pw_rpc_system_server_BACKEND = "${chip_root}/config/linux/lib/pw_rpc:pw_rpc" +dir_pw_third_party_nanopb = "${chip_root}/third_party/nanopb/repo" +pw_chrono_SYSTEM_CLOCK_BACKEND = "$dir_pw_chrono_stl:system_clock" +pw_sync_MUTEX_BACKEND = "$dir_pw_sync_stl:mutex_backend" +pw_thread_YIELD_BACKEND = "$dir_pw_thread_stl:yield" +pw_thread_SLEEP_BACKEND = "$dir_pw_thread_stl:sleep" + +pw_build_LINK_DEPS = [ + "$dir_pw_assert:impl", + "$dir_pw_log:impl", +] + +chip_use_pw_logging = true +bridge_enable_pw_rpc = true diff --git a/examples/platform/linux/RpcClientProcessor.cpp b/examples/platform/linux/RpcClientProcessor.cpp new file mode 100644 index 00000000000000..a2cbe5694f531d --- /dev/null +++ b/examples/platform/linux/RpcClientProcessor.cpp @@ -0,0 +1,124 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "RpcClientProcessor.h" + +#include +#include + +#include "pw_hdlc/decoder.h" +#include "pw_hdlc/default_addresses.h" +#include "pw_hdlc/rpc_channel.h" +#include "pw_stream/socket_stream.h" + +namespace chip { +namespace rpc { +namespace client { +namespace { + +// Constants +constexpr size_t kMaxTransmissionUnit = 256; +constexpr uint32_t kDefaultChannelId = 1; +const char * kDefaultRpcServerAddress = "127.0.0.1"; + +// RPC Stream and Channel Setup +pw::stream::SocketStream rpcSocketStream; +pw::hdlc::RpcChannelOutput hdlcChannelOutput(rpcSocketStream, pw::hdlc::kDefaultRpcAddress, "HDLC channel"); +pw::rpc::Channel channels[] = { pw::rpc::Channel::Create<1>(&hdlcChannelOutput) }; +pw::rpc::Client rpcClient(channels); + +// RPC Stream and Channel Setup +uint16_t rpcServerPort = 0; +const char * rpcServerAddress = kDefaultRpcServerAddress; + +// Function to process incoming packets +void ProcessPackets() +{ + std::array inputBuf; + pw::hdlc::Decoder decoder(inputBuf); + + while (true) + { + std::array data; + auto ret = rpcSocketStream.Read(data); + if (!ret.ok()) + { + if (ret.status() == pw::Status::OutOfRange()) + { + // Handle remote disconnect + rpcSocketStream.Close(); + return; + } + continue; + } + + for (std::byte byte : ret.value()) + { + auto result = decoder.Process(byte); + if (!result.ok()) + { + // Wait for more bytes that form a complete packet + continue; + } + pw::hdlc::Frame & frame = result.value(); + if (frame.address() != pw::hdlc::kDefaultRpcAddress) + { + // Wrong address; ignore the packet + continue; + } + + rpcClient.ProcessPacket(frame.data()).IgnoreError(); + } + } +} + +} // namespace + +void SetRpcServerAddress(const char * address) +{ + rpcServerAddress = address; +} + +void SetRpcServerPort(uint16_t port) +{ + rpcServerPort = port; +} + +pw::rpc::Client & GetDefaultRpcClient() +{ + return rpcClient; +} + +CHIP_ERROR StartPacketProcessing() +{ + if (rpcSocketStream.Connect(rpcServerAddress, rpcServerPort) != PW_STATUS_OK) + { + // Handle connection error + return CHIP_ERROR_NOT_CONNECTED; + } + + // Start a thread to process incoming packets + std::thread packet_processor(ProcessPackets); + packet_processor.detach(); + + return CHIP_NO_ERROR; +} + +} // namespace client +} // namespace rpc +} // namespace chip diff --git a/examples/platform/linux/RpcClientProcessor.h b/examples/platform/linux/RpcClientProcessor.h new file mode 100644 index 00000000000000..f2305df7aa046c --- /dev/null +++ b/examples/platform/linux/RpcClientProcessor.h @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "pw_rpc/client.h" +#include +#include + +namespace chip { +namespace rpc { +namespace client { + +void SetRpcServerAddress(const char * address); +void SetRpcServerPort(uint16_t port); +pw::rpc::Client & GetDefaultRpcClient(); +CHIP_ERROR StartPacketProcessing(); + +} // namespace client +} // namespace rpc +} // namespace chip