blob: 9964dc6767b67ece3805af32e74e0b92a4a21137 [file] [log] [blame]
/*
*
* 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 <chrono>
#include <condition_variable>
#include <mutex>
#include "fabric_bridge_service/fabric_bridge_service.pb.h"
#include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h"
using namespace chip;
namespace {
// Constants
constexpr uint32_t kRpcTimeoutMs = 1000;
constexpr uint32_t kDefaultChannelId = 1;
// Fabric Bridge Client
rpc::pw_rpc::nanopb::FabricBridge::Client fabricBridgeClient(rpc::client::GetDefaultRpcClient(), kDefaultChannelId);
std::mutex responseMutex;
std::condition_variable responseCv;
bool responseReceived = false;
CHIP_ERROR responseError = CHIP_NO_ERROR;
// By passing the `call` parameter into WaitForResponse we are explicitly trying to insure the caller takes into consideration that
// the lifetime of the `call` object when calling WaitForResponse
template <typename CallType>
CHIP_ERROR WaitForResponse(CallType & call)
{
std::unique_lock<std::mutex> lock(responseMutex);
responseReceived = false;
responseError = CHIP_NO_ERROR;
if (responseCv.wait_for(lock, std::chrono::milliseconds(kRpcTimeoutMs), [] { return responseReceived; }))
{
return responseError;
}
else
{
fprintf(stderr, "RPC Response timed out!");
return CHIP_ERROR_TIMEOUT;
}
}
// Callback function to be called when the RPC response is received
void OnAddDeviceResponseCompleted(const pw_protobuf_Empty & response, pw::Status status)
{
std::lock_guard<std::mutex> lock(responseMutex);
responseReceived = true;
responseError = status.ok() ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
responseCv.notify_one();
if (status.ok())
{
ChipLogProgress(NotSpecified, "AddSynchronizedDevice RPC call succeeded!");
}
else
{
ChipLogProgress(NotSpecified, "AddSynchronizedDevice RPC call failed with status: %d\n", status.code());
}
}
// Callback function to be called when the RPC response is received
void OnRemoveDeviceResponseCompleted(const pw_protobuf_Empty & response, pw::Status status)
{
std::lock_guard<std::mutex> lock(responseMutex);
responseReceived = true;
responseError = status.ok() ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
responseCv.notify_one();
if (status.ok())
{
ChipLogProgress(NotSpecified, "RemoveSynchronizedDevice RPC call succeeded!");
}
else
{
ChipLogProgress(NotSpecified, "RemoveSynchronizedDevice RPC call failed with status: %d", status.code());
}
}
void RpcCompletedWithEmptyResponse(const pw_protobuf_Empty & response, pw::Status status)
{
std::lock_guard<std::mutex> lock(responseMutex);
responseReceived = true;
responseError = status.ok() ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL;
responseCv.notify_one();
if (status.ok())
{
ChipLogProgress(NotSpecified, "RPC call succeeded!");
}
else
{
ChipLogProgress(NotSpecified, "RPC call failed with status: %d", status.code());
}
}
} // namespace
void SetRpcRemoteServerPort(uint16_t port)
{
rpc::client::SetRpcServerPort(port);
}
CHIP_ERROR StartRpcClient()
{
return rpc::client::StartPacketProcessing();
}
CHIP_ERROR AddSynchronizedDevice(const chip_rpc_SynchronizedDevice & data)
{
ChipLogProgress(NotSpecified, "AddSynchronizedDevice");
// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
auto call = fabricBridgeClient.AddSynchronizedDevice(data, OnAddDeviceResponseCompleted);
if (!call.active())
{
// The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
return CHIP_ERROR_INTERNAL;
}
return WaitForResponse(call);
}
CHIP_ERROR RemoveSynchronizedDevice(ScopedNodeId scopedNodeId)
{
ChipLogProgress(NotSpecified, "RemoveSynchronizedDevice");
chip_rpc_SynchronizedDevice device = chip_rpc_SynchronizedDevice_init_default;
device.has_id = true;
device.id.node_id = scopedNodeId.GetNodeId();
device.id.fabric_index = scopedNodeId.GetFabricIndex();
// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
auto call = fabricBridgeClient.RemoveSynchronizedDevice(device, OnRemoveDeviceResponseCompleted);
if (!call.active())
{
// The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
return CHIP_ERROR_INTERNAL;
}
return WaitForResponse(call);
}
CHIP_ERROR ActiveChanged(ScopedNodeId scopedNodeId, uint32_t promisedActiveDurationMs)
{
ChipLogProgress(NotSpecified, "ActiveChanged");
chip_rpc_KeepActiveChanged parameters;
parameters.has_id = true;
parameters.id.node_id = scopedNodeId.GetNodeId();
parameters.id.fabric_index = scopedNodeId.GetFabricIndex();
parameters.promised_active_duration_ms = promisedActiveDurationMs;
// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
auto call = fabricBridgeClient.ActiveChanged(parameters, RpcCompletedWithEmptyResponse);
if (!call.active())
{
// The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
return CHIP_ERROR_INTERNAL;
}
return WaitForResponse(call);
}
CHIP_ERROR AdminCommissioningAttributeChanged(const chip_rpc_AdministratorCommissioningChanged & data)
{
ChipLogProgress(NotSpecified, "AdminCommissioningAttributeChanged");
// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
auto call = fabricBridgeClient.AdminCommissioningAttributeChanged(data, RpcCompletedWithEmptyResponse);
if (!call.active())
{
// The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
return CHIP_ERROR_INTERNAL;
}
return WaitForResponse(call);
}
CHIP_ERROR DeviceReachableChanged(const chip_rpc_ReachabilityChanged & data)
{
ChipLogProgress(NotSpecified, "DeviceReachableChanged");
// TODO(#35333): When there is some sort of device manager in fabric-admin that handles all the devices we
// are currently connected to (and not just device on the remote bridge), we should notify that manager
// so that it can properly handle any sort of reconnection logic. This can either be done here when
// `data.reachability == false`, or more control can be given wherever DeviceReachableChanged is currently
// called
// The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
// function and the call will complete.
auto call = fabricBridgeClient.DeviceReachableChanged(data, RpcCompletedWithEmptyResponse);
if (!call.active())
{
// The RPC call was not sent. This could occur due to, for example, an invalid channel ID. Handle if necessary.
return CHIP_ERROR_INTERNAL;
}
return WaitForResponse(call);
}