Skip to content

Commit

Permalink
Refactoring of Energy Management App to move DEM, PowerTopology, EEM/…
Browse files Browse the repository at this point in the history
…EPM out of EVSE and into bespoke files.
  • Loading branch information
jamesharrow committed Sep 17, 2024
1 parent d04a667 commit 80a9f1c
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 437 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,12 @@
#pragma once

#include <DeviceEnergyManagementDelegateImpl.h>
#include <DeviceEnergyManagementManager.h>

chip::app::Clusters::DeviceEnergyManagement::DeviceEnergyManagementDelegate * GetDEMDelegate();
// TODO investigate this why isn't in in DEM?
extern std::unique_ptr<chip::app::Clusters::DeviceEnergyManagementManager> gDEMInstance;
extern std::unique_ptr<chip::app::Clusters::DeviceEnergyManagement::DeviceEnergyManagementDelegate> gDEMDelegate;

CHIP_ERROR DeviceEnergyManagementInit();
CHIP_ERROR DeviceEnergyManagementShutdown();
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,5 @@

#pragma once

#include <lib/core/CHIPError.h>

void EvseApplicationInit();
void EvseApplicationShutdown();

CHIP_ERROR DeviceEnergyManagementInit();
CHIP_ERROR DeviceEnergyManagementShutdown();

CHIP_ERROR EnergyMeterInit();
CHIP_ERROR EnergyMeterShutdown();

CHIP_ERROR PowerTopologyInit();
CHIP_ERROR PowerTopologyShutdown();
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include <PowerTopologyDelegate.h>
#include <device-energy-management-modes.h>
#include <energy-evse-modes.h>
#include <EnergyReportingMain.h>
#include <DEMDelegate.h>

#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
Expand All @@ -36,7 +38,7 @@
#include <lib/support/logging/CHIPLogging.h>
#include <platform/Linux/NetworkCommissioningDriver.h>

#define ENERGY_EVSE_ENDPOINT 1
static constexpr int ENERGY_EVSE_ENDPOINT = 1;

using namespace chip;
using namespace chip::app;
Expand All @@ -51,94 +53,16 @@ using namespace chip::app::Clusters::PowerTopology;
static std::unique_ptr<EnergyEvseDelegate> gEvseDelegate;
static std::unique_ptr<EvseTargetsDelegate> gEvseTargetsDelegate;
static std::unique_ptr<EnergyEvseManager> gEvseInstance;
static std::unique_ptr<DeviceEnergyManagementDelegate> gDEMDelegate;
static std::unique_ptr<DeviceEnergyManagementManager> gDEMInstance;

static std::unique_ptr<EVSEManufacturer> gEvseManufacturer;
static std::unique_ptr<ElectricalPowerMeasurementDelegate> gEPMDelegate;
static std::unique_ptr<ElectricalPowerMeasurementInstance> gEPMInstance;
// Electrical Energy Measurement cluster uses ember to initialise
static std::unique_ptr<ElectricalEnergyMeasurementAttrAccess> gEEMAttrAccess;

static std::unique_ptr<PowerTopologyDelegate> gPTDelegate;
static std::unique_ptr<PowerTopologyInstance> gPTInstance;

EVSEManufacturer * EnergyEvse::GetEvseManufacturer()
{
return gEvseManufacturer.get();
}

DeviceEnergyManagement::DeviceEnergyManagementDelegate * GetDEMDelegate()
{
VerifyOrDieWithMsg(gDEMDelegate.get() != nullptr, AppServer, "DEM Delegate is null");

return gDEMDelegate.get();
}

