Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
759e9ba
Add MGCP draft
gennadiyvt Apr 16, 2026
a0bf131
Add MGCP Scenario 1 support
gennadiyvt May 18, 2026
2848d43
Move the generic implementation from `MuMpcMeasurement` and `GcpMgcpM…
gennadiyvt May 19, 2026
8aa3a3b
Move the generic implementation from `MuMpcMonitor` and `GcpMgcpMonit…
gennadiyvt May 20, 2026
3e0871f
Move the generic implementation from `MuMpc` and `GcpMgcp` to `EebusM…
gennadiyvt May 20, 2026
98efd84
Move the generic implementation from `MuMpc::AddFeatures()` and `GcpM…
gennadiyvt May 20, 2026
49efe67
Move the generic implementation from `MaMpcMeasurement` and `MaMgcpMe…
gennadiyvt May 20, 2026
643bd1d
Replace `MaMpc::GetMeasurementDataInternal()` and `MaMgcp::GetMeasure…
gennadiyvt May 20, 2026
f48f03d
Move the generic implementation from `ma_mpc_events.c` and `ma_mgcp_e…
gennadiyvt May 20, 2026
acad496
Extend the MU MPC unit test
gennadiyvt May 20, 2026
d272238
Extend the GCP MGPC unit test
gennadiyvt May 20, 2026
75b7310
In `EebusMonitorContainer::Update()` fix the final return value
gennadiyvt May 20, 2026
d3ff3de
Extend the MA MPC unit test
gennadiyvt May 20, 2026
4865367
Extend the MA MGPC unit test
gennadiyvt May 20, 2026
4bdbf2e
Add MGCP scenarios supported description to `/README.md`
gennadiyvt Jun 3, 2026
2c9d707
Update formatting
gennadiyvt Jun 8, 2026
e7defac
Explicitly mark the unused function parameters
gennadiyvt Jun 8, 2026
a4710e5
Use brace initialization in MGCP unit tests
gennadiyvt Jun 8, 2026
f051d1d
In `/src/cli/eebus_cli.c` update re-registration use case comments
gennadiyvt Jun 15, 2026
3b18148
In `/src/use_case/actor/common/eebus_measurement_base.c` update forma…
gennadiyvt Jun 15, 2026
c379694
In `/examples/heat_pump/hpsrv.c` update private functions list
gennadiyvt Jun 15, 2026
ab7cd23
In `/src/use_case/actor/common/eebus_measurement_base.c` update priva…
gennadiyvt Jun 15, 2026
1240000
In `src/use_case/actor/common/eebus_measurement_base.h` add missing f…
gennadiyvt Jun 15, 2026
d321b30
In `/examples/heat_pump/hpsrv.c` and `/src/use_case/actor/common/eebu…
gennadiyvt Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ The source code in this repository implements the EEBUS protocol, based on the [

### Supported Use Cases

| Use Case | Actors | Scenarios | Description |
|----------|----------|-----------|---------------------------------|
| LPC | EG, CS | 1, 2, 4 | Limitation of power consumption |
| LPP | EG, CS | 1, 2, 4 | Limitation of power production |
| MPC | MA, MU | 1 - 5 | Monitoring of power consumption |
| Use Case | Actors | Scenarios | Description |
|----------|----------|-----------|--------------------------------------|
| LPC | EG, CS | 1, 2, 4 | Limitation of power consumption |
| LPP | EG, CS | 1, 2, 4 | Limitation of power production |
| MPC | MA, MU | 1 - 5 | Monitoring of power consumption |
| MGCP | MA, GCP | 1 - 7 | Monitoring of grid connection point |

### Limitations

Expand Down
255 changes: 248 additions & 7 deletions examples/heat_pump/hpsrv.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "src/spine/entity/entity_local.h"
#include "src/use_case/actor/cs/lpc/cs_lpc.h"
#include "src/use_case/actor/cs/lpp/cs_lpp.h"
#include "src/use_case/actor/gcp/mgcp/gcp_mgcp.h"
#include "src/use_case/actor/mu/mpc/mu_mpc.h"

static const int8_t kScaleDefault = -2; // Default scale for measurements
Expand All @@ -57,6 +58,7 @@ struct Hpsrv {
CsLpListenerObject* cs_lpp_listener;
CsLpUseCaseObject* cs_lpp;
MuMpcUseCaseObject* mu_mpc;
GcpMgcpUseCaseObject* gcp_mgcp;
EebusCliObject* cli;
};

Expand Down Expand Up @@ -92,8 +94,31 @@ static const ServiceReaderInterface hpsrv_methods = {
};

static EebusError HpsrvConstruct(Hpsrv* self);
static EebusError HpsrvStart(Hpsrv* self, int32_t port, const char* role, TlsCertificateObject* tls_certificate);

EebusError HpsrvConstruct(Hpsrv* self) {
static EebusError AddLpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local);
static EebusError AddLpp(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local);
static EebusError AddMpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local);
static EebusError AddGcpMgcp(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local);

