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
118 changes: 99 additions & 19 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,36 @@
* @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>

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
{
public:
// Register on all endpoints.
PowerSourceAttrAccess() : AttributeAccessInterface(Optional<EndpointId>::Missing(), PowerSource::Id) {}

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

PowerSourceAttrAccess gAttrAccess;

} // 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 +59,96 @@ 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);
}
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 == std::numeric_limits<size_t>::max())
{
idx = NextEmptyIndex();
}
if (idx == std::numeric_limits<size_t>::max())
{
return CHIP_ERROR_NO_MEMORY;
}
if (endpointList.size() == 0)
{
mPowerSourceClusterInfo[idx] = PowerSourceClusterInfo();
}
else
{
mPowerSourceClusterInfo[idx] = PowerSourceClusterInfo(powerSourceClusterEndpoint);
mPowerSourceClusterInfo[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 &mPowerSourceClusterInfo[idx].mEndpointList;
}
return nullptr;
}

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 (mPowerSourceClusterInfo[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 (mPowerSourceClusterInfo[i].mClusterEndpoint == kInvalidEndpointId)
{
return i;
}
}
return std::numeric_limits<size_t>::max();
}

} // namespace Clusters
} // namespace app
} // namespace chip
104 changes: 104 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,104 @@
/*
*
* 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 {

struct PowerSourceClusterInfo
{
PowerSourceClusterInfo() : mClusterEndpoint(kInvalidEndpointId) {}
explicit PowerSourceClusterInfo(EndpointId powerClusterEndpointId) : mClusterEndpoint(powerClusterEndpointId) {}
void Shutdown()
{
mEndpointList = Span<EndpointId>();
mBuf.Free();
}
CHIP_ERROR SetEndpointList(Span<EndpointId> endpointList)
{
Shutdown();
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;
};

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);
// returns nullptr if there's not endpoint list set for this power source cluster endpoint id.
const Span<EndpointId> * GetEndpointList(EndpointId powerSourceClusterEndpoint) const;
void Shutdown()
{
for (size_t i = 0; i < kNumSupportedEndpoints; ++i)
{
mPowerSourceClusterInfo[i].Shutdown();
}
}
size_t GetNumSupportedEndpointLists() const { return kNumSupportedEndpoints; };

private:
// There's probably a good way to properly set this from zap or have this grow dynamically as dynamic endpoints are
// finalized, but for now, let's just say 10 power clusters is probably ok.
// TODO: is there a way to get a static count of endpoints? CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT?
static constexpr size_t kNumSupportedEndpoints = 10;
PowerSourceClusterInfo mPowerSourceClusterInfo[kNumSupportedEndpoints] = {};
// 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