From 1033744821d7e5002bab9a2cf0a0bfef473f1156 Mon Sep 17 00:00:00 2001 From: Albert Chaulk Date: Wed, 24 Aug 2022 15:50:34 -0400 Subject: [PATCH] Add PW RPC --- examples/bridge-app/bridge-common/BUILD.gn | 13 +++ .../protos/bridge_service.options | 2 + .../bridge-common/protos/bridge_service.proto | 33 +++++++ examples/bridge-app/linux/BUILD.gn | 60 ++++++++++++- examples/bridge-app/linux/Backend.cpp | 62 +++++++++++++ examples/bridge-app/linux/Device.cpp | 2 +- .../bridge-app/linux/UserInputBackend.cpp | 25 +----- examples/bridge-app/linux/bridge_service.cpp | 90 +++++++++++++++++++ examples/bridge-app/linux/bridge_service.h | 45 ++++++++++ examples/bridge-app/linux/include/Backend.h | 8 ++ examples/bridge-app/linux/include/Device.h | 4 +- examples/bridge-app/linux/main.cpp | 14 +++ examples/bridge-app/linux/with_pw_rpc.gni | 44 +++++++++ 13 files changed, 374 insertions(+), 28 deletions(-) create mode 100644 examples/bridge-app/bridge-common/protos/bridge_service.options create mode 100644 examples/bridge-app/bridge-common/protos/bridge_service.proto create mode 100644 examples/bridge-app/linux/Backend.cpp create mode 100644 examples/bridge-app/linux/bridge_service.cpp create mode 100644 examples/bridge-app/linux/bridge_service.h create mode 100644 examples/bridge-app/linux/with_pw_rpc.gni diff --git a/examples/bridge-app/bridge-common/BUILD.gn b/examples/bridge-app/bridge-common/BUILD.gn index ac3aa414ae3791..1e7c59526db959 100644 --- a/examples/bridge-app/bridge-common/BUILD.gn +++ b/examples/bridge-app/bridge-common/BUILD.gn @@ -15,6 +15,7 @@ import("//build_overrides/chip.gni") import("${chip_root}/src/app/chip_data_model.gni") +import("${chip_root}/examples/common/pigweed/pigweed_rpcs.gni") chip_data_model("bridge-common") { zap_file = "bridge-app.zap" @@ -25,3 +26,15 @@ chip_data_model("bridge-common") { # TODO: the definition of DYNAMIC_ENDPOINT_COUNT needs find a common home! cflags = [ "-DDYNAMIC_ENDPOINT_COUNT=16" ] } + +if (chip_enable_pw_rpc) { + import("//build_overrides/pigweed.gni") + import("$dir_pw_protobuf_compiler/proto.gni") + + pw_proto_library("bridge_service") { + sources = [ "protos/bridge_service.proto" ] + inputs = [ "protos/bridge_service.options" ] + strip_prefix = "protos" + prefix = "bridge_service" + } +} diff --git a/examples/bridge-app/bridge-common/protos/bridge_service.options b/examples/bridge-app/bridge-common/protos/bridge_service.options new file mode 100644 index 00000000000000..11b8dfc097fba5 --- /dev/null +++ b/examples/bridge-app/bridge-common/protos/bridge_service.options @@ -0,0 +1,2 @@ +chip.rpc.bridge.AddDevice.device_types max_count:32 +chip.rpc.bridge.AddDevice.clusters max_count:64 diff --git a/examples/bridge-app/bridge-common/protos/bridge_service.proto b/examples/bridge-app/bridge-common/protos/bridge_service.proto new file mode 100644 index 00000000000000..1917ba1f0c4c00 --- /dev/null +++ b/examples/bridge-app/bridge-common/protos/bridge_service.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; + +package chip.rpc.bridge; + +message Empty {} + +message RemoveDevice { + uint32 id = 1; +} + +message Cluster { + uint32 cluster_id = 1; +} + +message DeviceType { + uint32 id = 1; + uint32 version = 2; +} + +message AddDevice { + repeated Cluster clusters = 1; + repeated DeviceType device_types = 2; + uint32 parent_endpoint = 3; +} + +message AddDeviceResponse { + uint32 id = 1; +} + +service Bridge { + rpc Add(AddDevice) returns (AddDeviceResponse){}; + rpc Remove(RemoveDevice) returns (Empty){}; +} diff --git a/examples/bridge-app/linux/BUILD.gn b/examples/bridge-app/linux/BUILD.gn index 2bf6bf7988f91b..da54ebe03f2a76 100644 --- a/examples/bridge-app/linux/BUILD.gn +++ b/examples/bridge-app/linux/BUILD.gn @@ -16,6 +16,14 @@ import("//build_overrides/build.gni") import("//build_overrides/chip.gni") import("${chip_root}/build/chip/tools.gni") +import("${chip_root}/src/app/common_flags.gni") + +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") +} assert(chip_build_tools) @@ -23,7 +31,8 @@ action("chip-bridge-codegen") { script = "${chip_root}/scripts/codegen.py" sources = [ "${chip_root}/examples/bridge-app/bridge-common/bridge-app.matter" ] - outputs = [ "$target_gen_dir/cpp/BridgeClustersImpl.h" ] + # Also several other files, but this is sufficient for dependency purposes. + outputs = [ "$target_gen_dir/bridge/BridgeClustersImpl.h" ] args = [ "--generator", @@ -41,6 +50,7 @@ executable("chip-bridge-app") { sources = [ "${chip_root}/examples/bridge-app/linux/bridged-actions-stub.cpp", "${chip_root}/examples/tv-app/tv-common/include/CHIPProjectAppConfig.h", + "Backend.cpp", "Clusters.cpp", "Device.cpp", "DynamicDevice.cpp", @@ -58,13 +68,57 @@ executable("chip-bridge-app") { "${chip_root}/src/lib", ] - cflags = [ "-Wconversion" ] - include_dirs = [ "include", target_gen_dir, ] + if (chip_enable_pw_rpc) { + defines = [ + "PW_RPC_ENABLED", + "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_DESCRIPTOR_SERVICE=1", + "PW_RPC_DEVICE_SERVICE=1", + "PW_RPC_TRACING_SERVICE=1" + ] + + sources += [ + "${chip_root}/examples/platform/linux/Rpc.cpp", + "${chip_root}/examples/platform/linux/system_rpc_server.cc", + "bridge_service.cpp" + ] + + deps += [ + "$dir_pw_hdlc:pw_rpc", + "$dir_pw_hdlc:rpc_channel_output", + "$dir_pw_log", + "$dir_pw_rpc:server", + "$dir_pw_rpc/system_server:facade", + "$dir_pw_stream:socket_stream", + "$dir_pw_stream:sys_io_stream", + "$dir_pw_sync:mutex", + "$dir_pw_trace", + "$dir_pw_trace_tokenized", + "$dir_pw_trace_tokenized:trace_rpc_service", + "${chip_root}/config/linux/lib/pw_rpc:pw_rpc", + "${chip_root}/examples/bridge-app/bridge-common:bridge_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:descriptor_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:lighting_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:rpc_services", + ] + + deps += pw_build_LINK_DEPS + + include_dirs += [ "${chip_root}/examples/common" ] + } else { + # The system_rpc_server.cc file is in pigweed and doesn't compile with + # -Wconversion, remove check for RPC build only. + cflags = [ "-Wconversion" ] + } + output_dir = root_out_dir } diff --git a/examples/bridge-app/linux/Backend.cpp b/examples/bridge-app/linux/Backend.cpp new file mode 100644 index 00000000000000..4cd3b6503a2cd5 --- /dev/null +++ b/examples/bridge-app/linux/Backend.cpp @@ -0,0 +1,62 @@ +/* + * + * Copyright (c) 2022 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 "Backend.h" + +#include "main.h" + +std::vector> g_devices; +std::vector> g_device_impls; + +bool RemoveDeviceAt(uint32_t index) +{ + if (index >= g_devices.size() || !g_devices[index]) + { + return false; + } + + RemoveDeviceEndpoint(g_devices[index].get()); + + for (auto & room : gRooms) + room.RemoveEndpoint(g_devices[index]->GetEndpointId()); + + g_devices[index] = nullptr; + g_device_impls[index] = nullptr; + + return true; +} + +int AddDevice(std::unique_ptr device) +{ + auto dev = std::make_unique(device->CreateDevice()); + int ep = AddDeviceEndpoint(dev.get()); + if (ep < 0) + { + return -1; + } + + size_t index = (size_t) ep; + if (g_devices.size() <= index) + { + g_devices.resize(index + 1); + g_device_impls.resize(index + 1); + } + g_devices[index] = std::move(dev); + g_device_impls[index] = std::move(device); + return ep; +} diff --git a/examples/bridge-app/linux/Device.cpp b/examples/bridge-app/linux/Device.cpp index 31d8b5c960fb0c..268feca462e9a0 100644 --- a/examples/bridge-app/linux/Device.cpp +++ b/examples/bridge-app/linux/Device.cpp @@ -42,7 +42,7 @@ void Device::SetEndpointId(chip::EndpointId id) const char * Device::GetName() { - return mDeviceName; + return mDeviceName.c_str(); } void Device::SetName(const char * name) diff --git a/examples/bridge-app/linux/UserInputBackend.cpp b/examples/bridge-app/linux/UserInputBackend.cpp index 6c3eb1e1a2924b..8b613cbd976e59 100644 --- a/examples/bridge-app/linux/UserInputBackend.cpp +++ b/examples/bridge-app/linux/UserInputBackend.cpp @@ -15,8 +15,6 @@ namespace { -std::vector> g_devices; -std::vector> g_device_impls; std::unique_ptr g_pending; // Pseudo-index representing the device being built. @@ -169,8 +167,8 @@ void FinishDevice(const std::vector & tokens) for (auto * c : g_pending->clusters()) c->SetCallback(&g_write_cb); - auto dev = std::make_unique(g_pending->CreateDevice()); - int ep = AddDeviceEndpoint(dev.get()); + int ep = AddDevice(std::move(g_pending)); + if (ep < 0) { printf("Failed to add device\n"); @@ -178,14 +176,6 @@ void FinishDevice(const std::vector & tokens) else { printf("Added device at index %d\n", ep); - size_t index = (size_t) ep; - if (g_devices.size() <= index) - { - g_devices.resize(index + 1); - g_device_impls.resize(index + 1); - } - g_devices[index] = std::move(dev); - g_device_impls[index] = std::move(g_pending); } } @@ -198,19 +188,10 @@ void RemoveDevice(const std::vector & tokens) printf("Error: %s.\nExpected index of a device\n", err); return; } - if (index >= g_devices.size()) + if (!RemoveDeviceAt(index)) { printf("%d is an invalid index\n", index); - return; } - - RemoveDeviceEndpoint(g_devices[index].get()); - - for (auto & room : gRooms) - room.RemoveEndpoint(g_devices[index]->GetEndpointId()); - - g_devices[index] = nullptr; - g_device_impls[index] = nullptr; } void AddType(const std::vector & tokens) diff --git a/examples/bridge-app/linux/bridge_service.cpp b/examples/bridge-app/linux/bridge_service.cpp new file mode 100644 index 00000000000000..af096e8371a76b --- /dev/null +++ b/examples/bridge-app/linux/bridge_service.cpp @@ -0,0 +1,90 @@ +/* + * + * Copyright (c) 2022 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 "bridge_service.h" + +#include "Backend.h" +#include "main.h" + +namespace chip { +namespace rpc { + +namespace { +const clusters::ClusterInfo * FindCluster(uint32_t id) +{ + for (const auto & cluster : clusters::kKnownClusters) + { + if (id == cluster.id) + return &cluster; + } + return nullptr; +} +} // namespace + +::pw::Status Bridge::Add(const ::chip_rpc_bridge_AddDevice & request, ::chip_rpc_bridge_AddDeviceResponse & response) +{ + std::unique_ptr pending; + + pending = std::make_unique(); + pending->SetParentEndpointId(request.parent_endpoint); + + for (pb_size_t i = 0; i < request.clusters_count; i++) + { + const chip_rpc_bridge_Cluster & c = request.clusters[i]; + + auto cluster = FindCluster(c.cluster_id); + if (!cluster) + { + return pw::Status::InvalidArgument(); + } + + std::unique_ptr obj(cluster->ctor(::operator new(cluster->size))); + DynamicAttributeList dynamic_attrs; + auto attrs = obj->GetAllAttributes(); + for (auto & attr : attrs) + dynamic_attrs.Add(attr); + pending->AddCluster(std::move(obj), dynamic_attrs, nullptr, nullptr); + } + + for (pb_size_t i = 0; i < request.device_types_count; i++) + { + EmberAfDeviceType devType = { (uint16_t) request.device_types[i].id, (uint8_t) request.device_types[i].version }; + pending->AddDeviceType(devType); + } + + int ret = AddDevice(std::move(pending)); + if (ret < 0) + { + return pw::Status::Aborted(); + } + response.id = ret; + return pw::OkStatus(); +} + +::pw::Status Bridge::Remove(const ::chip_rpc_bridge_RemoveDevice & request, ::chip_rpc_bridge_Empty & response) +{ + if (!RemoveDeviceAt(request.id)) + { + return pw::Status::NotFound(); + } + + return pw::OkStatus(); +} + +} // namespace rpc +} // namespace chip diff --git a/examples/bridge-app/linux/bridge_service.h b/examples/bridge-app/linux/bridge_service.h new file mode 100644 index 00000000000000..d2e05416a0a74f --- /dev/null +++ b/examples/bridge-app/linux/bridge_service.h @@ -0,0 +1,45 @@ +/* + * + * Copyright (c) 2022 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 "bridge_service/bridge_service.rpc.pb.h" +#include "pigweed/rpc_services/internal/StatusUtils.h" +#include +#include +#include +#include + +namespace chip { +namespace rpc { + +class Bridge : public bridge::pw_rpc::nanopb::Bridge::Service +{ +public: + Bridge() = default; + + virtual ~Bridge() = default; + + ::pw::Status Add(const ::chip_rpc_bridge_AddDevice & request, ::chip_rpc_bridge_AddDeviceResponse & response); + + ::pw::Status Remove(const ::chip_rpc_bridge_RemoveDevice & request, ::chip_rpc_bridge_Empty & response); +}; + +} // namespace rpc +} // namespace chip diff --git a/examples/bridge-app/linux/include/Backend.h b/examples/bridge-app/linux/include/Backend.h index 6e5ca8ee9c7e34..5058eeea69b5f5 100644 --- a/examples/bridge-app/linux/include/Backend.h +++ b/examples/bridge-app/linux/include/Backend.h @@ -18,4 +18,12 @@ #pragma once +#include "DynamicDevice.h" + void StartUserInput(); + +extern std::vector> g_devices; +extern std::vector> g_device_impls; + +bool RemoveDeviceAt(uint32_t index); +int AddDevice(std::unique_ptr device); diff --git a/examples/bridge-app/linux/include/Device.h b/examples/bridge-app/linux/include/Device.h index 2a127694c348f4..b65186b7fff8c0 100644 --- a/examples/bridge-app/linux/include/Device.h +++ b/examples/bridge-app/linux/include/Device.h @@ -18,8 +18,8 @@ #pragma once -#include #include +#include #include #include @@ -70,7 +70,7 @@ class Device chip::Span mClusterImpl; chip::Span mDeviceTypeList; EmberAfEndpointType mEndpointType; - const char * mDeviceName = ""; + std::string mDeviceName; }; class Action diff --git a/examples/bridge-app/linux/main.cpp b/examples/bridge-app/linux/main.cpp index 3f8c42104c05c3..9cdbc6af4b3144 100644 --- a/examples/bridge-app/linux/main.cpp +++ b/examples/bridge-app/linux/main.cpp @@ -50,6 +50,14 @@ #include "AppMain.h" +#ifdef PW_RPC_ENABLED +#include "bridge_service.h" +#include "Rpc.h" +#include "pw_rpc_system_server/rpc_server.h" +static chip::rpc::Bridge bridge_service; +#endif + + #include #include #include @@ -163,6 +171,12 @@ chip::Span GetActionListInfo(chip::EndpointId parentId) void ApplicationInit() { +#ifdef PW_RPC_ENABLED + chip::rpc::Init(); + + pw::rpc::system_server::Server().RegisterService(bridge_service); +#endif + clusters::BridgeRegisterAllAttributeOverrides(); gFirstDynamicEndpointId = static_cast( diff --git a/examples/bridge-app/linux/with_pw_rpc.gni b/examples/bridge-app/linux/with_pw_rpc.gni new file mode 100644 index 00000000000000..756a93469bf072 --- /dev/null +++ b/examples/bridge-app/linux/with_pw_rpc.gni @@ -0,0 +1,44 @@ +# Copyright (c) 2021 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") + +cpp_standard = "gnu++17" + +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_span_ENABLE_STD_SPAN_POLYFILL = false + +pw_build_LINK_DEPS = [ + "$dir_pw_assert:impl", + "$dir_pw_log:impl", +] + +chip_enable_pw_rpc = true +chip_build_pw_trace_lib = true +chip_use_pw_logging = true