static EebusError AddHeatPumpApplianceEntity(
Hpsrv* self,
DeviceLocalObject* device_local,
const uint32_t* entity_ids,
size_t entity_id_size
);
static EebusError
AddInverterEntity(Hpsrv* self, DeviceLocalObject* device_local, const uint32_t* entity_ids, size_t entity_id_size);
static EebusError AddGridConnectionPointEntity(
Hpsrv* self,
DeviceLocalObject* device_local,
const uint32_t* entity_ids,
size_t entity_id_size
);

static EebusError SetMpcData(Hpsrv* self, const MpcData* mpc_data, size_t mpc_data_size);

static EebusError HpsrvConstruct(Hpsrv* self) {
// Override "virtual functions table"
SERVICE_READER_INTERFACE(self) = &hpsrv_methods;

Expand All @@ -104,6 +129,7 @@ EebusError HpsrvConstruct(Hpsrv* self) {
self->cs_lpp_listener = NULL;
self->cs_lpp = NULL;
self->mu_mpc = NULL;
self->gcp_mgcp = NULL;
self->cli = NULL;

self->cli = EebusCliCreate();
Expand All @@ -114,7 +140,7 @@ EebusError HpsrvConstruct(Hpsrv* self) {
return kEebusErrorOk;
}

EebusError AddLpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
static EebusError AddLpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
UNUSED(device_local);

self->cs_lpc_listener = CsLpcListenerCreate();
Expand All @@ -133,7 +159,7 @@ EebusError AddLpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObjec
return kEebusErrorOk;
}

EebusError AddLpp(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
static EebusError AddLpp(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
UNUSED(device_local);

self->cs_lpp_listener = CsLppListenerCreate();
Expand All @@ -152,7 +178,7 @@ EebusError AddLpp(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObjec
return kEebusErrorOk;
}

EebusError AddMpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
static EebusError AddMpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
UNUSED(device_local);

static const MuMpcMeasurementConfig measurement_default_cfg = {
Expand Down Expand Up @@ -211,7 +237,7 @@ EebusError AddMpc(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObjec
return kEebusErrorOk;
}

EebusError AddHeatPumpApplianceEntity(
static EebusError AddHeatPumpApplianceEntity(
Hpsrv* self,
DeviceLocalObject* device_local,
const uint32_t* entity_ids,
Expand Down Expand Up @@ -264,7 +290,99 @@ AddInverterEntity(Hpsrv* self, DeviceLocalObject* device_local, const uint32_t*
return kEebusErrorOk;
}

EebusError HpsrvStart(Hpsrv* hpsrv, int32_t port, const char* role, TlsCertificateObject* tls_certificate) {
static EebusError AddGcpMgcp(Hpsrv* self, DeviceLocalObject* device_local, EntityLocalObject* entity_local) {
UNUSED(device_local);

static const GcpMgcpMeasurementConfig measurement_default_cfg = {
.value_source = kMeasurementValueSourceTypeMeasuredValue,
};

static const GcpMgcpMonitorEnergyConfig energy_cfg = {
.energy_feed_in_cfg = &measurement_default_cfg,
.energy_consumed_cfg = &measurement_default_cfg,
};

static const GcpMgcpMonitorCurrentConfig current_cfg = {
.current_phase_a_cfg = &measurement_default_cfg,
.current_phase_b_cfg = &measurement_default_cfg,
.current_phase_c_cfg = &measurement_default_cfg,
};

static const GcpMgcpMonitorVoltageConfig voltage_cfg = {
.voltage_phase_a_cfg = &measurement_default_cfg,
.voltage_phase_b_cfg = &measurement_default_cfg,
.voltage_phase_c_cfg = &measurement_default_cfg,
.voltage_phase_ab_cfg = &measurement_default_cfg,
.voltage_phase_bc_cfg = &measurement_default_cfg,
.voltage_phase_ac_cfg = &measurement_default_cfg,
};

static const GcpMgcpMonitorFrequencyConfig frequency_cfg = {
.frequency_cfg = {.value_source = kMeasurementValueSourceTypeMeasuredValue},
};

static const GcpMgcpPvCurtailmentConfig pv_curtailment_cfg = {0};

static const GcpMgcpConfig cfg = {
.pv_curtailment_cfg = &pv_curtailment_cfg,
.power_cfg = {
.phases = kElectricalConnectionPhaseNameTypeAbc,
.power_total_cfg = {.value_source = kMeasurementValueSourceTypeMeasuredValue},
},
.energy_cfg = &energy_cfg,
.current_cfg = &current_cfg,
.voltage_cfg = &voltage_cfg,
.frequency_cfg = &frequency_cfg,
};

self->gcp_mgcp = GcpMgcpUseCaseCreate(entity_local, kHpsrvElectricalConnectionId, &cfg);
if (self->gcp_mgcp == NULL) {
return kEebusErrorInit;
}

const ScaledValue zero_power = {.value = 0, .scale = kScaleDefault};
EebusError err = GcpMgcpSetMeasurementDataCache(self->gcp_mgcp, kGcpPowerTotal, &zero_power, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}

err = GcpMgcpUpdate(self->gcp_mgcp);
if (err != kEebusErrorOk) {
return err;
}

EEBUS_CLI_SET_GCP_MGCP(self->cli, self->gcp_mgcp);
return kEebusErrorOk;
}

static EebusError AddGridConnectionPointEntity(
Hpsrv* self,
DeviceLocalObject* device_local,
const uint32_t* entity_ids,
size_t entity_id_size
) {
EntityLocalObject* const entity = EntityLocalCreate(
device_local,
kEntityTypeTypeGridConnectionPointOfPremises,
entity_ids,
entity_id_size,
kHeartbeatTimeoutSeconds
);

if (entity == NULL) {
return kEebusErrorMemoryAllocate;
}

if (AddGcpMgcp(self, device_local, entity) != kEebusErrorOk) {
EntityLocalDelete(entity);
return kEebusErrorInit;
}

DEVICE_LOCAL_ADD_ENTITY(device_local, entity);
return kEebusErrorOk;
}

static EebusError HpsrvStart(Hpsrv* hpsrv, int32_t port, const char* role, TlsCertificateObject* tls_certificate) {
if (tls_certificate == NULL) {
return kEebusErrorInputArgument;
}
Expand Down Expand Up @@ -297,6 +415,11 @@ EebusError HpsrvStart(Hpsrv* hpsrv, int32_t port, const char* role, TlsCertifica
return kEebusErrorOther;
}

uint32_t gcp_entity_ids[1] = {VectorGetSize(DEVICE_LOCAL_GET_ENTITIES(device_local))};
if (AddGridConnectionPointEntity(hpsrv, device_local, gcp_entity_ids, ARRAY_SIZE(gcp_entity_ids)) != kEebusErrorOk) {
return kEebusErrorOther;
}

EEBUS_SERVICE_START(hpsrv->service);

return kEebusErrorOk;
Expand Down Expand Up @@ -349,6 +472,9 @@ void Destruct(ServiceReaderObject* self) {
CsLpcListenerDelete(hpsrv->cs_lpc_listener);
hpsrv->cs_lpc_listener = NULL;

UseCaseDelete(USE_CASE_OBJECT(hpsrv->gcp_mgcp));
hpsrv->gcp_mgcp = NULL;

EebusServiceConfigDelete(hpsrv->cfg);
hpsrv->cfg = NULL;
}
Expand Down Expand Up @@ -402,7 +528,7 @@ void HpsrvUnregisterRemoteSki(HpsrvObject* self, const char* ski) {
EEBUS_SERVICE_UNREGISTER_REMOTE_SKI(HPSRV(self)->service, ski);
}

EebusError SetMpcData(Hpsrv* self, const MpcData* mpc_data, size_t mpc_data_size) {
static EebusError SetMpcData(Hpsrv* self, const MpcData* mpc_data, size_t mpc_data_size) {
EebusError err = kEebusErrorOk;

for (size_t i = 0; i < mpc_data_size; ++i) {
Expand Down Expand Up @@ -533,6 +659,121 @@ EebusError HpsrvSetAcFrequency(HpsrvObject* self, int32_t ac_frequency) {
return MuMpcUpdate(hpsrv->mu_mpc);
}

EebusError HpsrvSetGcpMgcpPowerTotal(HpsrvObject* self, int32_t power_total) {
Hpsrv* const hpsrv = HPSRV(self);

const ScaledValue value = {.value = power_total, .scale = kScaleDefault};
EebusError err = GcpMgcpSetMeasurementDataCache(hpsrv->gcp_mgcp, kGcpPowerTotal, &value, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}

return GcpMgcpUpdate(hpsrv->gcp_mgcp);
}

EebusError HpsrvSetGcpMgcpEnergyFeedIn(HpsrvObject* self, int32_t energy_feed_in) {
Hpsrv* const hpsrv = HPSRV(self);

const ScaledValue value = {.value = energy_feed_in, .scale = kScaleDefault};
EebusError err = GcpMgcpSetEnergyFeedInCache(hpsrv->gcp_mgcp, &value, NULL, NULL, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}

return GcpMgcpUpdate(hpsrv->gcp_mgcp);
}

EebusError HpsrvSetGcpMgcpEnergyConsumed(HpsrvObject* self, int32_t energy_consumed) {
Hpsrv* const hpsrv = HPSRV(self);

const ScaledValue value = {.value = energy_consumed, .scale = kScaleDefault};
EebusError err = GcpMgcpSetEnergyConsumedCache(hpsrv->gcp_mgcp, &value, NULL, NULL, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}

return GcpMgcpUpdate(hpsrv->gcp_mgcp);
}

EebusError HpsrvSetGcpMgcpCurrentPerPhase(
HpsrvObject* self,
int32_t current_phase_a,
int32_t current_phase_b,
int32_t current_phase_c
) {
Hpsrv* const hpsrv = HPSRV(self);

const GcpMeasurementNameId names[] = {kGcpCurrentPhaseA, kGcpCurrentPhaseB, kGcpCurrentPhaseC};
const int32_t values[] = {current_phase_a, current_phase_b, current_phase_c};

for (size_t i = 0; i < ARRAY_SIZE(names); ++i) {
const ScaledValue sv = {.value = values[i], .scale = kScaleDefault};
EebusError err = GcpMgcpSetMeasurementDataCache(hpsrv->gcp_mgcp, names[i], &sv, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}
}

return GcpMgcpUpdate(hpsrv->gcp_mgcp);
}

EebusError HpsrvSetGcpMgcpVoltagePerPhase(
HpsrvObject* self,
int32_t voltage_phase_a,
int32_t voltage_phase_b,
int32_t voltage_phase_c,
int32_t voltage_phase_ab,
int32_t voltage_phase_bc,
int32_t voltage_phase_ac
) {
Hpsrv* const hpsrv = HPSRV(self);

const GcpMeasurementNameId names[] = {
kGcpVoltagePhaseA,
kGcpVoltagePhaseB,
kGcpVoltagePhaseC,
kGcpVoltagePhaseAb,
kGcpVoltagePhaseBc,
kGcpVoltagePhaseAc,
};
const int32_t values[] = {
voltage_phase_a,
voltage_phase_b,
voltage_phase_c,
voltage_phase_ab,
voltage_phase_bc,
voltage_phase_ac,
};

for (size_t i = 0; i < ARRAY_SIZE(names); ++i) {
const ScaledValue sv = {.value = values[i], .scale = kScaleDefault};
EebusError err = GcpMgcpSetMeasurementDataCache(hpsrv->gcp_mgcp, names[i], &sv, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}
}

return GcpMgcpUpdate(hpsrv->gcp_mgcp);
}

EebusError HpsrvSetGcpMgcpFrequency(HpsrvObject* self, int32_t frequency) {
Hpsrv* const hpsrv = HPSRV(self);

const ScaledValue value = {.value = frequency, .scale = kScaleDefault};
EebusError err = GcpMgcpSetMeasurementDataCache(hpsrv->gcp_mgcp, kGcpFrequency, &value, NULL, NULL);
if (err != kEebusErrorOk) {
return err;
}

return GcpMgcpUpdate(hpsrv->gcp_mgcp);
}

EebusError HpsrvSetGcpMgcpPvCurtailmentLimitFactor(HpsrvObject* self, const ScaledValue* value) {
Hpsrv* const hpsrv = HPSRV(self);

return GcpMgcpSetPvCurtailmentLimitFactor(hpsrv->gcp_mgcp, value);
}

void HpsrvHandleCmd(HpsrvObject* self, char* cmd) {
Hpsrv* const hpsrv = HPSRV(self);
EEBUS_CLI_HANDLE_CMD(hpsrv->cli, cmd);
Expand Down
Loading
Loading