Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Power source: Implementation of dynamic endpoint list setter #28110

Merged
175 changes: 158 additions & 17 deletions src/app/clusters/power-source-server/power-source-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,82 @@
* @brief Implementation for the Power Source Server Cluster
***************************************************************************/

#include "power-source-server.h"

#include <app-common/zap-generated/cluster-objects.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/AttributeAccessInterface.h>
#include <app/util/attribute-storage.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <zap-generated/gen_config.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::PowerSource::Attributes;
using namespace app;
using namespace app::Clusters;
using namespace app::Clusters::PowerSource::Attributes;

namespace {

class PowerSourceAttrAccess : public AttributeAccessInterface
struct PowerSourceClusterInfo
{
public:
// Register on all endpoints.
PowerSourceAttrAccess() : AttributeAccessInterface(Optional<EndpointId>::Missing(), PowerSource::Id) {}

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
PowerSourceClusterInfo() : mClusterEndpoint(kInvalidEndpointId) {}
explicit PowerSourceClusterInfo(EndpointId powerClusterEndpointId) : mClusterEndpoint(powerClusterEndpointId) {}
void Clear()
{
mBuf.Free();
cecille marked this conversation as resolved.
Show resolved Hide resolved
mEndpointList = Span<EndpointId>();
}
CHIP_ERROR SetEndpointList(Span<EndpointId> endpointList)
{
Clear();
if (endpointList.size() == 0)
{
mEndpointList = Span<EndpointId>();
return CHIP_NO_ERROR;
}
mBuf.Calloc(endpointList.size());
if (mBuf.Get() == nullptr)
{
return CHIP_ERROR_NO_MEMORY;
}
memcpy(mBuf.Get(), endpointList.data(), endpointList.size() * sizeof(EndpointId));
mEndpointList = Span<EndpointId>(mBuf.Get(), endpointList.size());
return CHIP_NO_ERROR;
}
EndpointId mClusterEndpoint = kInvalidEndpointId;
Platform::ScopedMemoryBuffer<EndpointId> mBuf;
Span<EndpointId> mEndpointList;
};

PowerSourceServer gPowerSourceServer;

PowerSourceAttrAccess gAttrAccess;

#ifdef ZCL_USING_POWER_SOURCE_CLUSTER_SERVER
#define POWER_SERVER_NUM_SUPPORTED_ENDPOINTS \
(EMBER_AF_POWER_SOURCE_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT)
#else
#define POWER_SERVER_NUM_SUPPORTED_ENDPOINTS CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT
#endif
static constexpr size_t kNumSupportedEndpoints = POWER_SERVER_NUM_SUPPORTED_ENDPOINTS;

#if POWER_SERVER_NUM_SUPPORTED_ENDPOINTS > 0
PowerSourceClusterInfo sPowerSourceClusterInfo[kNumSupportedEndpoints] = {};
#else
PowerSourceClusterInfo * sPowerSourceClusterInfo = nullptr;
#endif

} // anonymous namespace

void MatterPowerSourcePluginServerInitCallback()
{
registerAttributeAccessOverride(&gAttrAccess);
}

namespace chip {
namespace app {
namespace Clusters {

CHIP_ERROR PowerSourceAttrAccess::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Expand All @@ -55,20 +105,111 @@ CHIP_ERROR PowerSourceAttrAccess::Read(const ConcreteReadAttributePath & aPath,
// TODO: Needs implementation.
err = aEncoder.EncodeEmptyList();
break;
case EndpointList::Id:
// TODO: Needs implementation and a way to allow dynamic endpoints to register endpoints
err = aEncoder.EncodeEmptyList();
case EndpointList::Id: {
PowerSourceServer & server = PowerSourceServer::Instance();
const Span<EndpointId> * span = server.GetEndpointList(aPath.mEndpointId);
if (span == nullptr)
{
err = aEncoder.EncodeEmptyList();
}
else
{
DataModel::List<EndpointId> list(*span);
err = aEncoder.Encode(list);
cecille marked this conversation as resolved.
Show resolved Hide resolved
}
break;
}
default:
break;
}

return err;
}

} // anonymous namespace
PowerSourceAttrAccess & TestOnlyGetPowerSourceAttrAccess()
{
return gAttrAccess;
}

void MatterPowerSourcePluginServerInitCallback()
PowerSourceServer & PowerSourceServer::Instance()
{
registerAttributeAccessOverride(&gAttrAccess);
return gPowerSourceServer;
}

// Caller does not need to retain the span past the call point as these are copied into an internal storage
CHIP_ERROR PowerSourceServer::SetEndpointList(EndpointId powerSourceClusterEndpoint, Span<EndpointId> endpointList)
{
// TODO: should check here that the power source cluster exists on the endpoint, but for now let's take the caller's word for it

size_t idx = PowerSourceClusterEndpointIndex(powerSourceClusterEndpoint);
if (idx >= kNumSupportedEndpoints)
{
idx = NextEmptyIndex();
}
if (idx >= kNumSupportedEndpoints)
{
return CHIP_ERROR_NO_MEMORY;
cecille marked this conversation as resolved.
Show resolved Hide resolved
}

sPowerSourceClusterInfo[idx].Clear();
if (endpointList.size() == 0)
{
sPowerSourceClusterInfo[idx] = PowerSourceClusterInfo();
}
else
{
sPowerSourceClusterInfo[idx] = PowerSourceClusterInfo(powerSourceClusterEndpoint);
sPowerSourceClusterInfo[idx].SetEndpointList(endpointList);
}
return CHIP_NO_ERROR;
}
const Span<EndpointId> * PowerSourceServer::GetEndpointList(EndpointId powerSourceClusterEndpoint) const
{
size_t idx = PowerSourceClusterEndpointIndex(powerSourceClusterEndpoint);
if (idx != std::numeric_limits<size_t>::max())
{
return &sPowerSourceClusterInfo[idx].mEndpointList;
}
return nullptr;
}

void PowerSourceServer::Shutdown()
{
for (size_t i = 0; i < kNumSupportedEndpoints; ++i)
{
sPowerSourceClusterInfo[i].Clear();
}
}

size_t PowerSourceServer::GetNumSupportedEndpointLists() const
{
return kNumSupportedEndpoints;
}

size_t PowerSourceServer::PowerSourceClusterEndpointIndex(EndpointId endpointId) const
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably OK, but we have existing utilities (emberAfGetClusterServerEndpointIndex) for things like "map endpoint id to a list index, taking into account the fixed endpoints that we know have this cluster and the fact that all dynamic endpoints might have it"... Is there a reason we're not just using that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The endpoint count is different.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cecille Why is it different, exactly?

{
for (size_t i = 0; i < kNumSupportedEndpoints; ++i)
{
if (sPowerSourceClusterInfo[i].mClusterEndpoint == endpointId)
{
return i;
}
}
return std::numeric_limits<size_t>::max();
}

size_t PowerSourceServer::NextEmptyIndex() const
{
for (size_t i = 0; i < kNumSupportedEndpoints; ++i)
{
if (sPowerSourceClusterInfo[i].mClusterEndpoint == kInvalidEndpointId)
{
return i;
}
}
return std::numeric_limits<size_t>::max();
}

} // namespace Clusters
} // namespace app
} // namespace chip
66 changes: 66 additions & 0 deletions src/app/clusters/power-source-server/power-source-server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
*
* Copyright (c) 2023 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.
*/