/*
* @brief Creates a Delegate and Instance for DEM
*
* The Instance is a container around the Delegate, so
* create the Delegate first, then wrap it in the Instance
* Then call the Instance->Init() to register the attribute and command handlers
*/
CHIP_ERROR DeviceEnergyManagementInit()
{
if (gDEMDelegate || gDEMInstance)
{
ChipLogError(AppServer, "DEM Instance or Delegate already exist.");
return CHIP_ERROR_INCORRECT_STATE;
}

gDEMDelegate = std::make_unique<DeviceEnergyManagementDelegate>();
if (!gDEMDelegate)
{
ChipLogError(AppServer, "Failed to allocate memory for DeviceEnergyManagementDelegate");
return CHIP_ERROR_NO_MEMORY;
}

BitMask<DeviceEnergyManagement::Feature> featureMap = GetFeatureMapFromCmdLine();

/* Manufacturer may optionally not support all features, commands & attributes */
gDEMInstance = std::make_unique<DeviceEnergyManagementManager>(EndpointId(ENERGY_EVSE_ENDPOINT), *gDEMDelegate, featureMap);

if (!gDEMInstance)
{
ChipLogError(AppServer, "Failed to allocate memory for DeviceEnergyManagementManager");
gDEMDelegate.reset();
return CHIP_ERROR_NO_MEMORY;
}

gDEMDelegate->SetDeviceEnergyManagementInstance(*gDEMInstance);

CHIP_ERROR err = gDEMInstance->Init(); /* Register Attribute & Command handlers */
if (err != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "Init failed on gDEMInstance");
gDEMInstance.reset();
gDEMDelegate.reset();
return err;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceEnergyManagementShutdown()
{
/* Do this in the order Instance first, then delegate
* Ensure we call the Instance->Shutdown to free attribute & command handlers first
*/
if (gDEMInstance)
{
/* deregister attribute & command handlers */
gDEMInstance->Shutdown();
gDEMInstance.reset();
}
if (gDEMDelegate)
{
gDEMDelegate.reset();
}
return CHIP_NO_ERROR;
}

/*
* @brief Creates a Delegate and Instance for EVSE cluster
Expand Down Expand Up @@ -232,160 +156,6 @@ CHIP_ERROR EnergyEvseShutdown()
return CHIP_NO_ERROR;
}

/*
* @brief Creates a Delegate and Instance for PowerTopology clusters
*
* The Instance is a container around the Delegate, so
* create the Delegate first, then wrap it in the Instance
* Then call the Instance->Init() to register the attribute and command handlers
*/
CHIP_ERROR PowerTopologyInit()
{
CHIP_ERROR err;

if (gPTDelegate || gPTInstance)
{
ChipLogError(AppServer, "PowerTopology Instance or Delegate already exist.");
return CHIP_ERROR_INCORRECT_STATE;
}

gPTDelegate = std::make_unique<PowerTopologyDelegate>();
if (!gPTDelegate)
{
ChipLogError(AppServer, "Failed to allocate memory for PowerTopology Delegate");
return CHIP_ERROR_NO_MEMORY;
}

gPTInstance =
std::make_unique<PowerTopologyInstance>(EndpointId(ENERGY_EVSE_ENDPOINT), *gPTDelegate,
BitMask<PowerTopology::Feature, uint32_t>(PowerTopology::Feature::kNodeTopology),
BitMask<PowerTopology::OptionalAttributes, uint32_t>(0));

if (!gPTInstance)
{
ChipLogError(AppServer, "Failed to allocate memory for PowerTopology Instance");
gPTDelegate.reset();
return CHIP_ERROR_NO_MEMORY;
}

err = gPTInstance->Init(); /* Register Attribute & Command handlers */
if (err != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "Init failed on gPTInstance");
gPTInstance.reset();
gPTDelegate.reset();
return err;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR PowerTopologyShutdown()
{
/* Do this in the order Instance first, then delegate
* Ensure we call the Instance->Shutdown to free attribute & command handlers first
*/
if (gPTInstance)
{
/* deregister attribute & command handlers */
gPTInstance->Shutdown();
gPTInstance.reset();
}

if (gPTDelegate)
{
gPTDelegate.reset();
}

return CHIP_NO_ERROR;
}

/*
* @brief Creates a Delegate and Instance for Electrical Power/Energy Measurement clusters
*
* The Instance is a container around the Delegate, so
* create the Delegate first, then wrap it in the Instance
* Then call the Instance->Init() to register the attribute and command handlers
*/
CHIP_ERROR EnergyMeterInit()
{
CHIP_ERROR err;

if (gEPMDelegate || gEPMInstance)
{
ChipLogError(AppServer, "EPM Instance or Delegate already exist.");
return CHIP_ERROR_INCORRECT_STATE;
}

gEPMDelegate = std::make_unique<ElectricalPowerMeasurementDelegate>();
if (!gEPMDelegate)
{
ChipLogError(AppServer, "Failed to allocate memory for EPM Delegate");
return CHIP_ERROR_NO_MEMORY;
}

/* Manufacturer may optionally not support all features, commands & attributes */
/* Turning on all optional features and attributes for test certification purposes */
gEPMInstance = std::make_unique<ElectricalPowerMeasurementInstance>(
EndpointId(ENERGY_EVSE_ENDPOINT), *gEPMDelegate,
BitMask<ElectricalPowerMeasurement::Feature, uint32_t>(
ElectricalPowerMeasurement::Feature::kDirectCurrent, ElectricalPowerMeasurement::Feature::kAlternatingCurrent,
ElectricalPowerMeasurement::Feature::kPolyphasePower, ElectricalPowerMeasurement::Feature::kHarmonics,
ElectricalPowerMeasurement::Feature::kPowerQuality),
BitMask<ElectricalPowerMeasurement::OptionalAttributes, uint32_t>(
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeRanges,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeVoltage,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeActiveCurrent,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeReactiveCurrent,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeApparentCurrent,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeReactivePower,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeApparentPower,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeRMSVoltage,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeRMSCurrent,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeRMSPower,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeFrequency,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributePowerFactor,
ElectricalPowerMeasurement::OptionalAttributes::kOptionalAttributeNeutralCurrent));

if (!gEPMInstance)
{
ChipLogError(AppServer, "Failed to allocate memory for EPM Instance");
gEPMDelegate.reset();
return CHIP_ERROR_NO_MEMORY;
}

err = gEPMInstance->Init(); /* Register Attribute & Command handlers */
if (err != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "Init failed on gEPMInstance");
gEPMInstance.reset();
gEPMDelegate.reset();
return err;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR EnergyMeterShutdown()
{
/* Do this in the order Instance first, then delegate
* Ensure we call the Instance->Shutdown to free attribute & command handlers first
*/
if (gEPMInstance)
{
/* deregister attribute & command handlers */
gEPMInstance->Shutdown();
gEPMInstance.reset();
}

if (gEPMDelegate)
{
gEPMDelegate.reset();
}

return CHIP_NO_ERROR;
}

/*
* @brief Creates a EVSEManufacturer class to hold the EVSE & DEM clusters
*
Expand Down Expand Up @@ -460,7 +230,6 @@ void EvseApplicationInit()

if (PowerTopologyInit() != CHIP_NO_ERROR)
{
EVSEManufacturerShutdown();
DeviceEnergyManagementShutdown();
EnergyEvseShutdown();
EnergyMeterShutdown();
Expand All @@ -473,6 +242,7 @@ void EvseApplicationInit()
DeviceEnergyManagementShutdown();
EnergyEvseShutdown();
EnergyMeterShutdown();
PowerTopologyShutdown();
return;
}
}
Expand All @@ -491,50 +261,3 @@ void EvseApplicationShutdown()
Clusters::DeviceEnergyManagementMode::Shutdown();
Clusters::EnergyEvseMode::Shutdown();
}

void emberAfElectricalEnergyMeasurementClusterInitCallback(chip::EndpointId endpointId)
{
VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1.
VerifyOrDie(!gEEMAttrAccess);

gEEMAttrAccess = std::make_unique<ElectricalEnergyMeasurementAttrAccess>(
BitMask<ElectricalEnergyMeasurement::Feature, uint32_t>(
ElectricalEnergyMeasurement::Feature::kImportedEnergy, ElectricalEnergyMeasurement::Feature::kExportedEnergy,
ElectricalEnergyMeasurement::Feature::kCumulativeEnergy, ElectricalEnergyMeasurement::Feature::kPeriodicEnergy),
BitMask<ElectricalEnergyMeasurement::OptionalAttributes, uint32_t>(
ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset));

// Create an accuracy entry which is between +/-0.5 and +/- 5% across the range of all possible energy readings
ElectricalEnergyMeasurement::Structs::MeasurementAccuracyRangeStruct::Type energyAccuracyRanges[] = {
{ .rangeMin = 0,
.rangeMax = 1'000'000'000'000'000, // 1 million Mwh
.percentMax = MakeOptional(static_cast<chip::Percent100ths>(500)),
.percentMin = MakeOptional(static_cast<chip::Percent100ths>(50)) }
};

ElectricalEnergyMeasurement::Structs::MeasurementAccuracyStruct::Type accuracy = {
.measurementType = MeasurementTypeEnum::kElectricalEnergy,
.measured = true,
.minMeasuredValue = 0,
.maxMeasuredValue = 1'000'000'000'000'000, // 1 million Mwh
.accuracyRanges =
DataModel::List<const ElectricalEnergyMeasurement::Structs::MeasurementAccuracyRangeStruct::Type>(energyAccuracyRanges)
};

// Example of setting CumulativeEnergyReset structure - for now set these to 0
// but the manufacturer may want to store these in non volatile storage for timestamp (based on epoch_s)
ElectricalEnergyMeasurement::Structs::CumulativeEnergyResetStruct::Type resetStruct = {
.importedResetTimestamp = MakeOptional(MakeNullable(static_cast<uint32_t>(0))),
.exportedResetTimestamp = MakeOptional(MakeNullable(static_cast<uint32_t>(0))),
.importedResetSystime = MakeOptional(MakeNullable(static_cast<uint64_t>(0))),
.exportedResetSystime = MakeOptional(MakeNullable(static_cast<uint64_t>(0))),
};

if (gEEMAttrAccess)
{
gEEMAttrAccess->Init();

SetMeasurementAccuracy(endpointId, accuracy);
SetCumulativeReset(endpointId, MakeOptional(resetStruct));
}
}
Loading

0 comments on commit 80a9f1c

Please sign in to comment.