blob: d9848bcbc535151e361d1bd7e0b22be4d7225a6a [file] [log] [blame]
/*
*
* Copyright (c) 2023 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 <EVSEManufacturerImpl.h>
#include <EnergyEvseManager.h>
#include <app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h>
#include <app/clusters/electrical-energy-measurement-server/electrical-energy-measurement-server.h>
#include <app/clusters/energy-evse-server/EnergyEvseTestEventTriggerHandler.h>
#include <app/clusters/power-source-server/power-source-server.h>
#include <app/server/Server.h>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <protocols/interaction_model/StatusCode.h>
using namespace chip;
using namespace chip::app;
using namespace chip::app::DataModel;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::EnergyEvse;
using namespace chip::app::Clusters::ElectricalPowerMeasurement;
using namespace chip::app::Clusters::ElectricalEnergyMeasurement;
using namespace chip::app::Clusters::ElectricalEnergyMeasurement::Structs;
using namespace chip::app::Clusters::PowerSource;
using namespace chip::app::Clusters::PowerSource::Attributes;
using Protocols::InteractionModel::Status;
CHIP_ERROR EVSEManufacturer::Init()
{
/* Manufacturers should modify this to do any custom initialisation */
/* Register callbacks */
EnergyEvseDelegate * dg = GetEvseManufacturer()->GetEvseDelegate();
if (dg == nullptr)
{
ChipLogError(AppServer, "EVSE Delegate is not initialized");
return CHIP_ERROR_UNINITIALIZED;
}
dg->HwRegisterEvseCallbackHandler(ApplicationCallbackHandler, reinterpret_cast<intptr_t>(this));
ReturnErrorOnFailure(InitializePowerMeasurementCluster());
ReturnErrorOnFailure(InitializePowerSourceCluster());
/*
* This is an example implementation for manufacturers to consider
*
* For Manufacturer to specify the hardware capability in mA:
* dg->HwSetMaxHardwareCurrentLimit(32000); // 32A
*
* For Manufacturer to specify the CircuitCapacity in mA (e.g. from DIP switches)
* dg->HwSetCircuitCapacity(20000); // 20A
*
*/
/* Once the system is initialised then check to see if the state was restored
* (e.g. after a power outage), and if the Enable timer check needs to be started
*/
dg->ScheduleCheckOnEnabledTimeout();
return CHIP_NO_ERROR;
}
/*
* When the EV is plugged in, and asking for demand change the state
* and set the CableAssembly current limit
*
* EnergyEvseDelegate * dg = GetEvseManufacturer()->GetEvseDelegate();
* VerifyOrReturnError(dg != nullptr, CHIP_ERROR_UNINITIALIZED);
*
* dg->HwSetState(StateEnum::kPluggedInDemand);
* dg->HwSetCableAssemblyLimit(63000); // 63A = 63000mA
*
*
* If the vehicle ID can be retrieved (e.g. over Powerline)
* dg->HwSetVehicleID(CharSpan::fromCharString("TEST_VEHICLE_123456789"));
*
*
* If the EVSE has an RFID sensor, the RFID value read can cause an event to be sent
* (e.g. can be used to indicate if a user as tried to activate the charging)
*
* uint8_t uid[10] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE };
* dg->HwSetRFID(ByteSpan(uid));
*/
CHIP_ERROR EVSEManufacturer::Shutdown()
{
return CHIP_NO_ERROR;
}
/**
* @brief Allows a client application to initialise the Accuracy, Measurement types etc
*/
CHIP_ERROR EVSEManufacturer::InitializePowerMeasurementCluster()
{
EVSEManufacturer * mn = GetEvseManufacturer();
VerifyOrReturnError(mn != nullptr, CHIP_ERROR_UNINITIALIZED);
ElectricalPowerMeasurementDelegate * dg = mn->GetEPMDelegate();
VerifyOrReturnError(dg != nullptr, CHIP_ERROR_UNINITIALIZED);
ReturnErrorOnFailure(dg->SetPowerMode(PowerModeEnum::kAc));
return CHIP_NO_ERROR;
}
/**
* @brief Allows a client application to initialise the PowerSource cluster
*/
CHIP_ERROR EVSEManufacturer::InitializePowerSourceCluster()
{
Protocols::InteractionModel::Status status;
status = PowerSource::Attributes::Status::Set(EndpointId(0) /*RootNode*/, PowerSourceStatusEnum::kActive);
VerifyOrReturnError(status == Protocols::InteractionModel::Status::Success, CHIP_ERROR_INTERNAL);
status =
PowerSource::Attributes::FeatureMap::Set(EndpointId(0 /*RootNode*/), static_cast<uint32_t>(PowerSource::Feature::kWired));
VerifyOrReturnError(status == Protocols::InteractionModel::Status::Success, CHIP_ERROR_INTERNAL);
status = PowerSource::Attributes::WiredNominalVoltage::Set(EndpointId(0 /*RootNode*/), 230'000); // 230V in mv
VerifyOrReturnError(status == Protocols::InteractionModel::Status::Success, CHIP_ERROR_INTERNAL);
status = PowerSource::Attributes::WiredMaximumCurrent::Set(EndpointId(0 /*RootNode*/), 32'000); // 32A in mA
VerifyOrReturnError(status == Protocols::InteractionModel::Status::Success, CHIP_ERROR_INTERNAL);
status = PowerSource::Attributes::WiredCurrentType::Set(EndpointId(0 /*RootNode*/), PowerSource::WiredCurrentTypeEnum::kAc);
VerifyOrReturnError(status == Protocols::InteractionModel::Status::Success, CHIP_ERROR_INTERNAL);
status = PowerSource::Attributes::Description::Set(EndpointId(0 /*RootNode*/), CharSpan::fromCharString("Primary Mains Power"));
VerifyOrReturnError(status == Protocols::InteractionModel::Status::Success, CHIP_ERROR_INTERNAL);
chip::EndpointId endpointArray[] = { 1 /* EVSE Endpoint */ };
Span<EndpointId> endpointList = Span<EndpointId>(endpointArray);
// Note per API - we do not need to maintain the span after the SetEndpointList has been called
// since it takes a copy (see power-source-server.cpp)
PowerSourceServer::Instance().SetEndpointList(0 /* Root Node */, endpointList);
return CHIP_NO_ERROR;
}
/**
* @brief Allows a client application to send in power readings into the system
*
* @param[in] aEndpointId - Endpoint to send to EPM Cluster
* @param[in] aActivePower_mW - ActivePower measured in milli-watts
* @param[in] aVoltage_mV - Voltage measured in milli-volts
* @param[in] aActiveCurrent_mA - ActiveCurrent measured in milli-amps
*/
CHIP_ERROR EVSEManufacturer::SendPowerReading(EndpointId aEndpointId, int64_t aActivePower_mW, int64_t aVoltage_mV,
int64_t aActiveCurrent_mA)
{
EVSEManufacturer * mn = GetEvseManufacturer();
VerifyOrReturnError(mn != nullptr, CHIP_ERROR_UNINITIALIZED);
ElectricalPowerMeasurementDelegate * dg = mn->GetEPMDelegate();
VerifyOrReturnError(dg != nullptr, CHIP_ERROR_UNINITIALIZED);
dg->SetActivePower(MakeNullable(aActivePower_mW));
dg->SetVoltage(MakeNullable(aVoltage_mV));
dg->SetActiveCurrent(MakeNullable(aActiveCurrent_mA));
return CHIP_NO_ERROR;
}
/**
* @brief Allows a client application to send cumulative energy readings into the system
*
* This is a helper function to add timestamps to the readings
*
* @param[in] aCumulativeEnergyImported -total energy imported in milli-watthours
* @param[in] aCumulativeEnergyExported -total energy exported in milli-watthours
*/
CHIP_ERROR EVSEManufacturer::SendCumulativeEnergyReading(EndpointId aEndpointId, int64_t aCumulativeEnergyImported,
int64_t aCumulativeEnergyExported)
{
MeasurementData * data = MeasurementDataForEndpoint(aEndpointId);
VerifyOrReturnError(data != nullptr, CHIP_ERROR_UNINITIALIZED);
EnergyMeasurementStruct::Type energyImported;
EnergyMeasurementStruct::Type energyExported;
/** IMPORT */
// Copy last endTimestamp into new startTimestamp if it exists
energyImported.startTimestamp.ClearValue();
energyImported.startSystime.ClearValue();
if (data->cumulativeImported.HasValue())
{
energyImported.startTimestamp = data->cumulativeImported.Value().endTimestamp;
energyImported.startSystime = data->cumulativeImported.Value().endSystime;
}
energyImported.energy = aCumulativeEnergyImported;
/** EXPORT */
// Copy last endTimestamp into new startTimestamp if it exists
energyExported.startTimestamp.ClearValue();
energyExported.startSystime.ClearValue();
if (data->cumulativeExported.HasValue())
{
energyExported.startTimestamp = data->cumulativeExported.Value().endTimestamp;
energyExported.startSystime = data->cumulativeExported.Value().endSystime;
}
energyExported.energy = aCumulativeEnergyExported;
// Get current timestamp
uint32_t currentTimestamp;
CHIP_ERROR err = GetEpochTS(currentTimestamp);
if (err == CHIP_NO_ERROR)
{
// use EpochTS
energyImported.endTimestamp.SetValue(currentTimestamp);
energyExported.endTimestamp.SetValue(currentTimestamp);
}
else
{
ChipLogError(AppServer, "GetEpochTS returned error getting timestamp %" CHIP_ERROR_FORMAT, err.Format());
// use systemTime as a fallback
System::Clock::Milliseconds64 system_time_ms =
std::chrono::duration_cast<System::Clock::Milliseconds64>(chip::Server::GetInstance().TimeSinceInit());
uint64_t nowMS = static_cast<uint64_t>(system_time_ms.count());
energyImported.endSystime.SetValue(nowMS);
energyExported.endSystime.SetValue(nowMS);
}
// call the SDK to update attributes and generate an event
if (!NotifyCumulativeEnergyMeasured(aEndpointId, MakeOptional(energyImported), MakeOptional(energyExported)))
{
ChipLogError(AppServer, "Failed to notify Cumulative Energy reading.");
return CHIP_ERROR_INTERNAL;
}
return CHIP_NO_ERROR;
}
/**
* @brief Allows a client application to send periodic energy readings into the system
*
* This is a helper function to add timestamps to the readings
*
* @param[in] aPeriodicEnergyImported - energy imported in milli-watthours in last period
* @param[in] aPeriodicEnergyExported - energy exported in milli-watthours in last period
*/
CHIP_ERROR EVSEManufacturer::SendPeriodicEnergyReading(EndpointId aEndpointId, int64_t aPeriodicEnergyImported,
int64_t aPeriodicEnergyExported)
{
MeasurementData * data = MeasurementDataForEndpoint(aEndpointId);
VerifyOrReturnError(data != nullptr, CHIP_ERROR_UNINITIALIZED);
EnergyMeasurementStruct::Type energyImported;
EnergyMeasurementStruct::Type energyExported;
/** IMPORT */
// Copy last endTimestamp into new startTimestamp if it exists
energyImported.startTimestamp.ClearValue();
energyImported.startSystime.ClearValue();
if (data->periodicImported.HasValue())
{
energyImported.startTimestamp = data->periodicImported.Value().endTimestamp;
energyImported.startSystime = data->periodicImported.Value().endSystime;
}
energyImported.energy = aPeriodicEnergyImported;
/** EXPORT */
// Copy last endTimestamp into new startTimestamp if it exists
energyExported.startTimestamp.ClearValue();
energyExported.startSystime.ClearValue();
if (data->periodicExported.HasValue())
{
energyExported.startTimestamp = data->periodicExported.Value().endTimestamp;
energyExported.startSystime = data->periodicExported.Value().endSystime;
}
energyExported.energy = aPeriodicEnergyExported;
// Get current timestamp
uint32_t currentTimestamp;
CHIP_ERROR err = GetEpochTS(currentTimestamp);
if (err == CHIP_NO_ERROR)
{
// use EpochTS
energyImported.endTimestamp.SetValue(currentTimestamp);
energyExported.endTimestamp.SetValue(currentTimestamp);
}
else
{
ChipLogError(AppServer, "GetEpochTS returned error getting timestamp");
// use systemTime as a fallback
System::Clock::Milliseconds64 system_time_ms =
std::chrono::duration_cast<System::Clock::Milliseconds64>(chip::Server::GetInstance().TimeSinceInit());
uint64_t nowMS = static_cast<uint64_t>(system_time_ms.count());
energyImported.endSystime.SetValue(nowMS);
energyExported.endSystime.SetValue(nowMS);
}
// call the SDK to update attributes and generate an event
if (!NotifyPeriodicEnergyMeasured(aEndpointId, MakeOptional(energyImported), MakeOptional(energyExported)))
{
ChipLogError(AppServer, "Failed to notify Cumulative Energy reading.");
return CHIP_ERROR_INTERNAL;
}
return CHIP_NO_ERROR;
}
struct FakeReadingsData
{
bool bEnabled; /* If enabled then the timer callback will re-trigger */
EndpointId mEndpointId; /* Which endpoint the meter is on */
uint8_t mInterval_s; /* Interval in seconds to callback */
int64_t mPower_mW; /* Active Power on the load in mW (signed value) +ve = imported */
uint32_t mPowerRandomness_mW; /* The amount to randomize the Power on the load in mW */
int64_t mVoltage_mV; /* Voltage reading in mV (signed value) */
uint32_t mVoltageRandomness_mV; /* The amount to randomize the Voltage in mV */
int64_t mCurrent_mA; /* ActiveCurrent reading in mA (signed value) */
uint32_t mCurrentRandomness_mA; /* The amount to randomize the ActiveCurrent in mA */
/* These energy values can only be positive values.
* however the underlying energy type (power_mWh) is signed, so keeping with that convention */
int64_t mTotalEnergyImported = 0; /* Cumulative Energy Imported which is updated if mPower > 0 */
int64_t mTotalEnergyExported = 0; /* Cumulative Energy Imported which is updated if mPower < 0 */
int64_t mPeriodicEnergyImported = 0; /* Periodic Energy Imported which is updated if mPower > 0 */
int64_t mPeriodicEnergyExported = 0; /* Periodic Energy Imported which is updated if mPower < 0 */
};
static FakeReadingsData gFakeReadingsData;
/* This helper routine starts and handles a callback */
/**
* @brief Starts a fake load/generator to periodically callback the power and energy
* clusters.
* @param[in] aEndpointId - which endpoint is the meter to be updated on
* @param[in] aPower_mW - the mean power of the load
* Positive power indicates Imported energy (e.g. a load)
* Negative power indicated Exported energy (e.g. a generator)
* @param[in] aPowerRandomness_mW This is used to define the max randomness of the
* random power values around the mean power of the load
* @param[in] aVoltage_mV - the nominal voltage measurement
* @param[in] aVoltageRandomness_mV This is used to define the max randomness of the
* random voltage values
* @param[in] aCurrent_mA - the nominal current measurement
* @param[in] aCurrentRandomness_mA This is used to define the max randomness of the
* random current values
* @param[in] aInterval_s - the callback interval in seconds
* @param[in] bReset - boolean: true will reset the energy values to 0
*/
void EVSEManufacturer::StartFakeReadings(EndpointId aEndpointId, int64_t aPower_mW, uint32_t aPowerRandomness_mW,
int64_t aVoltage_mV, uint32_t aVoltageRandomness_mV, int64_t aCurrent_mA,
uint32_t aCurrentRandomness_mA, uint8_t aInterval_s, bool bReset)
{
gFakeReadingsData.bEnabled = true;
gFakeReadingsData.mEndpointId = aEndpointId;
gFakeReadingsData.mPower_mW = aPower_mW;
gFakeReadingsData.mPowerRandomness_mW = aPowerRandomness_mW;
gFakeReadingsData.mVoltage_mV = aVoltage_mV;
gFakeReadingsData.mVoltageRandomness_mV = aVoltageRandomness_mV;
gFakeReadingsData.mCurrent_mA = aCurrent_mA;
gFakeReadingsData.mCurrentRandomness_mA = aCurrentRandomness_mA;
gFakeReadingsData.mInterval_s = aInterval_s;
if (bReset)
{
gFakeReadingsData.mTotalEnergyImported = 0;
gFakeReadingsData.mTotalEnergyExported = 0;
}
// Call update function to kick off regular readings
FakeReadingsUpdate();
}
/**
* @brief Stops any active updates to the fake load data callbacks
*/
void EVSEManufacturer::StopFakeReadings()
{
gFakeReadingsData.bEnabled = false;
}
/**
* @brief Sends fake meter data into the cluster and restarts the timer
*/
void EVSEManufacturer::FakeReadingsUpdate()
{
/* Check to see if the fake Load is still running - don't send updates if the timer was already cancelled */
if (!gFakeReadingsData.bEnabled)
{
return;
}
// Update readings
// Avoid using floats - so we will do a basic rand() call which will generate a integer value between 0 and RAND_MAX
// first compute power as a mean + some random value in range +/- mPowerRandomness_mW
int64_t power =
(static_cast<int64_t>(rand()) % (2 * gFakeReadingsData.mPowerRandomness_mW)) - gFakeReadingsData.mPowerRandomness_mW;
power += gFakeReadingsData.mPower_mW; // add in the base power
int64_t voltage =
(static_cast<int64_t>(rand()) % (2 * gFakeReadingsData.mVoltageRandomness_mV)) - gFakeReadingsData.mVoltageRandomness_mV;
voltage += gFakeReadingsData.mVoltage_mV; // add in the base voltage
/* Note: whilst we could compute a current from the power and voltage,
* there will always be some random error from the sensor
* that measures it. To keep this simple and to avoid doing divides in integer
* format etc use the same approach here too.
* This is meant more as an example to show how to use the APIs, not
* to be a real representation of laws of physics.
*/
int64_t current =
(static_cast<int64_t>(rand()) % (2 * gFakeReadingsData.mCurrentRandomness_mA)) - gFakeReadingsData.mCurrentRandomness_mA;
current += gFakeReadingsData.mCurrent_mA; // add in the base current
SendPowerReading(gFakeReadingsData.mEndpointId, power, voltage, current);
// update the energy meter - we'll assume that the power has been constant during the previous interval
if (gFakeReadingsData.mPower_mW > 0)
{
// Positive power - means power is imported
gFakeReadingsData.mPeriodicEnergyImported = ((power * gFakeReadingsData.mInterval_s) / 3600);
gFakeReadingsData.mPeriodicEnergyExported = 0;
gFakeReadingsData.mTotalEnergyImported += gFakeReadingsData.mPeriodicEnergyImported;
}
else
{
// Negative power - means power is exported, but the exported energy is reported positive
gFakeReadingsData.mPeriodicEnergyImported = 0;
gFakeReadingsData.mPeriodicEnergyExported = ((-power * gFakeReadingsData.mInterval_s) / 3600);
gFakeReadingsData.mTotalEnergyExported += gFakeReadingsData.mPeriodicEnergyExported;
}
SendPeriodicEnergyReading(gFakeReadingsData.mEndpointId, gFakeReadingsData.mPeriodicEnergyImported,
gFakeReadingsData.mPeriodicEnergyExported);
SendCumulativeEnergyReading(gFakeReadingsData.mEndpointId, gFakeReadingsData.mTotalEnergyImported,
gFakeReadingsData.mTotalEnergyExported);
// start/restart the timer
DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds32(gFakeReadingsData.mInterval_s), FakeReadingsTimerExpiry, this);
}
/**
* @brief Timer expiry callback to handle fake load
*/
void EVSEManufacturer::FakeReadingsTimerExpiry(System::Layer * systemLayer, void * manufacturer)
{
EVSEManufacturer * mn = reinterpret_cast<EVSEManufacturer *>(manufacturer);
mn->FakeReadingsUpdate();
}
/**
* @brief Main Callback handler - to be implemented by Manufacturer
*
* @param EVSECbInfo describes the type of call back, and a union of structs
* which contain relevant info for the specific callback type
*
* @param arg - optional pointer to some context information (see register function)
*/
void EVSEManufacturer::ApplicationCallbackHandler(const EVSECbInfo * cb, intptr_t arg)
{
EVSEManufacturer * pClass = reinterpret_cast<EVSEManufacturer *>(arg);
switch (cb->type)
{
case EVSECallbackType::StateChanged:
ChipLogProgress(AppServer, "EVSE callback - state changed");
break;
case EVSECallbackType::ChargeCurrentChanged:
ChipLogProgress(AppServer, "EVSE callback - maxChargeCurrent changed to %ld",
static_cast<long>(cb->ChargingCurrent.maximumChargeCurrent));
break;
case EVSECallbackType::EnergyMeterReadingRequested:
ChipLogProgress(AppServer, "EVSE callback - EnergyMeterReadingRequested");
if (cb->EnergyMeterReadingRequest.meterType == ChargingDischargingType::kCharging)
{
*(cb->EnergyMeterReadingRequest.energyMeterValuePtr) = pClass->mLastChargingEnergyMeter;
}
else
{
*(cb->EnergyMeterReadingRequest.energyMeterValuePtr) = pClass->mLastDischargingEnergyMeter;
}
break;
default:
ChipLogError(AppServer, "Unhandled EVSE Callback type %d", static_cast<int>(cb->type));
}
}
struct EVSETestEventSaveData
{
int64_t mOldMaxHardwareCurrentLimit;
int64_t mOldCircuitCapacity;
int64_t mOldUserMaximumChargeCurrent;
int64_t mOldCableAssemblyLimit;
StateEnum mOldHwStateBasic; /* For storing hwState before Basic Func event */
StateEnum mOldHwStatePluggedIn; /* For storing hwState before PluggedIn event */
StateEnum mOldHwStatePluggedInDemand; /* For storing hwState before PluggedInDemand event */
};
static EVSETestEventSaveData sEVSETestEventSaveData;
EnergyEvseDelegate * GetEvseDelegate()
{
EVSEManufacturer * mn = GetEvseManufacturer();
VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
EnergyEvseDelegate * dg = mn->GetEvseDelegate();
VerifyOrDieWithMsg(dg != nullptr, AppServer, "EVSE Delegate is null");
return dg;
}
void SetTestEventTrigger_BasicFunctionality()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
sEVSETestEventSaveData.mOldMaxHardwareCurrentLimit = dg->HwGetMaxHardwareCurrentLimit();
sEVSETestEventSaveData.mOldCircuitCapacity = dg->GetCircuitCapacity();
sEVSETestEventSaveData.mOldUserMaximumChargeCurrent = dg->GetUserMaximumChargeCurrent();
sEVSETestEventSaveData.mOldHwStateBasic = dg->HwGetState();
dg->HwSetMaxHardwareCurrentLimit(32000);
dg->HwSetCircuitCapacity(32000);
dg->SetUserMaximumChargeCurrent(32000);
dg->HwSetState(StateEnum::kNotPluggedIn);
}
void SetTestEventTrigger_BasicFunctionalityClear()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwSetMaxHardwareCurrentLimit(sEVSETestEventSaveData.mOldMaxHardwareCurrentLimit);
dg->HwSetCircuitCapacity(sEVSETestEventSaveData.mOldCircuitCapacity);
dg->SetUserMaximumChargeCurrent(sEVSETestEventSaveData.mOldUserMaximumChargeCurrent);
dg->HwSetState(sEVSETestEventSaveData.mOldHwStateBasic);
}
void SetTestEventTrigger_EVPluggedIn()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
sEVSETestEventSaveData.mOldCableAssemblyLimit = dg->HwGetCableAssemblyLimit();
sEVSETestEventSaveData.mOldHwStatePluggedIn = dg->HwGetState();
dg->HwSetCableAssemblyLimit(63000);
dg->HwSetState(StateEnum::kPluggedInNoDemand);
}
void SetTestEventTrigger_EVPluggedInClear()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwSetCableAssemblyLimit(sEVSETestEventSaveData.mOldCableAssemblyLimit);
dg->HwSetState(sEVSETestEventSaveData.mOldHwStatePluggedIn);
}
void SetTestEventTrigger_EVChargeDemand()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
sEVSETestEventSaveData.mOldHwStatePluggedInDemand = dg->HwGetState();
dg->HwSetState(StateEnum::kPluggedInDemand);
}
void SetTestEventTrigger_EVChargeDemandClear()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwSetState(sEVSETestEventSaveData.mOldHwStatePluggedInDemand);
}
void SetTestEventTrigger_EVSEGroundFault()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwSetFault(FaultStateEnum::kGroundFault);
}
void SetTestEventTrigger_EVSEOverTemperatureFault()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwSetFault(FaultStateEnum::kOverTemperature);
}
void SetTestEventTrigger_EVSEFaultClear()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwSetFault(FaultStateEnum::kNoError);
}
void SetTestEventTrigger_EVSEDiagnosticsComplete()
{
EnergyEvseDelegate * dg = GetEvseDelegate();
dg->HwDiagnosticsComplete();
}
void SetTestEventTrigger_FakeReadingsLoadStart()
{
EVSEManufacturer * mn = GetEvseManufacturer();
VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
int64_t aPower_mW = 1'000'000; // Fake load 1000 W
uint32_t aPowerRandomness_mW = 20'000; // randomness 20W
int64_t aVoltage_mV = 230'000; // Fake Voltage 230V
uint32_t aVoltageRandomness_mV = 1'000; // randomness 1V
int64_t aCurrent_mA = 4'348; // Fake Current (at 1kW@230V = 4.3478 Amps)
uint32_t aCurrentRandomness_mA = 500; // randomness 500mA
uint8_t aInterval_s = 2; // 2s updates
bool bReset = true;
mn->StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aVoltage_mV, aVoltageRandomness_mV, aCurrent_mA,
aCurrentRandomness_mA, aInterval_s, bReset);
}
void SetTestEventTrigger_FakeReadingsGeneratorStart()
{
EVSEManufacturer * mn = GetEvseManufacturer();
VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
int64_t aPower_mW = -3'000'000; // Fake Generator -3000 W
uint32_t aPowerRandomness_mW = 20'000; // randomness 20W
int64_t aVoltage_mV = 230'000; // Fake Voltage 230V
uint32_t aVoltageRandomness_mV = 1'000; // randomness 1V
int64_t aCurrent_mA = -13'043; // Fake Current (at -3kW@230V = -13.0434 Amps)
uint32_t aCurrentRandomness_mA = 500; // randomness 500mA
uint8_t aInterval_s = 5; // 5s updates
bool bReset = true;
mn->StartFakeReadings(EndpointId(1), aPower_mW, aPowerRandomness_mW, aVoltage_mV, aVoltageRandomness_mV, aCurrent_mA,
aCurrentRandomness_mA, aInterval_s, bReset);
}
void SetTestEventTrigger_FakeReadingsStop()
{
EVSEManufacturer * mn = GetEvseManufacturer();
VerifyOrDieWithMsg(mn != nullptr, AppServer, "EVSEManufacturer is null");
mn->StopFakeReadings();
}
bool HandleEnergyEvseTestEventTrigger(uint64_t eventTrigger)
{
EnergyEvseTrigger trigger = static_cast<EnergyEvseTrigger>(eventTrigger);
switch (trigger)
{
case EnergyEvseTrigger::kBasicFunctionality:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => Basic Functionality install");
SetTestEventTrigger_BasicFunctionality();
break;
case EnergyEvseTrigger::kBasicFunctionalityClear:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => Basic Functionality clear");
SetTestEventTrigger_BasicFunctionalityClear();
break;
case EnergyEvseTrigger::kEVPluggedIn:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV plugged in");
SetTestEventTrigger_EVPluggedIn();
break;
case EnergyEvseTrigger::kEVPluggedInClear:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV unplugged");
SetTestEventTrigger_EVPluggedInClear();
break;
case EnergyEvseTrigger::kEVChargeDemand:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV Charge Demand");
SetTestEventTrigger_EVChargeDemand();
break;
case EnergyEvseTrigger::kEVChargeDemandClear:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EV Charge NoDemand");
SetTestEventTrigger_EVChargeDemandClear();
break;
case EnergyEvseTrigger::kEVSEGroundFault:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE has a GroundFault fault");
SetTestEventTrigger_EVSEGroundFault();
break;
case EnergyEvseTrigger::kEVSEOverTemperatureFault:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE has a OverTemperature fault");
SetTestEventTrigger_EVSEOverTemperatureFault();
break;
case EnergyEvseTrigger::kEVSEFaultClear:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE faults have cleared");
SetTestEventTrigger_EVSEFaultClear();
break;
case EnergyEvseTrigger::kEVSEDiagnosticsComplete:
ChipLogProgress(Support, "[EnergyEVSE-Test-Event] => EVSE Diagnostics Completed");
SetTestEventTrigger_EVSEDiagnosticsComplete();
break;
default:
return false;
}
return true;
}
bool HandleEnergyReportingTestEventTrigger(uint64_t eventTrigger)
{
EnergyReportingTrigger trigger = static_cast<EnergyReportingTrigger>(eventTrigger);
switch (trigger)
{
case EnergyReportingTrigger::kFakeReadingsStop:
ChipLogProgress(Support, "[EnergyReporting-Test-Event] => Stop Fake load");
SetTestEventTrigger_FakeReadingsStop();
break;
case EnergyReportingTrigger::kFakeReadingsLoadStart_1kW_2s:
ChipLogProgress(Support, "[EnergyReporting-Test-Event] => Start Fake load 1kW @2s Import");
SetTestEventTrigger_FakeReadingsLoadStart();
break;
case EnergyReportingTrigger::kFakeReadingsGenStart_3kW_5s:
ChipLogProgress(Support, "[EnergyReporting-Test-Event] => Start Fake generator 3kW @5s Export");
SetTestEventTrigger_FakeReadingsGeneratorStart();
break;
default:
return false;
}
return true;
}