#pragma once

#include <app-common/zap-generated/ids/Clusters.h>
#include <app/AttributeAccessInterface.h>
#include <app/util/af-types.h>
#include <app/util/basic-types.h>
#include <lib/support/Span.h>
#include <platform/CHIPDeviceConfig.h>

namespace chip {
namespace app {
namespace Clusters {

class PowerSourceServer
{
public:
static PowerSourceServer & Instance();

// Caller does not need to retain the span past the call point as these are copied into an internal storage
CHIP_ERROR SetEndpointList(EndpointId powerSourceClusterEndpoint, Span<EndpointId> endpointList);
cecille marked this conversation as resolved.
Show resolved Hide resolved
CHIP_ERROR ClearEndpointList(EndpointId powerSourceClusterEndpoint)
{
return SetEndpointList(powerSourceClusterEndpoint, Span<EndpointId>());
}
// returns nullptr if there's not endpoint list set for this power source cluster endpoint id.
const Span<EndpointId> * GetEndpointList(EndpointId powerSourceClusterEndpoint) const;
cecille marked this conversation as resolved.
Show resolved Hide resolved
void Shutdown();
size_t GetNumSupportedEndpointLists() const;

private:
// Both return std::numeric_limits<size_t>::max() for not found
size_t PowerSourceClusterEndpointIndex(EndpointId endpointId) const;
size_t NextEmptyIndex() const;
};

class PowerSourceAttrAccess : public AttributeAccessInterface
{
public:
// Register on all endpoints.
PowerSourceAttrAccess() : AttributeAccessInterface(Optional<EndpointId>::Missing(), PowerSource::Id) {}

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
};

PowerSourceAttrAccess & TestOnlyGetPowerSourceAttrAccess();

} // namespace Clusters
} // namespace app
} // namespace chip
14 changes: 14 additions & 0 deletions src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ source_set("time-sync-data-provider-test-srcs") {
]
}

source_set("power-cluster-test-srcs") {
sources = [
"${chip_root}/src/app/clusters/power-source-server/power-source-server.cpp",
]

public_deps = [
"${chip_root}/src/app/common:cluster-objects",
"${chip_root}/src/app/util/mock:mock_ember",
"${chip_root}/src/lib/core",
]
}

source_set("scenes-table-test-srcs") {
sources = [
"${chip_root}/src/app/clusters/scenes-server/ExtensionFieldSets.h",
Expand Down Expand Up @@ -131,6 +143,7 @@ chip_test_suite("tests") {
"TestNumericAttributeTraits.cpp",
"TestOperationalStateDelegate.cpp",
"TestPendingNotificationMap.cpp",
"TestPowerSourceCluster.cpp",
"TestReadInteraction.cpp",
"TestReportingEngine.cpp",
"TestSceneTable.cpp",
Expand Down Expand Up @@ -181,6 +194,7 @@ chip_test_suite("tests") {
":binding-test-srcs",
":operational-state-test-srcs",
":ota-requestor-test-srcs",
":power-cluster-test-srcs",
":scenes-table-test-srcs",
":time-sync-data-provider-test-srcs",
"${chip_root}/src/app",
Expand Down
Loading
Loading